Lengte van salt
Ik ben bezig met het maken van een installatie script voor mijn systeem.
Nu wil ik dat de gebruiker een salt ( secret_key ) invoerd voordat de installatie voldaan kan worden,
Dit ivm met het hashen voor wachtwoord en het encrypten en decrypten voor bepaalde onderdelen.
Maar nu wil ik weten wat een goede lengte zal zijn.
Heb zelf in me hoofd een lengte van 15 tot 20 characters maar kwam op stackoverflow en het topic van ward hier dat het zo lang mogelijk moet zijn zodat het uniek is.
Kan iemand mij uitleggen welke lengte echt optimaal is en waarom.
Begin AUB niet over het hashen zelf daar gebruik ik password_hash voor !
M.V.G,
Wouter.
Wouter Van Marrum op 01/11/2014 19:23:40:
Heb zelf in me hoofd een lengte van 15 tot 20 characters maar kwam op stackoverflow en het topic van ward hier dat het zo lang mogelijk moet zijn zodat het uniek is.
Correct. De salt moet "wereldwijd" uniek zijn, zodat de salt slechts dat ene wachtwoord in de ene database beveiligt.
Langere salts zijn dus inderdaad beter. Maar vergeet vooral ook de diepte niet: het maakt nogal uit of je alleen de karakterset A..Z gebruikt of daaraan ook a..z en 0..9 of de volledige UTF-8 karakterset toevoegt.
Ik gebruik nu deze code om de salt te laten genereren als men dit wil :
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+[{]};:'\"\|,<.>/?";
var returnField = field;
var secretLength = length;
for( var i=0; i < secretLength; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return $(returnField).val(text);
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+[{]};:'\"\|,<.>/?";
var returnField = field;
var secretLength = length;
for( var i=0; i < secretLength; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return $(returnField).val(text);
Als je verbeteringen weet hierop wil ik ze graag weten :)
Verder heb ik de default lengte op 255 gezet, zodat de salt in een CHAR(255) past.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<?php
namespace System\Security;
/**
* Salt
*
* @author Ward van der Put <[email protected]>
* @copyright Copyright (c) 2014 NewCo
* @license http://www.gnu.org/licenses/gpl.html GPLv3
* @version 0.1.0
*/
final class Salt
{
/**
* @param string VERSION
* Semantic version (SemVer)
*/
const VERSION = '0.1.0';
/**
* @param integer $Length
* String length of the salt (default 255).
*/
private $Length = 255;
/**
* @param string $Salt
* String containing the random salt.
*/
private $Salt;
/**
* Constructor
*
* @param integer $length
* The length of the desired salt string. This must be a positive
* integer. Defaults to 255 for a VARCHAR(255) or CHAR(255) column
* in MySQL.
*/
public function __construct($length = 255)
{
$this->setLength($length);
$this->randomize();
}
/**
* Return the salt object as a string.
*
* @param void
* @return string
*/
public function __toString()
{
return $this->get();
}
/**
* Get the salt string.
*
* @param void
* @return string
*/
public function get()
{
return $this->Salt;
}
/**
* Get the salt string length.
*
* @param void
* @return integer
*/
public function getLength()
{
return $this->Length;
}
/**
* Alias of randomize()
*
* @see randomize()
*/
public function randomise()
{
$this->randomize();
return $this;
}
/**
* Generate or regenerate a random salt.
*
* @param void
* @return $this
*/
public function randomize()
{
if (function_exists('openssl_random_pseudo_bytes')) {
$chars = openssl_random_pseudo_bytes($this->Length);
} elseif (function_exists('mcrypt_create_iv')) {
$chars = mcrypt_create_iv($this->Length, MCRYPT_DEV_URANDOM);
} else {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
}
$salt = (string)null;
$size = strlen($chars);
for ($i = 0; $i < $this->Length; $i++) {
$salt .= $chars[mt_rand(0, $size - 1)];
}
$this->Salt = $salt;
return $this;
}
/**
* Set the salt string length.
*
* @param integer $length
* The length of the salt string. This must be a positive integer.
* PHP will try to cast this parameter to a non-null integer to use it.
*
* @return $this
*/
public function setLength($length)
{
$this->Length = (int)$length;
return $this;
}
}
[/code]
namespace System\Security;
/**
* Salt
*
* @author Ward van der Put <[email protected]>
* @copyright Copyright (c) 2014 NewCo
* @license http://www.gnu.org/licenses/gpl.html GPLv3
* @version 0.1.0
*/
final class Salt
{
/**
* @param string VERSION
* Semantic version (SemVer)
*/
const VERSION = '0.1.0';
/**
* @param integer $Length
* String length of the salt (default 255).
*/
private $Length = 255;
/**
* @param string $Salt
* String containing the random salt.
*/
private $Salt;
/**
* Constructor
*
* @param integer $length
* The length of the desired salt string. This must be a positive
* integer. Defaults to 255 for a VARCHAR(255) or CHAR(255) column
* in MySQL.
*/
public function __construct($length = 255)
{
$this->setLength($length);
$this->randomize();
}
/**
* Return the salt object as a string.
*
* @param void
* @return string
*/
public function __toString()
{
return $this->get();
}
/**
* Get the salt string.
*
* @param void
* @return string
*/
public function get()
{
return $this->Salt;
}
/**
* Get the salt string length.
*
* @param void
* @return integer
*/
public function getLength()
{
return $this->Length;
}
/**
* Alias of randomize()
*
* @see randomize()
*/
public function randomise()
{
$this->randomize();
return $this;
}
/**
* Generate or regenerate a random salt.
*
* @param void
* @return $this
*/
public function randomize()
{
if (function_exists('openssl_random_pseudo_bytes')) {
$chars = openssl_random_pseudo_bytes($this->Length);
} elseif (function_exists('mcrypt_create_iv')) {
$chars = mcrypt_create_iv($this->Length, MCRYPT_DEV_URANDOM);
} else {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
}
$salt = (string)null;
$size = strlen($chars);
for ($i = 0; $i < $this->Length; $i++) {
$salt .= $chars[mt_rand(0, $size - 1)];
}
$this->Salt = $salt;
return $this;
}
/**
* Set the salt string length.
*
* @param integer $length
* The length of the salt string. This must be a positive integer.
* PHP will try to cast this parameter to a non-null integer to use it.
*
* @return $this
*/
public function setLength($length)
{
$this->Length = (int)$length;
return $this;
}
}
[/code]
Dankjewel voor je voorbeeld hoe jij het doet.
Nu snap ik natuurlijk hoe langer, hoe unieker je salt is maar mij lijkt 255 beetje overkill.
Natuurlijk ben ik geen beveiliging expert maar waarom adviseer jij dan 255 ?
En zou jij het alleen via php doen of maakt het voor jouw niks uit dat ik het hier bijvoorbeeld met javascript doe ?
Zou je kunnen toelichten waarom ?
Wouter Van Marrum op 01/11/2014 21:14:57:
Nu snap ik natuurlijk hoe langer, hoe unieker je salt is maar mij lijkt 255 beetje overkill.
Natuurlijk ben ik geen beveiliging expert maar waarom adviseer jij dan 255 ?
Natuurlijk ben ik geen beveiliging expert maar waarom adviseer jij dan 255 ?
Waarom denk je dat het overkill is?
Het maakt in performance niets uit, dus dan ga je voor de langste en sterkste salt die je "gratis" of "voor dezelfde prijs" kunt krijgen.
Wouter Van Marrum op 01/11/2014 21:14:57:
Natuurlijk ben ik geen beveiliging expert maar waarom adviseer jij dan 255 ?
Langer is beter.
Nog langer is nóg beter.
Ik gebruik de lengte van 255 karakters omdat dit in een CHAR(255) past van een MySQL-kolom.
Het kan nog veiliger door (a) daar een veel langere string van te maken en die (b) op te slaan in een andere tabel, die (c) liefst op een compleet andere server is opgeslagen. Met ander woorden: dan sla je het wachtwoord en de salt bij dat wachtwoord op in een compleet andere omgeving.
Gewijzigd op 01/11/2014 21:39:31 door Ward van der Put
Ik genereer vaak een system wide salt, ( dus statisch ).
Ook maakt het toch niet uit of je salt in de database of in een config bestand opslaat ?
Dacht ooit te hebben gelezen dat de salt in een config bestand mag staan en een eventuele pepper in de db.
Maar niet iedereen heeft toegang tot een extra mysql server waar je wachtwoord met salt opslaat.
Hoe zou je dit aanpakken dan ? ( als je perse een andere omgeving wilt hebben daarvoor ? )
Via een sqllite bestand ?
De hash daarvan is vereenvoudigd in pseudo-code:
In plaats van één systeem-salt voor alle gebruikers gebruik je dus een unieke salt voor elke individuele gebruiker. Die verschuiving van niveau, van systeem naar gebruiker, geeft een veel sterkere beveiliging.
Dus als ik het extreem makkelijk zou doen dan wordt het zoiets :
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
<?php
$random_salt = $iv = mcrypt_create_iv(255, MCRYPT_DEV_URANDOM);
$save_password = password_hash($password, PASSWORD_BCRYPT, ["cost"=> 10, "salt"=>$system_salt.$random_salt]);
// Nu heeft system_salt een lengte van 255 en random_salt ook.
?>
$random_salt = $iv = mcrypt_create_iv(255, MCRYPT_DEV_URANDOM);
$save_password = password_hash($password, PASSWORD_BCRYPT, ["cost"=> 10, "salt"=>$system_salt.$random_salt]);
// Nu heeft system_salt een lengte van 255 en random_salt ook.
?>
Of zou jij bijvoorbeeld een UUID gebruiken als random_salt ?
> Nog langer is nóg beter.
Niet mee eens. Het heeft geen nut om een salt te gebruiken die langer is dan de hash die je genereert. Dus als je een hash hebt van 256 bits, is elke salt van meer dan 32 bytes (256 bits) niet meer dan verspilde opslagruimte.
Voor de salt voor je hele systeem is er de volgende constructie: bcrypt(hmac(password))
The first rule of crypto club: don't roll your own!
Default algoritme voor password_hash() is Bcrypt (op tijd van schrijven) wat betekend dat van de password parameter maximaal 22 tekens gebruikt worden.
Ook betekend het dat van de salt maar 22 tekens (en van de laatste maar 4 van de 8 bits) gebruikt worden. En dan ook alleen maar tekens uit het volgende alfabet:
Wat jullie hier boven doen geeft dus een vals gevoel van veiligheid.
De salt wordt in de hash opgeslagen, daar is in de database dus geen appart veld voor. Ookal is een bcrypt hash 60 tekens, maak het veld een varchar(255) en laat password_hash() PASSWORD_DEFAULT gebruiken zodat als je ooit eens naar PHP 8 upgrade (er vanuit gaand dat PHP 8 een andere default heeft) je automatisch een sterker algoritme gebruikt voor het hashen.
Anyway, je komt dan uit op iets als dit:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$options = [];
$null_byte = chr(0);
// get bytes for the maximum entropy
$hmac = hash_hmac('sha512', $password, $system_salt, true);
if (PASSWORD_DEFAULT === PASSWORD_BCRYPT) {
// strip null bytes since bcrypt assumes zero terminated strings
//$hmac .= hash_mac('sha512', strrev($password), $system_salt, true); // for the paranoid that fear more then 56 null bytes
$hmac = strreplace($null_byte, '', $hmac);
$options['cost'] = 42; // DON'T PANIC! http://nl1.php.net/manual/en/function.password-hash.php#example-923
unset($options['salt']); // password_hash() can do this just fine
}
$hash = password_hash($hmac, PASSWORD_DEFAULT, $options);
?>
$options = [];
$null_byte = chr(0);
// get bytes for the maximum entropy
$hmac = hash_hmac('sha512', $password, $system_salt, true);
if (PASSWORD_DEFAULT === PASSWORD_BCRYPT) {
// strip null bytes since bcrypt assumes zero terminated strings
//$hmac .= hash_mac('sha512', strrev($password), $system_salt, true); // for the paranoid that fear more then 56 null bytes
$hmac = strreplace($null_byte, '', $hmac);
$options['cost'] = 42; // DON'T PANIC! http://nl1.php.net/manual/en/function.password-hash.php#example-923
unset($options['salt']); // password_hash() can do this just fine
}
$hash = password_hash($hmac, PASSWORD_DEFAULT, $options);
?>
Gewijzigd op 02/11/2014 13:31:02 door Dos Moonen
Dos Moonen op 01/11/2014 23:32:44:
The first rule of crypto club: don't roll your own!
Helemaal mee eens!
Alleen... ik heb het nergens over het opslaan van de hash van een wachtwoord gehad, hé? ;-)
Het gaat hier niet om een wachtwoord, niet om een hash, niet om het algoritme, maar primair om de salt.
Ik begrijp jullie overwegingen, en ben ook heel dankbaar voor de input, maar jullie zijn allemaal één stap verder dan de eigenlijke vraag: hoe genereer je een veilig salt (mijn vraag) en hoe sla je die op (Wouters vraag).
Was al benieuwd wanneer jij zou reageren. ( gezien het feit dat je altijd op deze topics reageert haha )
Prima dat dat password_hash zelf een salt aanmaakt natuurlijk, Maar volgens mij maak het weinig uit als je een eigen salt gebruikt toch ?
Ze geven niet voor niks de optie om deze zelf te kunnen aanpassen lijkt me.
Je stopt geen rotzooi in een functie die voor de beveiliging is, Dus waarom zou je dan geen eigen salt kunnen gebruiken icm password_hash ?
Ward van der Put op 01/11/2014 23:55:59:
Helemaal mee eens!
Alleen... ik heb het nergens over het opslaan van de hash van een wachtwoord gehad, hé? ;-)
Het gaat hier niet om een wachtwoord, niet om een hash, niet om het algoritme, maar primair om de salt.
Dos Moonen op 01/11/2014 23:32:44:
The first rule of crypto club: don't roll your own!
Helemaal mee eens!
Alleen... ik heb het nergens over het opslaan van de hash van een wachtwoord gehad, hé? ;-)
Het gaat hier niet om een wachtwoord, niet om een hash, niet om het algoritme, maar primair om de salt.
Mijn reactie was niet voor jou bedoeld. Jouw topic is een stuk algemener, binnen dit topic gaat het over het genereren van een salt voor het gebruik binnen password_hash(), waarop mijn antwoord blijft "doe niet zo moeilijk en laat password_hash() dit voor je doen".
Al krijg ik wel het idee dat je te veel waarde hecht aan salts. Passwords zijn secrets, salts niet. Salts zijn een implementation detail. De reden dat we de salts niet op profielen zetten is omdat er geen reden voor is.
Wouter Van Marrum op 02/11/2014 00:02:07:
Ze geven niet voor niks de optie om deze zelf te kunnen aanpassen lijkt me.
Ze geven niet voor niks de optie om deze zelf te kunnen aanpassen lijkt me.
Klopt, ze geven die optie zodat je er gebruik van kunt maken als je er een reden voor hebt. "Ik doe het omdat het kan" is in dit geval geen goede reden. Als je het over skydiven zou hebben zou het wat mij betreft een prima reden zijn. Maar wanneer je het over security hebt? NOPE!
Wouter Van Marrum op 02/11/2014 00:02:07:
Je stopt geen rotzooi in een functie die voor de beveiliging is, Dus waarom zou je dan geen eigen salt kunnen gebruiken icm password_hash ?
Je stopt geen rotzooi in een functie die voor de beveiliging is, Dus waarom zou je dan geen eigen salt kunnen gebruiken icm password_hash ?
Omdat je moet weten wat je doet. Wat betekend dat het makkelijker is om het aan password_hash() over te laten. Zelf dingen doen terwijl het niet nodig is doe je wanneer je iets wilt leren, en dat betekend dat je die code niet schrijft met het doel om het in productie te draaien.
Ik krijg nou niet echt het idee dat jij weet wat je moet doen. Dus ik zou die vraag kunnen beantwoorden met "omdat ik bang ben dat JIJ er rotzooi in stopt". En dat zou jammer zijn.
Mocht je willen weten welke random source PHP gebruikt voor het genereren van de salt:
http://lxr.php.net/xref/PHP_5_6/ext/standard/password.c#127
PS. ik ben schuldig aan het gebruiken van $system_salt op de plaats van de key bij hash_hmac(). Keys zijn in tegenstelling to salts wel geheimen. Zolang het geen public keys zijn dan...
Maar het gaat mij niet zozeer om een salt die ik in password_hash gebruik.
Dat was meer een voorbeeld om te geven met de vraag of het zo gedaan kon worden.
Mijn salt wil ik voor encryption en decrytion gebruiken indien dit nodig zou zijn.
Maar om terug te komen op mijn vraag, Hoe zou jij een random maken die wordt aangemaakt bij een installatie van een script ?
Dus hoelang, characters heb je al laten zien en of je het alleen in php of ook in js zou doen.
Tsja, wat zal ik hier nu eens op zeggen...
Is het voldoende als ik zeg dat een salt per definitie alleen gebruikt wordt bij hashing? ;-)
Wilt je elk geïnstalleerd exemplaar voorzien van een unieke ID, als een soort fingerprint?
Of wil je per geïnstalleerd exemplaar een unieke systeem-salt gebruiken?
Gewijzigd op 02/11/2014 18:38:03 door Ward van der Put
Maar met encryption en decryption gebruik je ook een unieke key.
Ben van plan om die op een bepaalde manier te genereren per gebruiker ( anders kun je alles decrypten. ) maar wel gekoppeld met de system_salt.
@ward, ik zou per geinstalleerd exemplaar een unieke systeem salt gebruiken.
Dus de gebruiker maakt zelf een systeem salt ( door zelf wat te verzinnen ) of gebruik te maken van me js functie die random iets genereert.
Maar nu je het over fingerprint hebt breng je me wel op een idee. ( morgen op werk eens over nadenken )
DO use:
The PHP source code at the bottom of this page.
OpenWall's Portable PHP password hashing framework
Any modern well-tested cryptographic hash algorithm, such as SHA256, SHA512, RipeMD, WHIRLPOOL, SHA3, etc.
Well-designed key stretching algorithms such as PBKDF2, bcrypt, and scrypt.
Secure versions of crypt ($2y$, $5$, $6$)
DO NOT use:
Outdated hash functions like MD5 or SHA1.
Insecure versions of crypt ($1$, $2$, $2x$, $3$).
Any algorithm that you designed yourself. Only use technology that is in the public domain and has been well-tested by experienced cryptographers.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<?php
/*
* Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
* Copyright (c) 2013, Taylor Hornby
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTE_SIZE", 24);
define("PBKDF2_HASH_BYTE_SIZE", 24);
define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);
function create_hash($password)
{
// format: algorithm:iterations:salt:hash
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM));
return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" . $salt . ":" .
base64_encode(pbkdf2(
PBKDF2_HASH_ALGORITHM,
$password,
$salt,
PBKDF2_ITERATIONS,
PBKDF2_HASH_BYTE_SIZE,
true
));
}
function validate_password($password, $correct_hash)
{
$params = explode(":", $correct_hash);
if(count($params) < HASH_SECTIONS)
return false;
$pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
return slow_equals(
$pbkdf2,
pbkdf2(
$params[HASH_ALGORITHM_INDEX],
$password,
$params[HASH_SALT_INDEX],
(int)$params[HASH_ITERATION_INDEX],
strlen($pbkdf2),
true
)
);
}
// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
$diff = strlen($a) ^ strlen($b);
for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
{
$diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
}
/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
*
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*/
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
if($count <= 0 || $key_length <= 0)
trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
if (function_exists("hash_pbkdf2")) {
// The output length is in NIBBLES (4-bits) if $raw_output is false!
if (!$raw_output) {
$key_length = $key_length * 2;
}
return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
}
?>
/*
* Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
* Copyright (c) 2013, Taylor Hornby
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTE_SIZE", 24);
define("PBKDF2_HASH_BYTE_SIZE", 24);
define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);
function create_hash($password)
{
// format: algorithm:iterations:salt:hash
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM));
return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" . $salt . ":" .
base64_encode(pbkdf2(
PBKDF2_HASH_ALGORITHM,
$password,
$salt,
PBKDF2_ITERATIONS,
PBKDF2_HASH_BYTE_SIZE,
true
));
}
function validate_password($password, $correct_hash)
{
$params = explode(":", $correct_hash);
if(count($params) < HASH_SECTIONS)
return false;
$pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
return slow_equals(
$pbkdf2,
pbkdf2(
$params[HASH_ALGORITHM_INDEX],
$password,
$params[HASH_SALT_INDEX],
(int)$params[HASH_ITERATION_INDEX],
strlen($pbkdf2),
true
)
);
}
// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
$diff = strlen($a) ^ strlen($b);
for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
{
$diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
}
/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
*
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*/
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
if($count <= 0 || $key_length <= 0)
trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
if (function_exists("hash_pbkdf2")) {
// The output length is in NIBBLES (4-bits) if $raw_output is false!
if (!$raw_output) {
$key_length = $key_length * 2;
}
return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
}
?>
Gewijzigd op 04/11/2014 08:31:41 door Stefan Fransen