best practice to generate random token for forgot password
I want to generate identifier for forgot password . I read i can do it by using timestamp with mt_rand(), but some people are saying that time stamp might not be unique every time. So i am bit of confused here. Can i do it with using time stamp with this ?
Question
What's best practice to generate random/unique tokens of custom length?
I know there are lot of questions asked around here but i am getting more confused after reading different opinion from the different people.
php security random timestamp token
|
show 1 more comment
I want to generate identifier for forgot password . I read i can do it by using timestamp with mt_rand(), but some people are saying that time stamp might not be unique every time. So i am bit of confused here. Can i do it with using time stamp with this ?
Question
What's best practice to generate random/unique tokens of custom length?
I know there are lot of questions asked around here but i am getting more confused after reading different opinion from the different people.
php security random timestamp token
@AlmaDoMundo: A computer can't divide time unlimited.
– juergen d
Sep 20 '13 at 7:15
@juergend - sorry, do not get that.
– Alma Do
Sep 20 '13 at 7:15
You will get the same timestamp if you call it for instance a nano second apart. Some time functions for instance can only return time in 100ns steps, some only in seconds step.
– juergen d
Sep 20 '13 at 7:17
@juergend ah, that. Yes. I mentioned 'classic' timestamp with seconds only. But if act like you've said - yes (that only leaves us an option with time machine to get non-unique timestamp)
– Alma Do
Sep 20 '13 at 7:18
1
Head's up, the accepted answer does not leverage a CSPRNG.
– Scott Arciszewski
Jul 14 '15 at 22:47
|
show 1 more comment
I want to generate identifier for forgot password . I read i can do it by using timestamp with mt_rand(), but some people are saying that time stamp might not be unique every time. So i am bit of confused here. Can i do it with using time stamp with this ?
Question
What's best practice to generate random/unique tokens of custom length?
I know there are lot of questions asked around here but i am getting more confused after reading different opinion from the different people.
php security random timestamp token
I want to generate identifier for forgot password . I read i can do it by using timestamp with mt_rand(), but some people are saying that time stamp might not be unique every time. So i am bit of confused here. Can i do it with using time stamp with this ?
Question
What's best practice to generate random/unique tokens of custom length?
I know there are lot of questions asked around here but i am getting more confused after reading different opinion from the different people.
php security random timestamp token
php security random timestamp token
edited Dec 20 '15 at 6:45
Scott Arciszewski
23k1057155
23k1057155
asked Sep 20 '13 at 7:06
keenkeen
1,53732154
1,53732154
@AlmaDoMundo: A computer can't divide time unlimited.
– juergen d
Sep 20 '13 at 7:15
@juergend - sorry, do not get that.
– Alma Do
Sep 20 '13 at 7:15
You will get the same timestamp if you call it for instance a nano second apart. Some time functions for instance can only return time in 100ns steps, some only in seconds step.
– juergen d
Sep 20 '13 at 7:17
@juergend ah, that. Yes. I mentioned 'classic' timestamp with seconds only. But if act like you've said - yes (that only leaves us an option with time machine to get non-unique timestamp)
– Alma Do
Sep 20 '13 at 7:18
1
Head's up, the accepted answer does not leverage a CSPRNG.
– Scott Arciszewski
Jul 14 '15 at 22:47
|
show 1 more comment
@AlmaDoMundo: A computer can't divide time unlimited.
– juergen d
Sep 20 '13 at 7:15
@juergend - sorry, do not get that.
– Alma Do
Sep 20 '13 at 7:15
You will get the same timestamp if you call it for instance a nano second apart. Some time functions for instance can only return time in 100ns steps, some only in seconds step.
– juergen d
Sep 20 '13 at 7:17
@juergend ah, that. Yes. I mentioned 'classic' timestamp with seconds only. But if act like you've said - yes (that only leaves us an option with time machine to get non-unique timestamp)
– Alma Do
Sep 20 '13 at 7:18
1
Head's up, the accepted answer does not leverage a CSPRNG.
– Scott Arciszewski
Jul 14 '15 at 22:47
@AlmaDoMundo: A computer can't divide time unlimited.
– juergen d
Sep 20 '13 at 7:15
@AlmaDoMundo: A computer can't divide time unlimited.
– juergen d
Sep 20 '13 at 7:15
@juergend - sorry, do not get that.
– Alma Do
Sep 20 '13 at 7:15
@juergend - sorry, do not get that.
– Alma Do
Sep 20 '13 at 7:15
You will get the same timestamp if you call it for instance a nano second apart. Some time functions for instance can only return time in 100ns steps, some only in seconds step.
– juergen d
Sep 20 '13 at 7:17
You will get the same timestamp if you call it for instance a nano second apart. Some time functions for instance can only return time in 100ns steps, some only in seconds step.
– juergen d
Sep 20 '13 at 7:17
@juergend ah, that. Yes. I mentioned 'classic' timestamp with seconds only. But if act like you've said - yes (that only leaves us an option with time machine to get non-unique timestamp)
– Alma Do
Sep 20 '13 at 7:18
@juergend ah, that. Yes. I mentioned 'classic' timestamp with seconds only. But if act like you've said - yes (that only leaves us an option with time machine to get non-unique timestamp)
– Alma Do
Sep 20 '13 at 7:18
1
1
Head's up, the accepted answer does not leverage a CSPRNG.
– Scott Arciszewski
Jul 14 '15 at 22:47
Head's up, the accepted answer does not leverage a CSPRNG.
– Scott Arciszewski
Jul 14 '15 at 22:47
|
show 1 more comment
6 Answers
6
active
oldest
votes
In PHP, use random_bytes()
. Reason: your are seeking the way to get a password reminder token, and, if it is a one-time login credentials, then you actually have a data to protect (which is - whole user account)
So, the code will be as follows:
//$length = 78 etc
$token = bin2hex(random_bytes($length));
Update: previous versions of this answer was referring to uniqid()
and that is incorrect if there is a matter of security and not only uniqueness. uniqid()
is essentially just microtime()
with some encoding. There are simple ways to get accurate predictions of the microtime()
on your server. An attacker can issue a password reset request and then try through a couple of likely tokens. This is also possible if more_entropy is used, as the additional entropy is similarly weak. Thanks to @NikiC and @ScottArciszewski for pointing this out.
For more details see
- http://phpsecurity.readthedocs.org/en/latest/Insufficient-Entropy-For-Random-Values.html
17
Note thatrandom_bytes()
is only available as of PHP7. For older versions, the answer by @yesitsme seems to be the best option.
– Gerald Schneider
Jul 26 '15 at 13:36
3
@GeraldSchneider or random_compat, which is the polyfill for these features that has received the most peer review ;)
– Scott Arciszewski
Aug 13 '15 at 15:36
I made a varchar(64) field in my sql database to store this token. I set $length to 64, but the string returned is 128 characters long. How can I get a string with a fixed size (here, 64 then) ?
– gordie
Feb 22 '16 at 19:12
2
@gordie Set the length to 32, each byte is 2 hex characters
– JohnHoulderUK
Jun 21 '16 at 10:05
What should be$length
? The id of user? Or what?
– stack
Jun 24 '16 at 14:41
add a comment |
This answers the 'best random' request:
Adi's answer1 from Security.StackExchange has a solution for this:
Make sure you have OpenSSL support, and you'll never go wrong with this one-liner
$token = bin2hex(openssl_random_pseudo_bytes(16));
1. Adi, Mon Nov 12 2018, Celeritas, "Generating an unguessable token for confirmation e-mails", Sep 20 '13 at 7:06, https://security.stackexchange.com/a/40314/
19
openssl_random_pseudo_bytes($length)
- support: PHP 5 >= 5.3.0 , .......................................................... (For PHP 7 and up, userandom_bytes($length)
) .......................................... (For PHP below 5.3 - don't use PHP below 5.3)
– jave.web
Jan 3 '16 at 22:46
For php 5.3 to php7 use github.com/paragonie/random_compat
– dsas
Dec 21 '16 at 10:59
add a comment |
The earlier version of the accepted answer (md5(uniqid(mt_rand(), true))
) is insecure and only offers about 2^60 possible outputs -- well within the range of a brute force search in about a week's time for a low-budget attacker:
mt_rand()
is predictable (and only adds up to 31 bits of entropy)
uniqid()
only adds up to 29 bits of entropy
md5()
doesn't add entropy, it just mixes it deterministically
Since a 56-bit DES key can be brute-forced in about 24 hours, and an average case would have about 59 bits of entropy, we can calculate 2^59 / 2^56 = about 8 days. Depending on how this token verification is implemented, it might be possible to practically leak timing information and infer the first N bytes of a valid reset token.
Since the question is about "best practices" and opens with...
I want to generate identifier for forgot password
...we can infer that this token has implicit security requirements. And when you add security requirements to a random number generator, the best practice is to always use a cryptographically secure pseudorandom number generator (abbreviated CSPRNG).
Using a CSPRNG
In PHP 7, you can use bin2hex(random_bytes($n))
(where $n
is an integer larger than 15).
In PHP 5, you can use random_compat
to expose the same API.
Alternatively, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
if you have ext/mcrypt
installed. Another good one-liner is bin2hex(openssl_random_pseudo_bytes($n))
.
Separating the Lookup from the Validator
Pulling from my previous work on secure "remember me" cookies in PHP, the only effective way to mitigate the aforementioned timing leak (typically introduced by the database query) is to separate the lookup from the validation.
If your table looks like this (MySQL)...
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id)
);
... you need to add one more column, selector
, like so:
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
selector CHAR(16),
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id),
KEY(selector)
);
Use a CSPRNG When a password reset token is issued, send both values to the user, store the selector and a SHA-256 hash of the random token in the database. Use the selector to grab the hash and User ID, calculate the SHA-256 hash of the token the user provides with the one stored in the database using hash_equals()
.
Example Code
Generating a reset token in PHP 7 (or 5.6 with random_compat) with PDO:
$selector = bin2hex(random_bytes(8));
$token = random_bytes(32);
$urlToEmail = 'http://example.com/reset.php?'.http_build_query([
'selector' => $selector,
'validator' => bin2hex($token)
]);
$expires = new DateTime('NOW');
$expires->add(new DateInterval('PT01H')); // 1 hour
$stmt = $pdo->prepare("INSERT INTO account_recovery (userid, selector, token, expires) VALUES (:userid, :selector, :token, :expires);");
$stmt->execute([
'userid' => $userId, // define this elsewhere!
'selector' => $selector,
'token' => hash('sha256', $token),
'expires' => $expires->format('Y-m-dTH:i:s')
]);
Verifying the user-provided reset token:
$stmt = $pdo->prepare("SELECT * FROM account_recovery WHERE selector = ? AND expires >= NOW()");
$stmt->execute([$selector]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($results)) {
$calc = hash('sha256', hex2bin($validator));
if (hash_equals($calc, $results[0]['token'])) {
// The reset token is valid. Authenticate the user.
}
// Remove the token from the DB regardless of success or failure.
}
These code snippets are not complete solutions (I eschewed the input validation and framework integrations), but they should serve as an example of what to do.
When you verify the user-provided reset token, why do you use the binary representation of the random token? Do you think it will be possible (and secure?) to: 1) store in DB the hashed hex value of the token withhash('sha256', bin2hex($token))
, 2) verify withif (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Thanks!
– Guicara
Dec 18 '15 at 16:25
Yes, comparing hex strings is secure too. It's really a matter of preference. I prefer to do all crypto operations on raw binary and only ever convert to hex/base64 for transmission or storage.
– Scott Arciszewski
Dec 18 '15 at 18:56
Hi Scott, it is basically a question not only for your answer, but for the entire article about the "Remember Me" feature. Why not to use the uniqueid
as the selector? I mean, the primary key of theaccount_recovery
table. We don't need additional layer of security for the selector, do we? Thanks!
– Andre Polykanine
May 28 '17 at 21:31
id:secret
is OK.selector:secret
is OK.secret
itself is not. The goal is to separate the database query (which is timing-leaky) from the authentication protocol (which should be constant time).
– Scott Arciszewski
May 29 '17 at 5:50
Is there any harm in usingopenssl_random_pseudo_bytes
insteadrandom_bytes
if is running PHP 5.6? Also, shouldn't you append just the selector and not the validator in the querystring of the link?
– greg
Jul 11 '18 at 14:09
|
show 1 more comment
You can also use DEV_RANDOM, where 128 = 1/2 the generated token length. Code below generates 256 token.
$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
3
I would suggestMCRYPT_DEV_URANDOM
overMCRYPT_DEV_RANDOM
.
– Scott Arciszewski
Jul 14 '15 at 22:45
add a comment |
This may be helpful whenever you need a very very random token
<?php
echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>
add a comment |
You can use
echo str_shuffle('ASGDHFfdgfdre5475433fd');
Hahaha yes this!
– Jeff Puckett
Feb 10 '17 at 14:41
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f18910814%2fbest-practice-to-generate-random-token-for-forgot-password%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
In PHP, use random_bytes()
. Reason: your are seeking the way to get a password reminder token, and, if it is a one-time login credentials, then you actually have a data to protect (which is - whole user account)
So, the code will be as follows:
//$length = 78 etc
$token = bin2hex(random_bytes($length));
Update: previous versions of this answer was referring to uniqid()
and that is incorrect if there is a matter of security and not only uniqueness. uniqid()
is essentially just microtime()
with some encoding. There are simple ways to get accurate predictions of the microtime()
on your server. An attacker can issue a password reset request and then try through a couple of likely tokens. This is also possible if more_entropy is used, as the additional entropy is similarly weak. Thanks to @NikiC and @ScottArciszewski for pointing this out.
For more details see
- http://phpsecurity.readthedocs.org/en/latest/Insufficient-Entropy-For-Random-Values.html
17
Note thatrandom_bytes()
is only available as of PHP7. For older versions, the answer by @yesitsme seems to be the best option.
– Gerald Schneider
Jul 26 '15 at 13:36
3
@GeraldSchneider or random_compat, which is the polyfill for these features that has received the most peer review ;)
– Scott Arciszewski
Aug 13 '15 at 15:36
I made a varchar(64) field in my sql database to store this token. I set $length to 64, but the string returned is 128 characters long. How can I get a string with a fixed size (here, 64 then) ?
– gordie
Feb 22 '16 at 19:12
2
@gordie Set the length to 32, each byte is 2 hex characters
– JohnHoulderUK
Jun 21 '16 at 10:05
What should be$length
? The id of user? Or what?
– stack
Jun 24 '16 at 14:41
add a comment |
In PHP, use random_bytes()
. Reason: your are seeking the way to get a password reminder token, and, if it is a one-time login credentials, then you actually have a data to protect (which is - whole user account)
So, the code will be as follows:
//$length = 78 etc
$token = bin2hex(random_bytes($length));
Update: previous versions of this answer was referring to uniqid()
and that is incorrect if there is a matter of security and not only uniqueness. uniqid()
is essentially just microtime()
with some encoding. There are simple ways to get accurate predictions of the microtime()
on your server. An attacker can issue a password reset request and then try through a couple of likely tokens. This is also possible if more_entropy is used, as the additional entropy is similarly weak. Thanks to @NikiC and @ScottArciszewski for pointing this out.
For more details see
- http://phpsecurity.readthedocs.org/en/latest/Insufficient-Entropy-For-Random-Values.html
17
Note thatrandom_bytes()
is only available as of PHP7. For older versions, the answer by @yesitsme seems to be the best option.
– Gerald Schneider
Jul 26 '15 at 13:36
3
@GeraldSchneider or random_compat, which is the polyfill for these features that has received the most peer review ;)
– Scott Arciszewski
Aug 13 '15 at 15:36
I made a varchar(64) field in my sql database to store this token. I set $length to 64, but the string returned is 128 characters long. How can I get a string with a fixed size (here, 64 then) ?
– gordie
Feb 22 '16 at 19:12
2
@gordie Set the length to 32, each byte is 2 hex characters
– JohnHoulderUK
Jun 21 '16 at 10:05
What should be$length
? The id of user? Or what?
– stack
Jun 24 '16 at 14:41
add a comment |
In PHP, use random_bytes()
. Reason: your are seeking the way to get a password reminder token, and, if it is a one-time login credentials, then you actually have a data to protect (which is - whole user account)
So, the code will be as follows:
//$length = 78 etc
$token = bin2hex(random_bytes($length));
Update: previous versions of this answer was referring to uniqid()
and that is incorrect if there is a matter of security and not only uniqueness. uniqid()
is essentially just microtime()
with some encoding. There are simple ways to get accurate predictions of the microtime()
on your server. An attacker can issue a password reset request and then try through a couple of likely tokens. This is also possible if more_entropy is used, as the additional entropy is similarly weak. Thanks to @NikiC and @ScottArciszewski for pointing this out.
For more details see
- http://phpsecurity.readthedocs.org/en/latest/Insufficient-Entropy-For-Random-Values.html
In PHP, use random_bytes()
. Reason: your are seeking the way to get a password reminder token, and, if it is a one-time login credentials, then you actually have a data to protect (which is - whole user account)
So, the code will be as follows:
//$length = 78 etc
$token = bin2hex(random_bytes($length));
Update: previous versions of this answer was referring to uniqid()
and that is incorrect if there is a matter of security and not only uniqueness. uniqid()
is essentially just microtime()
with some encoding. There are simple ways to get accurate predictions of the microtime()
on your server. An attacker can issue a password reset request and then try through a couple of likely tokens. This is also possible if more_entropy is used, as the additional entropy is similarly weak. Thanks to @NikiC and @ScottArciszewski for pointing this out.
For more details see
- http://phpsecurity.readthedocs.org/en/latest/Insufficient-Entropy-For-Random-Values.html
edited May 23 '17 at 11:33
Community♦
11
11
answered Sep 20 '13 at 7:14
Alma DoAlma Do
31.5k75288
31.5k75288
17
Note thatrandom_bytes()
is only available as of PHP7. For older versions, the answer by @yesitsme seems to be the best option.
– Gerald Schneider
Jul 26 '15 at 13:36
3
@GeraldSchneider or random_compat, which is the polyfill for these features that has received the most peer review ;)
– Scott Arciszewski
Aug 13 '15 at 15:36
I made a varchar(64) field in my sql database to store this token. I set $length to 64, but the string returned is 128 characters long. How can I get a string with a fixed size (here, 64 then) ?
– gordie
Feb 22 '16 at 19:12
2
@gordie Set the length to 32, each byte is 2 hex characters
– JohnHoulderUK
Jun 21 '16 at 10:05
What should be$length
? The id of user? Or what?
– stack
Jun 24 '16 at 14:41
add a comment |
17
Note thatrandom_bytes()
is only available as of PHP7. For older versions, the answer by @yesitsme seems to be the best option.
– Gerald Schneider
Jul 26 '15 at 13:36
3
@GeraldSchneider or random_compat, which is the polyfill for these features that has received the most peer review ;)
– Scott Arciszewski
Aug 13 '15 at 15:36
I made a varchar(64) field in my sql database to store this token. I set $length to 64, but the string returned is 128 characters long. How can I get a string with a fixed size (here, 64 then) ?
– gordie
Feb 22 '16 at 19:12
2
@gordie Set the length to 32, each byte is 2 hex characters
– JohnHoulderUK
Jun 21 '16 at 10:05
What should be$length
? The id of user? Or what?
– stack
Jun 24 '16 at 14:41
17
17
Note that
random_bytes()
is only available as of PHP7. For older versions, the answer by @yesitsme seems to be the best option.– Gerald Schneider
Jul 26 '15 at 13:36
Note that
random_bytes()
is only available as of PHP7. For older versions, the answer by @yesitsme seems to be the best option.– Gerald Schneider
Jul 26 '15 at 13:36
3
3
@GeraldSchneider or random_compat, which is the polyfill for these features that has received the most peer review ;)
– Scott Arciszewski
Aug 13 '15 at 15:36
@GeraldSchneider or random_compat, which is the polyfill for these features that has received the most peer review ;)
– Scott Arciszewski
Aug 13 '15 at 15:36
I made a varchar(64) field in my sql database to store this token. I set $length to 64, but the string returned is 128 characters long. How can I get a string with a fixed size (here, 64 then) ?
– gordie
Feb 22 '16 at 19:12
I made a varchar(64) field in my sql database to store this token. I set $length to 64, but the string returned is 128 characters long. How can I get a string with a fixed size (here, 64 then) ?
– gordie
Feb 22 '16 at 19:12
2
2
@gordie Set the length to 32, each byte is 2 hex characters
– JohnHoulderUK
Jun 21 '16 at 10:05
@gordie Set the length to 32, each byte is 2 hex characters
– JohnHoulderUK
Jun 21 '16 at 10:05
What should be
$length
? The id of user? Or what?– stack
Jun 24 '16 at 14:41
What should be
$length
? The id of user? Or what?– stack
Jun 24 '16 at 14:41
add a comment |
This answers the 'best random' request:
Adi's answer1 from Security.StackExchange has a solution for this:
Make sure you have OpenSSL support, and you'll never go wrong with this one-liner
$token = bin2hex(openssl_random_pseudo_bytes(16));
1. Adi, Mon Nov 12 2018, Celeritas, "Generating an unguessable token for confirmation e-mails", Sep 20 '13 at 7:06, https://security.stackexchange.com/a/40314/
19
openssl_random_pseudo_bytes($length)
- support: PHP 5 >= 5.3.0 , .......................................................... (For PHP 7 and up, userandom_bytes($length)
) .......................................... (For PHP below 5.3 - don't use PHP below 5.3)
– jave.web
Jan 3 '16 at 22:46
For php 5.3 to php7 use github.com/paragonie/random_compat
– dsas
Dec 21 '16 at 10:59
add a comment |
This answers the 'best random' request:
Adi's answer1 from Security.StackExchange has a solution for this:
Make sure you have OpenSSL support, and you'll never go wrong with this one-liner
$token = bin2hex(openssl_random_pseudo_bytes(16));
1. Adi, Mon Nov 12 2018, Celeritas, "Generating an unguessable token for confirmation e-mails", Sep 20 '13 at 7:06, https://security.stackexchange.com/a/40314/
19
openssl_random_pseudo_bytes($length)
- support: PHP 5 >= 5.3.0 , .......................................................... (For PHP 7 and up, userandom_bytes($length)
) .......................................... (For PHP below 5.3 - don't use PHP below 5.3)
– jave.web
Jan 3 '16 at 22:46
For php 5.3 to php7 use github.com/paragonie/random_compat
– dsas
Dec 21 '16 at 10:59
add a comment |
This answers the 'best random' request:
Adi's answer1 from Security.StackExchange has a solution for this:
Make sure you have OpenSSL support, and you'll never go wrong with this one-liner
$token = bin2hex(openssl_random_pseudo_bytes(16));
1. Adi, Mon Nov 12 2018, Celeritas, "Generating an unguessable token for confirmation e-mails", Sep 20 '13 at 7:06, https://security.stackexchange.com/a/40314/
This answers the 'best random' request:
Adi's answer1 from Security.StackExchange has a solution for this:
Make sure you have OpenSSL support, and you'll never go wrong with this one-liner
$token = bin2hex(openssl_random_pseudo_bytes(16));
1. Adi, Mon Nov 12 2018, Celeritas, "Generating an unguessable token for confirmation e-mails", Sep 20 '13 at 7:06, https://security.stackexchange.com/a/40314/
edited Nov 14 '18 at 15:04
TylerH
15.8k105368
15.8k105368
answered Mar 19 '15 at 5:17
yesitsmeyesitsme
1,204924
1,204924
19
openssl_random_pseudo_bytes($length)
- support: PHP 5 >= 5.3.0 , .......................................................... (For PHP 7 and up, userandom_bytes($length)
) .......................................... (For PHP below 5.3 - don't use PHP below 5.3)
– jave.web
Jan 3 '16 at 22:46
For php 5.3 to php7 use github.com/paragonie/random_compat
– dsas
Dec 21 '16 at 10:59
add a comment |
19
openssl_random_pseudo_bytes($length)
- support: PHP 5 >= 5.3.0 , .......................................................... (For PHP 7 and up, userandom_bytes($length)
) .......................................... (For PHP below 5.3 - don't use PHP below 5.3)
– jave.web
Jan 3 '16 at 22:46
For php 5.3 to php7 use github.com/paragonie/random_compat
– dsas
Dec 21 '16 at 10:59
19
19
openssl_random_pseudo_bytes($length)
- support: PHP 5 >= 5.3.0 , .......................................................... (For PHP 7 and up, use random_bytes($length)
) .......................................... (For PHP below 5.3 - don't use PHP below 5.3)– jave.web
Jan 3 '16 at 22:46
openssl_random_pseudo_bytes($length)
- support: PHP 5 >= 5.3.0 , .......................................................... (For PHP 7 and up, use random_bytes($length)
) .......................................... (For PHP below 5.3 - don't use PHP below 5.3)– jave.web
Jan 3 '16 at 22:46
For php 5.3 to php7 use github.com/paragonie/random_compat
– dsas
Dec 21 '16 at 10:59
For php 5.3 to php7 use github.com/paragonie/random_compat
– dsas
Dec 21 '16 at 10:59
add a comment |
The earlier version of the accepted answer (md5(uniqid(mt_rand(), true))
) is insecure and only offers about 2^60 possible outputs -- well within the range of a brute force search in about a week's time for a low-budget attacker:
mt_rand()
is predictable (and only adds up to 31 bits of entropy)
uniqid()
only adds up to 29 bits of entropy
md5()
doesn't add entropy, it just mixes it deterministically
Since a 56-bit DES key can be brute-forced in about 24 hours, and an average case would have about 59 bits of entropy, we can calculate 2^59 / 2^56 = about 8 days. Depending on how this token verification is implemented, it might be possible to practically leak timing information and infer the first N bytes of a valid reset token.
Since the question is about "best practices" and opens with...
I want to generate identifier for forgot password
...we can infer that this token has implicit security requirements. And when you add security requirements to a random number generator, the best practice is to always use a cryptographically secure pseudorandom number generator (abbreviated CSPRNG).
Using a CSPRNG
In PHP 7, you can use bin2hex(random_bytes($n))
(where $n
is an integer larger than 15).
In PHP 5, you can use random_compat
to expose the same API.
Alternatively, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
if you have ext/mcrypt
installed. Another good one-liner is bin2hex(openssl_random_pseudo_bytes($n))
.
Separating the Lookup from the Validator
Pulling from my previous work on secure "remember me" cookies in PHP, the only effective way to mitigate the aforementioned timing leak (typically introduced by the database query) is to separate the lookup from the validation.
If your table looks like this (MySQL)...
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id)
);
... you need to add one more column, selector
, like so:
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
selector CHAR(16),
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id),
KEY(selector)
);
Use a CSPRNG When a password reset token is issued, send both values to the user, store the selector and a SHA-256 hash of the random token in the database. Use the selector to grab the hash and User ID, calculate the SHA-256 hash of the token the user provides with the one stored in the database using hash_equals()
.
Example Code
Generating a reset token in PHP 7 (or 5.6 with random_compat) with PDO:
$selector = bin2hex(random_bytes(8));
$token = random_bytes(32);
$urlToEmail = 'http://example.com/reset.php?'.http_build_query([
'selector' => $selector,
'validator' => bin2hex($token)
]);
$expires = new DateTime('NOW');
$expires->add(new DateInterval('PT01H')); // 1 hour
$stmt = $pdo->prepare("INSERT INTO account_recovery (userid, selector, token, expires) VALUES (:userid, :selector, :token, :expires);");
$stmt->execute([
'userid' => $userId, // define this elsewhere!
'selector' => $selector,
'token' => hash('sha256', $token),
'expires' => $expires->format('Y-m-dTH:i:s')
]);
Verifying the user-provided reset token:
$stmt = $pdo->prepare("SELECT * FROM account_recovery WHERE selector = ? AND expires >= NOW()");
$stmt->execute([$selector]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($results)) {
$calc = hash('sha256', hex2bin($validator));
if (hash_equals($calc, $results[0]['token'])) {
// The reset token is valid. Authenticate the user.
}
// Remove the token from the DB regardless of success or failure.
}
These code snippets are not complete solutions (I eschewed the input validation and framework integrations), but they should serve as an example of what to do.
When you verify the user-provided reset token, why do you use the binary representation of the random token? Do you think it will be possible (and secure?) to: 1) store in DB the hashed hex value of the token withhash('sha256', bin2hex($token))
, 2) verify withif (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Thanks!
– Guicara
Dec 18 '15 at 16:25
Yes, comparing hex strings is secure too. It's really a matter of preference. I prefer to do all crypto operations on raw binary and only ever convert to hex/base64 for transmission or storage.
– Scott Arciszewski
Dec 18 '15 at 18:56
Hi Scott, it is basically a question not only for your answer, but for the entire article about the "Remember Me" feature. Why not to use the uniqueid
as the selector? I mean, the primary key of theaccount_recovery
table. We don't need additional layer of security for the selector, do we? Thanks!
– Andre Polykanine
May 28 '17 at 21:31
id:secret
is OK.selector:secret
is OK.secret
itself is not. The goal is to separate the database query (which is timing-leaky) from the authentication protocol (which should be constant time).
– Scott Arciszewski
May 29 '17 at 5:50
Is there any harm in usingopenssl_random_pseudo_bytes
insteadrandom_bytes
if is running PHP 5.6? Also, shouldn't you append just the selector and not the validator in the querystring of the link?
– greg
Jul 11 '18 at 14:09
|
show 1 more comment
The earlier version of the accepted answer (md5(uniqid(mt_rand(), true))
) is insecure and only offers about 2^60 possible outputs -- well within the range of a brute force search in about a week's time for a low-budget attacker:
mt_rand()
is predictable (and only adds up to 31 bits of entropy)
uniqid()
only adds up to 29 bits of entropy
md5()
doesn't add entropy, it just mixes it deterministically
Since a 56-bit DES key can be brute-forced in about 24 hours, and an average case would have about 59 bits of entropy, we can calculate 2^59 / 2^56 = about 8 days. Depending on how this token verification is implemented, it might be possible to practically leak timing information and infer the first N bytes of a valid reset token.
Since the question is about "best practices" and opens with...
I want to generate identifier for forgot password
...we can infer that this token has implicit security requirements. And when you add security requirements to a random number generator, the best practice is to always use a cryptographically secure pseudorandom number generator (abbreviated CSPRNG).
Using a CSPRNG
In PHP 7, you can use bin2hex(random_bytes($n))
(where $n
is an integer larger than 15).
In PHP 5, you can use random_compat
to expose the same API.
Alternatively, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
if you have ext/mcrypt
installed. Another good one-liner is bin2hex(openssl_random_pseudo_bytes($n))
.
Separating the Lookup from the Validator
Pulling from my previous work on secure "remember me" cookies in PHP, the only effective way to mitigate the aforementioned timing leak (typically introduced by the database query) is to separate the lookup from the validation.
If your table looks like this (MySQL)...
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id)
);
... you need to add one more column, selector
, like so:
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
selector CHAR(16),
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id),
KEY(selector)
);
Use a CSPRNG When a password reset token is issued, send both values to the user, store the selector and a SHA-256 hash of the random token in the database. Use the selector to grab the hash and User ID, calculate the SHA-256 hash of the token the user provides with the one stored in the database using hash_equals()
.
Example Code
Generating a reset token in PHP 7 (or 5.6 with random_compat) with PDO:
$selector = bin2hex(random_bytes(8));
$token = random_bytes(32);
$urlToEmail = 'http://example.com/reset.php?'.http_build_query([
'selector' => $selector,
'validator' => bin2hex($token)
]);
$expires = new DateTime('NOW');
$expires->add(new DateInterval('PT01H')); // 1 hour
$stmt = $pdo->prepare("INSERT INTO account_recovery (userid, selector, token, expires) VALUES (:userid, :selector, :token, :expires);");
$stmt->execute([
'userid' => $userId, // define this elsewhere!
'selector' => $selector,
'token' => hash('sha256', $token),
'expires' => $expires->format('Y-m-dTH:i:s')
]);
Verifying the user-provided reset token:
$stmt = $pdo->prepare("SELECT * FROM account_recovery WHERE selector = ? AND expires >= NOW()");
$stmt->execute([$selector]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($results)) {
$calc = hash('sha256', hex2bin($validator));
if (hash_equals($calc, $results[0]['token'])) {
// The reset token is valid. Authenticate the user.
}
// Remove the token from the DB regardless of success or failure.
}
These code snippets are not complete solutions (I eschewed the input validation and framework integrations), but they should serve as an example of what to do.
When you verify the user-provided reset token, why do you use the binary representation of the random token? Do you think it will be possible (and secure?) to: 1) store in DB the hashed hex value of the token withhash('sha256', bin2hex($token))
, 2) verify withif (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Thanks!
– Guicara
Dec 18 '15 at 16:25
Yes, comparing hex strings is secure too. It's really a matter of preference. I prefer to do all crypto operations on raw binary and only ever convert to hex/base64 for transmission or storage.
– Scott Arciszewski
Dec 18 '15 at 18:56
Hi Scott, it is basically a question not only for your answer, but for the entire article about the "Remember Me" feature. Why not to use the uniqueid
as the selector? I mean, the primary key of theaccount_recovery
table. We don't need additional layer of security for the selector, do we? Thanks!
– Andre Polykanine
May 28 '17 at 21:31
id:secret
is OK.selector:secret
is OK.secret
itself is not. The goal is to separate the database query (which is timing-leaky) from the authentication protocol (which should be constant time).
– Scott Arciszewski
May 29 '17 at 5:50
Is there any harm in usingopenssl_random_pseudo_bytes
insteadrandom_bytes
if is running PHP 5.6? Also, shouldn't you append just the selector and not the validator in the querystring of the link?
– greg
Jul 11 '18 at 14:09
|
show 1 more comment
The earlier version of the accepted answer (md5(uniqid(mt_rand(), true))
) is insecure and only offers about 2^60 possible outputs -- well within the range of a brute force search in about a week's time for a low-budget attacker:
mt_rand()
is predictable (and only adds up to 31 bits of entropy)
uniqid()
only adds up to 29 bits of entropy
md5()
doesn't add entropy, it just mixes it deterministically
Since a 56-bit DES key can be brute-forced in about 24 hours, and an average case would have about 59 bits of entropy, we can calculate 2^59 / 2^56 = about 8 days. Depending on how this token verification is implemented, it might be possible to practically leak timing information and infer the first N bytes of a valid reset token.
Since the question is about "best practices" and opens with...
I want to generate identifier for forgot password
...we can infer that this token has implicit security requirements. And when you add security requirements to a random number generator, the best practice is to always use a cryptographically secure pseudorandom number generator (abbreviated CSPRNG).
Using a CSPRNG
In PHP 7, you can use bin2hex(random_bytes($n))
(where $n
is an integer larger than 15).
In PHP 5, you can use random_compat
to expose the same API.
Alternatively, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
if you have ext/mcrypt
installed. Another good one-liner is bin2hex(openssl_random_pseudo_bytes($n))
.
Separating the Lookup from the Validator
Pulling from my previous work on secure "remember me" cookies in PHP, the only effective way to mitigate the aforementioned timing leak (typically introduced by the database query) is to separate the lookup from the validation.
If your table looks like this (MySQL)...
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id)
);
... you need to add one more column, selector
, like so:
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
selector CHAR(16),
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id),
KEY(selector)
);
Use a CSPRNG When a password reset token is issued, send both values to the user, store the selector and a SHA-256 hash of the random token in the database. Use the selector to grab the hash and User ID, calculate the SHA-256 hash of the token the user provides with the one stored in the database using hash_equals()
.
Example Code
Generating a reset token in PHP 7 (or 5.6 with random_compat) with PDO:
$selector = bin2hex(random_bytes(8));
$token = random_bytes(32);
$urlToEmail = 'http://example.com/reset.php?'.http_build_query([
'selector' => $selector,
'validator' => bin2hex($token)
]);
$expires = new DateTime('NOW');
$expires->add(new DateInterval('PT01H')); // 1 hour
$stmt = $pdo->prepare("INSERT INTO account_recovery (userid, selector, token, expires) VALUES (:userid, :selector, :token, :expires);");
$stmt->execute([
'userid' => $userId, // define this elsewhere!
'selector' => $selector,
'token' => hash('sha256', $token),
'expires' => $expires->format('Y-m-dTH:i:s')
]);
Verifying the user-provided reset token:
$stmt = $pdo->prepare("SELECT * FROM account_recovery WHERE selector = ? AND expires >= NOW()");
$stmt->execute([$selector]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($results)) {
$calc = hash('sha256', hex2bin($validator));
if (hash_equals($calc, $results[0]['token'])) {
// The reset token is valid. Authenticate the user.
}
// Remove the token from the DB regardless of success or failure.
}
These code snippets are not complete solutions (I eschewed the input validation and framework integrations), but they should serve as an example of what to do.
The earlier version of the accepted answer (md5(uniqid(mt_rand(), true))
) is insecure and only offers about 2^60 possible outputs -- well within the range of a brute force search in about a week's time for a low-budget attacker:
mt_rand()
is predictable (and only adds up to 31 bits of entropy)
uniqid()
only adds up to 29 bits of entropy
md5()
doesn't add entropy, it just mixes it deterministically
Since a 56-bit DES key can be brute-forced in about 24 hours, and an average case would have about 59 bits of entropy, we can calculate 2^59 / 2^56 = about 8 days. Depending on how this token verification is implemented, it might be possible to practically leak timing information and infer the first N bytes of a valid reset token.
Since the question is about "best practices" and opens with...
I want to generate identifier for forgot password
...we can infer that this token has implicit security requirements. And when you add security requirements to a random number generator, the best practice is to always use a cryptographically secure pseudorandom number generator (abbreviated CSPRNG).
Using a CSPRNG
In PHP 7, you can use bin2hex(random_bytes($n))
(where $n
is an integer larger than 15).
In PHP 5, you can use random_compat
to expose the same API.
Alternatively, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
if you have ext/mcrypt
installed. Another good one-liner is bin2hex(openssl_random_pseudo_bytes($n))
.
Separating the Lookup from the Validator
Pulling from my previous work on secure "remember me" cookies in PHP, the only effective way to mitigate the aforementioned timing leak (typically introduced by the database query) is to separate the lookup from the validation.
If your table looks like this (MySQL)...
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id)
);
... you need to add one more column, selector
, like so:
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
selector CHAR(16),
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id),
KEY(selector)
);
Use a CSPRNG When a password reset token is issued, send both values to the user, store the selector and a SHA-256 hash of the random token in the database. Use the selector to grab the hash and User ID, calculate the SHA-256 hash of the token the user provides with the one stored in the database using hash_equals()
.
Example Code
Generating a reset token in PHP 7 (or 5.6 with random_compat) with PDO:
$selector = bin2hex(random_bytes(8));
$token = random_bytes(32);
$urlToEmail = 'http://example.com/reset.php?'.http_build_query([
'selector' => $selector,
'validator' => bin2hex($token)
]);
$expires = new DateTime('NOW');
$expires->add(new DateInterval('PT01H')); // 1 hour
$stmt = $pdo->prepare("INSERT INTO account_recovery (userid, selector, token, expires) VALUES (:userid, :selector, :token, :expires);");
$stmt->execute([
'userid' => $userId, // define this elsewhere!
'selector' => $selector,
'token' => hash('sha256', $token),
'expires' => $expires->format('Y-m-dTH:i:s')
]);
Verifying the user-provided reset token:
$stmt = $pdo->prepare("SELECT * FROM account_recovery WHERE selector = ? AND expires >= NOW()");
$stmt->execute([$selector]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($results)) {
$calc = hash('sha256', hex2bin($validator));
if (hash_equals($calc, $results[0]['token'])) {
// The reset token is valid. Authenticate the user.
}
// Remove the token from the DB regardless of success or failure.
}
These code snippets are not complete solutions (I eschewed the input validation and framework integrations), but they should serve as an example of what to do.
edited Jun 26 '16 at 4:38
answered Jul 14 '15 at 23:47
Scott ArciszewskiScott Arciszewski
23k1057155
23k1057155
When you verify the user-provided reset token, why do you use the binary representation of the random token? Do you think it will be possible (and secure?) to: 1) store in DB the hashed hex value of the token withhash('sha256', bin2hex($token))
, 2) verify withif (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Thanks!
– Guicara
Dec 18 '15 at 16:25
Yes, comparing hex strings is secure too. It's really a matter of preference. I prefer to do all crypto operations on raw binary and only ever convert to hex/base64 for transmission or storage.
– Scott Arciszewski
Dec 18 '15 at 18:56
Hi Scott, it is basically a question not only for your answer, but for the entire article about the "Remember Me" feature. Why not to use the uniqueid
as the selector? I mean, the primary key of theaccount_recovery
table. We don't need additional layer of security for the selector, do we? Thanks!
– Andre Polykanine
May 28 '17 at 21:31
id:secret
is OK.selector:secret
is OK.secret
itself is not. The goal is to separate the database query (which is timing-leaky) from the authentication protocol (which should be constant time).
– Scott Arciszewski
May 29 '17 at 5:50
Is there any harm in usingopenssl_random_pseudo_bytes
insteadrandom_bytes
if is running PHP 5.6? Also, shouldn't you append just the selector and not the validator in the querystring of the link?
– greg
Jul 11 '18 at 14:09
|
show 1 more comment
When you verify the user-provided reset token, why do you use the binary representation of the random token? Do you think it will be possible (and secure?) to: 1) store in DB the hashed hex value of the token withhash('sha256', bin2hex($token))
, 2) verify withif (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Thanks!
– Guicara
Dec 18 '15 at 16:25
Yes, comparing hex strings is secure too. It's really a matter of preference. I prefer to do all crypto operations on raw binary and only ever convert to hex/base64 for transmission or storage.
– Scott Arciszewski
Dec 18 '15 at 18:56
Hi Scott, it is basically a question not only for your answer, but for the entire article about the "Remember Me" feature. Why not to use the uniqueid
as the selector? I mean, the primary key of theaccount_recovery
table. We don't need additional layer of security for the selector, do we? Thanks!
– Andre Polykanine
May 28 '17 at 21:31
id:secret
is OK.selector:secret
is OK.secret
itself is not. The goal is to separate the database query (which is timing-leaky) from the authentication protocol (which should be constant time).
– Scott Arciszewski
May 29 '17 at 5:50
Is there any harm in usingopenssl_random_pseudo_bytes
insteadrandom_bytes
if is running PHP 5.6? Also, shouldn't you append just the selector and not the validator in the querystring of the link?
– greg
Jul 11 '18 at 14:09
When you verify the user-provided reset token, why do you use the binary representation of the random token? Do you think it will be possible (and secure?) to: 1) store in DB the hashed hex value of the token with
hash('sha256', bin2hex($token))
, 2) verify with if (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Thanks!– Guicara
Dec 18 '15 at 16:25
When you verify the user-provided reset token, why do you use the binary representation of the random token? Do you think it will be possible (and secure?) to: 1) store in DB the hashed hex value of the token with
hash('sha256', bin2hex($token))
, 2) verify with if (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Thanks!– Guicara
Dec 18 '15 at 16:25
Yes, comparing hex strings is secure too. It's really a matter of preference. I prefer to do all crypto operations on raw binary and only ever convert to hex/base64 for transmission or storage.
– Scott Arciszewski
Dec 18 '15 at 18:56
Yes, comparing hex strings is secure too. It's really a matter of preference. I prefer to do all crypto operations on raw binary and only ever convert to hex/base64 for transmission or storage.
– Scott Arciszewski
Dec 18 '15 at 18:56
Hi Scott, it is basically a question not only for your answer, but for the entire article about the "Remember Me" feature. Why not to use the unique
id
as the selector? I mean, the primary key of the account_recovery
table. We don't need additional layer of security for the selector, do we? Thanks!– Andre Polykanine
May 28 '17 at 21:31
Hi Scott, it is basically a question not only for your answer, but for the entire article about the "Remember Me" feature. Why not to use the unique
id
as the selector? I mean, the primary key of the account_recovery
table. We don't need additional layer of security for the selector, do we? Thanks!– Andre Polykanine
May 28 '17 at 21:31
id:secret
is OK. selector:secret
is OK. secret
itself is not. The goal is to separate the database query (which is timing-leaky) from the authentication protocol (which should be constant time).– Scott Arciszewski
May 29 '17 at 5:50
id:secret
is OK. selector:secret
is OK. secret
itself is not. The goal is to separate the database query (which is timing-leaky) from the authentication protocol (which should be constant time).– Scott Arciszewski
May 29 '17 at 5:50
Is there any harm in using
openssl_random_pseudo_bytes
instead random_bytes
if is running PHP 5.6? Also, shouldn't you append just the selector and not the validator in the querystring of the link?– greg
Jul 11 '18 at 14:09
Is there any harm in using
openssl_random_pseudo_bytes
instead random_bytes
if is running PHP 5.6? Also, shouldn't you append just the selector and not the validator in the querystring of the link?– greg
Jul 11 '18 at 14:09
|
show 1 more comment
You can also use DEV_RANDOM, where 128 = 1/2 the generated token length. Code below generates 256 token.
$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
3
I would suggestMCRYPT_DEV_URANDOM
overMCRYPT_DEV_RANDOM
.
– Scott Arciszewski
Jul 14 '15 at 22:45
add a comment |
You can also use DEV_RANDOM, where 128 = 1/2 the generated token length. Code below generates 256 token.
$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
3
I would suggestMCRYPT_DEV_URANDOM
overMCRYPT_DEV_RANDOM
.
– Scott Arciszewski
Jul 14 '15 at 22:45
add a comment |
You can also use DEV_RANDOM, where 128 = 1/2 the generated token length. Code below generates 256 token.
$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
You can also use DEV_RANDOM, where 128 = 1/2 the generated token length. Code below generates 256 token.
$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
edited Nov 21 '13 at 19:36
answered Nov 21 '13 at 18:13
Graham TGraham T
777513
777513
3
I would suggestMCRYPT_DEV_URANDOM
overMCRYPT_DEV_RANDOM
.
– Scott Arciszewski
Jul 14 '15 at 22:45
add a comment |
3
I would suggestMCRYPT_DEV_URANDOM
overMCRYPT_DEV_RANDOM
.
– Scott Arciszewski
Jul 14 '15 at 22:45
3
3
I would suggest
MCRYPT_DEV_URANDOM
over MCRYPT_DEV_RANDOM
.– Scott Arciszewski
Jul 14 '15 at 22:45
I would suggest
MCRYPT_DEV_URANDOM
over MCRYPT_DEV_RANDOM
.– Scott Arciszewski
Jul 14 '15 at 22:45
add a comment |
This may be helpful whenever you need a very very random token
<?php
echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>
add a comment |
This may be helpful whenever you need a very very random token
<?php
echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>
add a comment |
This may be helpful whenever you need a very very random token
<?php
echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>
This may be helpful whenever you need a very very random token
<?php
echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>
edited Feb 6 at 16:38
A-B-B
23.4k66369
23.4k66369
answered May 22 '17 at 11:22
Ir CalifIr Calif
14226
14226
add a comment |
add a comment |
You can use
echo str_shuffle('ASGDHFfdgfdre5475433fd');
Hahaha yes this!
– Jeff Puckett
Feb 10 '17 at 14:41
add a comment |
You can use
echo str_shuffle('ASGDHFfdgfdre5475433fd');
Hahaha yes this!
– Jeff Puckett
Feb 10 '17 at 14:41
add a comment |
You can use
echo str_shuffle('ASGDHFfdgfdre5475433fd');
You can use
echo str_shuffle('ASGDHFfdgfdre5475433fd');
edited Aug 8 '18 at 0:20
Pang
6,9091664102
6,9091664102
answered Oct 8 '15 at 22:47
saifsaif
111
111
Hahaha yes this!
– Jeff Puckett
Feb 10 '17 at 14:41
add a comment |
Hahaha yes this!
– Jeff Puckett
Feb 10 '17 at 14:41
Hahaha yes this!
– Jeff Puckett
Feb 10 '17 at 14:41
Hahaha yes this!
– Jeff Puckett
Feb 10 '17 at 14:41
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f18910814%2fbest-practice-to-generate-random-token-for-forgot-password%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
@AlmaDoMundo: A computer can't divide time unlimited.
– juergen d
Sep 20 '13 at 7:15
@juergend - sorry, do not get that.
– Alma Do
Sep 20 '13 at 7:15
You will get the same timestamp if you call it for instance a nano second apart. Some time functions for instance can only return time in 100ns steps, some only in seconds step.
– juergen d
Sep 20 '13 at 7:17
@juergend ah, that. Yes. I mentioned 'classic' timestamp with seconds only. But if act like you've said - yes (that only leaves us an option with time machine to get non-unique timestamp)
– Alma Do
Sep 20 '13 at 7:18
1
Head's up, the accepted answer does not leverage a CSPRNG.
– Scott Arciszewski
Jul 14 '15 at 22:47