📜 ⬆️ ⬇️

Come in! Login without login and password

Who is guilty?


One of the frequently arising tasks in the development of web projects is to let the user onto the site without entering a login and password, while authorizing him.

Here are some examples of such situations:

In each of these cases, we need to create a key for the user and add it to the URL sent in the letter.

As a rule, this key should be:

I have seen many solutions (and I used to resort to them myself) based on adding a service field or a whole table to the database, in which generated keys were placed and some additional information that allows you to authorize the user when he comes to the site with this key. Usually this key is the result of some hash function. For example: sha1 ($ userId. “Secret_key”. Time ());
')
Very often, this decision comes first. In fact, his place is at the very end.

I offer your solution, which allows you to do "little blood" - do not require working with the database.

What to do?


In addition to the above properties of the key, other restrictions are often imposed on it. From them, in fact, have to dance.

In the simplest cases, you can do with constructions of the form: sha1 ($ userId. "Secret_key"); or sha1 ($ userId. "secret_key". "confirm_code");

If a user comes with such a key to a site using a URL like example.com/users/%user_id%?t=%key% , we can easily check it.

Disadvantages of this approach:

The disadvantages are significant and therefore, in most cases, this solution will not work.
But this is not a reason to store data in the database. They can be stored in the key. Store safely - in encrypted form.
In php, encryption functions are implemented in the Mcrypt library, which can be easily installed from pecl or your OS repositories.
You yourself can implement your favorite algorithm. This is not important - the main thing is that we have functions allowing us to encrypt and decrypt arbitrary text.

The idea is simple. In the line we place the necessary data, encrypt it with our secret key, bring it to a URL-safe form and insert it as a key into the link that the user will use to get to us.

It must be remembered that the more data we want to put in the key, the longer the key itself will be, because this is no longer a fixed-length hash.

For my solution, I allocated such data for storage in the key:

Looking ahead, I'll show an example of the resulting key: 67147328f43d69f7784770a2d9c84b181a8c .
What you will keep in the key depends on your task. Here I showed that the key of an acceptable length can easily fit 18 bytes of information.

We turn to the implementation. I implemented this solution in the same class with two static methods.

Copy Source | Copy HTML<br/> <?php <br/> <br/> class AuthToken {<br/> private static $key = " " ;<br/> private static $iv = " , " ;<br/> <br/> private static function int2char( $int ) {<br/> $char = "" ;<br/> $hex = sprintf( "%08x" , $int );<br/> for ( $i = 0 ; $i < 4 ; $i ++) {<br/> $char .= chr(hexdec(substr( $hex , $i * 2 , 2 )));<br/> }<br/> return $char ;<br/> }<br/> <br/> private static function char2int( $char ) {<br/> $int = 0 ;<br/> $hex = "" ;<br/> for ( $i = 0 ; $i < 4 ; $i ++) {<br/> $hex .= sprintf( "%02x" , ord( $char { $i }));<br/> }<br/> $int = hexdec( $hex );<br/> return $int ;<br/> }<br/> <br/> public static function create( $id , $expire = 0 , $mode = 0 ) {<br/> $id = intval( $id );<br/> $expire = intval( $expire );<br/> $mode = intval( $mode );<br/> if ( $id < 0 || $expire < 0 || $mode < 0 ) {<br/> return null ;<br/> }<br/> <br/> $info = array ();<br/> $info [ "id" ] = $id ;<br/> $info [ "time" ] = time();<br/> $info [ "expire" ] = $expire ;<br/> $info [ "mode" ] = $mode ;<br/> $info [ "rnd" ] = ceil(mt_rand( 0 , 255 ));<br/> $info [ "sum" ] = $info [ "time" ] - $info [ "expire" ] - $info [ "mode" ] - $info [ "rnd" ] - $info [ "id" ];<br/> $info = self::int2char( $info [ "id" ]) . self::int2char( $info [ "time" ]) . self::int2char( $info [ "expire" ]) . chr( $info [ "mode" ]) . chr( $info [ "rnd" ]) . self::int2char( $info [ "sum" ]);<br/> <br/> $token = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5(self:: $key ), $info , MCRYPT_MODE_OFB, md5(self:: $iv ));<br/> $tokenHex = "" ;<br/> $tokenLength = strlen( $token );<br/> for ( $i = 0 ; $i < $tokenLength ; $i ++) {<br/> $tokenHex .= sprintf( "%02x" , ord( $token { $i }));<br/> }<br/> return $tokenHex ;<br/> }<br/> <br/> public static function check( $tokenHex , $mode = null ) {<br/> $token = "" ;<br/> $tokenHexLength = strlen( $tokenHex ) / 2 ;<br/> for ( $i = 0 ; $i < $tokenHexLength ; $i ++) {<br/> $token .= chr(hexdec(substr( $tokenHex , $i * 2 , 2 )));<br/> }<br/> $info = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5(self:: $key ), $token , MCRYPT_MODE_OFB, md5(self:: $iv ));<br/> if (strlen( $info ) == 18 ) {<br/> $info = array ( "id" => self::char2int(substr( $info , 0 , 4 )), "time" => self::char2int(substr( $info , 4 , 4 )), "expire" => self::char2int(substr( $info , 8 , 4 )), "mode" => ord( $info { 12 }), "rnd" => ord( $info { 13 }), "sum" => self::char2int(substr( $info , 14 , 4 )));<br/> if ( $info [ "sum" ] == $info [ "time" ] - $info [ "expire" ] - $info [ "mode" ] - $info [ "rnd" ] - $info [ "id" ]) {<br/> if ( $info [ "expire" ] > 0 ) {<br/> if ( $info [ "expire" ] + $info [ "time" ] < time()) {<br/> return false ;<br/> }<br/> }<br/> if ( $info [ "mode" ] > 0 ) {<br/> if ( $mode !== null ) {<br/> if ( $info [ "mode" ] != $mode ) {<br/> return false ;<br/> }<br/> }<br/> }<br/> return $info [ "id" ];<br/> } else {<br/> return false ;<br/> }<br/> } else {<br/> return false ;<br/> }<br/> }<br/>}<br/> <br/> ?> <br/> <br/>
Copy Source | Copy HTML<br/> <?php <br/> <br/> class AuthToken {<br/> private static $key = " " ;<br/> private static $iv = " , " ;<br/> <br/> private static function int2char( $int ) {<br/> $char = "" ;<br/> $hex = sprintf( "%08x" , $int );<br/> for ( $i = 0 ; $i < 4 ; $i ++) {<br/> $char .= chr(hexdec(substr( $hex , $i * 2 , 2 )));<br/> }<br/> return $char ;<br/> }<br/> <br/> private static function char2int( $char ) {<br/> $int = 0 ;<br/> $hex = "" ;<br/> for ( $i = 0 ; $i < 4 ; $i ++) {<br/> $hex .= sprintf( "%02x" , ord( $char { $i }));<br/> }<br/> $int = hexdec( $hex );<br/> return $int ;<br/> }<br/> <br/> public static function create( $id , $expire = 0 , $mode = 0 ) {<br/> $id = intval( $id );<br/> $expire = intval( $expire );<br/> $mode = intval( $mode );<br/> if ( $id < 0 || $expire < 0 || $mode < 0 ) {<br/> return null ;<br/> }<br/> <br/> $info = array ();<br/> $info [ "id" ] = $id ;<br/> $info [ "time" ] = time();<br/> $info [ "expire" ] = $expire ;<br/> $info [ "mode" ] = $mode ;<br/> $info [ "rnd" ] = ceil(mt_rand( 0 , 255 ));<br/> $info [ "sum" ] = $info [ "time" ] - $info [ "expire" ] - $info [ "mode" ] - $info [ "rnd" ] - $info [ "id" ];<br/> $info = self::int2char( $info [ "id" ]) . self::int2char( $info [ "time" ]) . self::int2char( $info [ "expire" ]) . chr( $info [ "mode" ]) . chr( $info [ "rnd" ]) . self::int2char( $info [ "sum" ]);<br/> <br/> $token = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5(self:: $key ), $info , MCRYPT_MODE_OFB, md5(self:: $iv ));<br/> $tokenHex = "" ;<br/> $tokenLength = strlen( $token );<br/> for ( $i = 0 ; $i < $tokenLength ; $i ++) {<br/> $tokenHex .= sprintf( "%02x" , ord( $token { $i }));<br/> }<br/> return $tokenHex ;<br/> }<br/> <br/> public static function check( $tokenHex , $mode = null ) {<br/> $token = "" ;<br/> $tokenHexLength = strlen( $tokenHex ) / 2 ;<br/> for ( $i = 0 ; $i < $tokenHexLength ; $i ++) {<br/> $token .= chr(hexdec(substr( $tokenHex , $i * 2 , 2 )));<br/> }<br/> $info = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5(self:: $key ), $token , MCRYPT_MODE_OFB, md5(self:: $iv ));<br/> if (strlen( $info ) == 18 ) {<br/> $info = array ( "id" => self::char2int(substr( $info , 0 , 4 )), "time" => self::char2int(substr( $info , 4 , 4 )), "expire" => self::char2int(substr( $info , 8 , 4 )), "mode" => ord( $info { 12 }), "rnd" => ord( $info { 13 }), "sum" => self::char2int(substr( $info , 14 , 4 )));<br/> if ( $info [ "sum" ] == $info [ "time" ] - $info [ "expire" ] - $info [ "mode" ] - $info [ "rnd" ] - $info [ "id" ]) {<br/> if ( $info [ "expire" ] > 0 ) {<br/> if ( $info [ "expire" ] + $info [ "time" ] < time()) {<br/> return false ;<br/> }<br/> }<br/> if ( $info [ "mode" ] > 0 ) {<br/> if ( $mode !== null ) {<br/> if ( $info [ "mode" ] != $mode ) {<br/> return false ;<br/> }<br/> }<br/> }<br/> return $info [ "id" ];<br/> } else {<br/> return false ;<br/> }<br/> } else {<br/> return false ;<br/> }<br/> }<br/>}<br/> <br/> ?> <br/> <br/>
Copy Source | Copy HTML<br/> <?php <br/> <br/> class AuthToken {<br/> private static $key = " " ;<br/> private static $iv = " , " ;<br/> <br/> private static function int2char( $int ) {<br/> $char = "" ;<br/> $hex = sprintf( "%08x" , $int );<br/> for ( $i = 0 ; $i < 4 ; $i ++) {<br/> $char .= chr(hexdec(substr( $hex , $i * 2 , 2 )));<br/> }<br/> return $char ;<br/> }<br/> <br/> private static function char2int( $char ) {<br/> $int = 0 ;<br/> $hex = "" ;<br/> for ( $i = 0 ; $i < 4 ; $i ++) {<br/> $hex .= sprintf( "%02x" , ord( $char { $i }));<br/> }<br/> $int = hexdec( $hex );<br/> return $int ;<br/> }<br/> <br/> public static function create( $id , $expire = 0 , $mode = 0 ) {<br/> $id = intval( $id );<br/> $expire = intval( $expire );<br/> $mode = intval( $mode );<br/> if ( $id < 0 || $expire < 0 || $mode < 0 ) {<br/> return null ;<br/> }<br/> <br/> $info = array ();<br/> $info [ "id" ] = $id ;<br/> $info [ "time" ] = time();<br/> $info [ "expire" ] = $expire ;<br/> $info [ "mode" ] = $mode ;<br/> $info [ "rnd" ] = ceil(mt_rand( 0 , 255 ));<br/> $info [ "sum" ] = $info [ "time" ] - $info [ "expire" ] - $info [ "mode" ] - $info [ "rnd" ] - $info [ "id" ];<br/> $info = self::int2char( $info [ "id" ]) . self::int2char( $info [ "time" ]) . self::int2char( $info [ "expire" ]) . chr( $info [ "mode" ]) . chr( $info [ "rnd" ]) . self::int2char( $info [ "sum" ]);<br/> <br/> $token = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5(self:: $key ), $info , MCRYPT_MODE_OFB, md5(self:: $iv ));<br/> $tokenHex = "" ;<br/> $tokenLength = strlen( $token );<br/> for ( $i = 0 ; $i < $tokenLength ; $i ++) {<br/> $tokenHex .= sprintf( "%02x" , ord( $token { $i }));<br/> }<br/> return $tokenHex ;<br/> }<br/> <br/> public static function check( $tokenHex , $mode = null ) {<br/> $token = "" ;<br/> $tokenHexLength = strlen( $tokenHex ) / 2 ;<br/> for ( $i = 0 ; $i < $tokenHexLength ; $i ++) {<br/> $token .= chr(hexdec(substr( $tokenHex , $i * 2 , 2 )));<br/> }<br/> $info = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5(self:: $key ), $token , MCRYPT_MODE_OFB, md5(self:: $iv ));<br/> if (strlen( $info ) == 18 ) {<br/> $info = array ( "id" => self::char2int(substr( $info , 0 , 4 )), "time" => self::char2int(substr( $info , 4 , 4 )), "expire" => self::char2int(substr( $info , 8 , 4 )), "mode" => ord( $info { 12 }), "rnd" => ord( $info { 13 }), "sum" => self::char2int(substr( $info , 14 , 4 )));<br/> if ( $info [ "sum" ] == $info [ "time" ] - $info [ "expire" ] - $info [ "mode" ] - $info [ "rnd" ] - $info [ "id" ]) {<br/> if ( $info [ "expire" ] > 0 ) {<br/> if ( $info [ "expire" ] + $info [ "time" ] < time()) {<br/> return false ;<br/> }<br/> }<br/> if ( $info [ "mode" ] > 0 ) {<br/> if ( $mode !== null ) {<br/> if ( $info [ "mode" ] != $mode ) {<br/> return false ;<br/> }<br/> }<br/> }<br/> return $info [ "id" ];<br/> } else {<br/> return false ;<br/> }<br/> } else {<br/> return false ;<br/> }<br/> }<br/>}<br/> <br/> ?> <br/> <br/>

The keys self :: $ key and self :: $ iv, for security reasons, are best kept away from the algorithm.

(string) AuthToken :: create ($ id, $ expire = 0, $ mode = 0)
Creates a key.

(mixed) AuthToken :: check ($ tokenHex, $ mode = null)
Checks the validity of the key. In case of successful verification returns the user ID, otherwise the logical FALSE.

If you do not specify $ expire and / or $ mode - they will not be taken into account when checking the key. If you do not specify $ mode when checking - it will also not be taken into account.

If nothing helps?


Such a solution is not suitable if you need a one-time key. But it can be solved with small crutches. For example, setting the check key of such a key in memchache in combination with a limited key validity time will give the desired result in most cases.

Well, then - either a fantasy or a database.

Source: https://habr.com/ru/post/109421/


All Articles