| | |
Password encoding/decoding
Please support our PHP advertiser: PostgreSQL or MySQL? Compare and contrast the two most popular open source databases
![]() |
•
•
Join Date: Oct 2009
Posts: 100
Reputation:
Solved Threads: 18
0
#11 26 Days Ago
I think she recommended high memory usage so that the effort needed to try to recreate/hack any of the passwords would be excessive and not worth it. But if you use up that much processing/memory, wouldn't you make it untenable to be used within a login/registering system? For even a reasonable amount of requests even.
0
#12 26 Days Ago
•
•
•
•
@digital-ether
I agree with you 100%, although 100.000 iterations seem a bit excessive to me. (But that's just me :-P)
However, I got to ask why you specifically mention high memory usage?
100.000 iterations of SHA1 for instance only takes 300ms on a Core2 Duo 2.83Ghz.
On an production (under heavy use) 8 core Xeon 2.27 Ghz it took 500 ms.
So it looks like 100.000 is too high. (I just threw it in there). Maybe around 10.000 iterations. (around 50 ms to do).
Usually, the logins are not a bottleneck in your application.
See: http://chargen.matasano.com/chargen/...w-about-s.html
under "adaptive hashing"
I can't find any implementations of this in PHP. Though what is described in that article (BCrypt) uses Blowfish which PHP 5.3 has a native implementation. http://php.net/manual/en/function.crypt.php
So you could do something equivalent.
•
•
•
•
I think she recommended high memory usage so that the effort needed to try to recreate/hack any of the passwords would be excessive and not worth it. But if you use up that much processing/memory, wouldn't you make it untenable to be used within a login/registering system? For even a reasonable amount of requests even.
www.fijiwebdesign.com - web design and development and fun
Cpanel Email - Let users Register email accounts on your website upon registration
Ajax Chat - Fully browser based chat!
Cpanel Email - Let users Register email accounts on your website upon registration
Ajax Chat - Fully browser based chat!
1
#13 26 Days Ago
Thought I'd write an example:
note: for PHP4 just remove the "public static" in front of each function declaration.
The class:
Example using SHA1 with default of 10000 iterations.
Example using whirlpool as the hash function, a 128 length salt as well as a secret.
note: for PHP4 just remove the "public static" in front of each function declaration.
The class:
php Syntax (Toggle Plain Text)
/** * Generate cryptographic Hashes for passwords * * Features: * Harderned against precomputation attacks like rainbow tables (using salt) * Harderned against brute force and dictionary attacks (using key stretching, large memory usage and an optional secret key) * * http://en.wikipedia.org/wiki/Password_cracking * * Note: for PHP4 and lower, just remove the "public static" before function declaration * * @author gabe@fijiwebdesign.com * @link http://www.fijiwebdesign.com/ * @version $Id$ */ class Password_Hash { /** * Generate the Hash * @return String * @param $password String * @param $salt String[optional] * @param $iterations Int[optional] * @param $secret String[optional] */ public static function generate($password, $salt = null, $iterations = 10000, $hash_function = 'sha1', $secret = '') { $salt or $salt = self::generateToken(); $hashes = array(); $hash = $password; // stores a sequence of 10000 unique hashes in memory $i = $iterations; while(--$i) { $hash = $hashes[] = $hash_function($hash.$salt.$secret); } // hash the 10000 unique hashes into a single hash $hash = $hash_function(implode('', $hashes).$salt.$secret); return implode(':', array($hash, $iterations, $hash_function, $salt)); } /** * Verify a password meets a hash * @return Bool * @param $password String * @param $hash String * @param $secret String[optional] */ public static function verify($password, $hash, $secret = '') { list($_hash, $iterations, $hash_function, $salt) = explode(':', $hash); return ($hash == self::generate($password, $salt, $iterations, $hash_function, $secret)); } /** * Generate a random hex based token * @return String * @param $length Int[optional] */ public static function generateToken($length = 40) { $token = array(); for( $i = 0; $i < $length; ++$i ) { $token[] = dechex( mt_rand(0, 15) ); } return implode('', $token); } }
Example using SHA1 with default of 10000 iterations.
php Syntax (Toggle Plain Text)
// generating the hash $password = 'test'; $hash = Password_Hash::generate($password); // verifying a password $result = Password_Hash::verify($password, $hash); // dump results var_dump($hash, $result);
Example using whirlpool as the hash function, a 128 length salt as well as a secret.
php Syntax (Toggle Plain Text)
// define our custom hash function function whirlpool($str) { return hash('whirlpool', $str); } $password = 'test'; $salt = password_Hash::generateToken(128); $secret = password_Hash::generateToken(128); $iterations = 10000; // generate hash $hash = Password_Hash::generate($password, $salt, $iterations, 'whirlpool', $secret); // verify $result = Password_Hash::verify($password, $hash, $secret); // dump results var_dump($result);
Last edited by digital-ether; 25 Days Ago at 1:46 pm.
www.fijiwebdesign.com - web design and development and fun
Cpanel Email - Let users Register email accounts on your website upon registration
Ajax Chat - Fully browser based chat!
Cpanel Email - Let users Register email accounts on your website upon registration
Ajax Chat - Fully browser based chat!
0
#14 26 Days Ago
Thanks for that.
I see what you mean now, about the memory.
Interesting article to. I had never really considered using bcrypt before, although after doing some testing, and considering your memory point, I'm inclined to keep using the hashing algorithms.
Your code looks pretty awesome to. Very flexible :-)
Looks like it could easily starve you for memory if you aren't careful. (Which is what you were aiming for, I assume.)
I'm trying out a system where I store details on the hash inside the database, everything from which algorithm to use to how much RAM it should be using. I use a similar method you used, to make sure the RAM suffers during the process.
Been working fine so far:
I use my own database wrapper classes in there, but I trust you can read around them ;-)
Using the following, validating a single password uses more than 10Mb memory and takes just under a second.
(Using Core Duo 3.16GHz CPU and 1333Mhz DDR3 RAM)
Looks promising... I think :-)
I see what you mean now, about the memory.
Interesting article to. I had never really considered using bcrypt before, although after doing some testing, and considering your memory point, I'm inclined to keep using the hashing algorithms.
Your code looks pretty awesome to. Very flexible :-)
Looks like it could easily starve you for memory if you aren't careful. (Which is what you were aiming for, I assume.)
I'm trying out a system where I store details on the hash inside the database, everything from which algorithm to use to how much RAM it should be using. I use a similar method you used, to make sure the RAM suffers during the process.
Been working fine so far:
php Syntax (Toggle Plain Text)
/** * Handles member security. * * Assumes a DB table named `member` structured like so: * CREATE TABLE `member` ( * `id` int Unsigned NOT NULL Auto_Increment Primary Key, * `name` VarChar(64) Not Null Unique, * `pw_salt` char(32) NOT NULL, * `pw_memory` int(10) unsigned DEFAULT 10485760, * `pw_algo` varchar(32) DEFAULT 'sha512', * `password` char(128) NOT NULL * ) ENGINE=InnoDB DEFAULT CHARSET=utf8; * * Static Methods: * generate_hash - Generates a has based on the input and the user's pw_ details. * validate_password - Validates the given password against the member password. * */ class MemberSecurity { /** * Generates a hash based on the user-specific salt and the passed data. * @param string $input The keyword (password) to hash. * @param uint $userID The ID of the user, who's salt will be used. * @return string The hash. Variable length, based on the algorithm used. * @throws Exception */ public static function generate_hash($input, $userID) { try { // Fetch and initialize data. $sql = "SELECT `pw_salt`, `pw_memory`, `pw_algo` FROM `member` WHERE `id` = {$userID}"; $resultSet = Database::Get()->query($sql); $resultRow = $resultSet->fetch_assoc(); $salt = $resultRow['pw_salt']; $algo = $resultRow['pw_algo']; $memorySize = $resultRow['pw_memory']; $hash = $input . $salt; // In case of a normal hash algorithm. if(in_array($algo, hash_algos())) { // Set up a memory buffer and fill it with hashes created using // the input and the salt, then hash the whole thing as the output. // Should eat up loads of memory and CPU time. $buffer = ""; while(strlen($buffer) < $memorySize) { $buffer .= hash($algo, $input . $salt); } $hash = hash($algo, $buffer); $buffer = null; } // Fall back to bcrypt, if possible. // No memory limit. (The memory field will be used as the key length) // This eats up much more CPU time than memory. else if($algo == 'bcrypt' && CRYPT_BLOWFISH == 1) { $keyLength = ($memorySize < 10 ? "0".$memorySize : $memorySize); $hash = substr(crypt($input, '$2a$'. $keyLength .'$'. $salt . '$'), 28); } // Algorithm not available. Bail out! else { throw new Exception("The hashing algorithm '$algo' is not available.", 501); } return $hash; } catch(DatabaseException $ex) { throw new Exception("Failed to fetch user data.", 304, $ex); } } /** * Validates the password against the one stored in the user database. * @param string $password The password to validate. * @param string $userName The name of the member. * @return bool * @throws Exception */ public static function validate_password($password, $userName) { try { $sql = "SELECT `id`, `password` FROM `member` WHERE `name` = '{$userName}'"; $resultSet = Database::Get()->query($sql); if($resultSet && $resultSet->num_rows == 1) { $resultRow = $resultSet->fetch_assoc(); $generatedHash = self::generate_hash($password, $resultRow['id']); return ($resultRow['password'] == $generatedHash); } else { throw new DatabaseException("Failed to fetch user data.", 500, $sql, Database::Get()->error); return false; } } catch(DatabaseException $ex) { throw new Exception("Failed to fetch user data.", 500, $ex); } } }
Using the following, validating a single password uses more than 10Mb memory and takes just under a second.
(Using Core Duo 3.16GHz CPU and 1333Mhz DDR3 RAM)
text Syntax (Toggle Plain Text)
mysql> SELECT * FROM member\G *************************** 1. row *************************** id: 1 name: User 1 pw_salt: 0e20d1dc3c6e60225597be pw_memory: 10485760 pw_algo: sha512 password: 09254dc706eb1acb5420adcbc97d67a0992fb3530e8c844c4c3a930614d447f371f2f0a2af369dd1c73452819d3aac30bdeea7f889b11f4cf038435c26d5036e
Looks promising... I think :-)
Please do not ask for help in a PM. Use the forums.
And use [code] tags!
And use [code] tags!
1
#15 25 Days Ago
Looks great. I modified the class I wrote a bit after looking at your code.
Just a few suggestions:
You could optimize the string concatenation.
A lot of the execution time is the concatenation of the $buffer string.
If you use an array however, it becomes negligible. I believe the reason is the whole string has to be copied/destroyed in memory each time you append to it. Arrays are mutable so appending would not create a copy.
You could calculate the number of iterations you'll need by dividing the memory usage (in bytes) by the length of the hash. That way you also don't have to re-check the length of the string.
Just a few suggestions:
You could optimize the string concatenation.
php Syntax (Toggle Plain Text)
$buffer .= hash($algo, $input . $salt);
A lot of the execution time is the concatenation of the $buffer string.
If you use an array however, it becomes negligible. I believe the reason is the whole string has to be copied/destroyed in memory each time you append to it. Arrays are mutable so appending would not create a copy.
You could calculate the number of iterations you'll need by dividing the memory usage (in bytes) by the length of the hash. That way you also don't have to re-check the length of the string.
www.fijiwebdesign.com - web design and development and fun
Cpanel Email - Let users Register email accounts on your website upon registration
Ajax Chat - Fully browser based chat!
Cpanel Email - Let users Register email accounts on your website upon registration
Ajax Chat - Fully browser based chat!
0
#16 25 Days Ago
Thanks.
I did use an array at first, adding each hash as an element and them imploding it before creating the final hash. (Like you do in your code.)
However, after testing that I found that this method uses double the amount of memory the string concatenation method uses.
Makes sense when you think about it. Both the array and the string will have to exist in memory at the same time when the implode function is called.
After testing this a bit more, I've decided to just use half the array size. The array takes up half the memory, and the string the implode creates uses the rest. Seems to be most efficient.
This takes only half the time my previous code did :-)
This also fixed another problem in my previous code.
The loop that created the memory buffer in my previous code just hashed and added the same hash over and over. It could have been optimized to practically nothing by hashing it once outside the loop and adding that each loop, rather than recreating the hash each time.
By adding the loop index to the string that gets hashed, I now get a unique hash added to the buffer each loop.
Good point, thanks.
•
•
•
•
You could optimize the string concatenation.
However, after testing that I found that this method uses double the amount of memory the string concatenation method uses.
Makes sense when you think about it. Both the array and the string will have to exist in memory at the same time when the implode function is called.
After testing this a bit more, I've decided to just use half the array size. The array takes up half the memory, and the string the implode creates uses the rest. Seems to be most efficient.
php Syntax (Toggle Plain Text)
$buffer = array(); $hash = hash($algo, $input . $salt); for($y = floor($memorySize / strlen($hash) / 2); $y > 0; --$y) { $buffer[] = hash($algo, $y . $hash . $salt); } $hash = hash($algo, implode($buffer));
This also fixed another problem in my previous code.
The loop that created the memory buffer in my previous code just hashed and added the same hash over and over. It could have been optimized to practically nothing by hashing it once outside the loop and adding that each loop, rather than recreating the hash each time.
By adding the loop index to the string that gets hashed, I now get a unique hash added to the buffer each loop.
•
•
•
•
You could calculate the number of iterations you'll need by dividing the memory usage (in bytes) by the length of the hash. That way you also don't have to re-check the length of the string.
Last edited by Atli; 25 Days Ago at 6:07 am. Reason: Fixed. Sorry XD
Please do not ask for help in a PM. Use the forums.
And use [code] tags!
And use [code] tags!
0
#17 24 Days Ago
•
•
•
•
Thanks.
I did use an array at first, adding each hash as an element and them imploding it before creating the final hash. (Like you do in your code.)
However, after testing that I found that this method uses double the amount of memory the string concatenation method uses.
Makes sense when you think about it. Both the array and the string will have to exist in memory at the same time when the implode function is called.
After testing this a bit more, I've decided to just use half the array size. The array takes up half the memory, and the string the implode creates uses the rest. Seems to be most efficient.
This takes only half the time my previous code did :-)php Syntax (Toggle Plain Text)
$buffer = array(); $hash = hash($algo, $input . $salt); for($y = floor($memorySize / strlen($hash) / 2); $y > 0; --$y) { $buffer[] = hash($algo, $y . $hash . $salt); } $hash = hash($algo, implode($buffer));
This also fixed another problem in my previous code.
The loop that created the memory buffer in my previous code just hashed and added the same hash over and over. It could have been optimized to practically nothing by hashing it once outside the loop and adding that each loop, rather than recreating the hash each time.
By adding the loop index to the string that gets hashed, I now get a unique hash added to the buffer each loop.
Good point, thanks.
But it appears the hash is generated by reading the input in sequence. That way it does not require storing the whole input in memory.
A bit of searching online seemed to prove it: http://www.partow.net/programming/ha...gMethodologies
And a test case:
php Syntax (Toggle Plain Text)
// generate a hash for 2MB file $hash = sha1_file('hash.txt'); echo 'memory peak use: '.floor(memory_get_peak_usage(true)/1000).' Kb';
The result was only 524 Kb memory allocated for the PHP process even though the file was 2MB which shows that the file was read sequentially.
For example with PHP, sha1_file() supports the stream wrappers. So you could just write each hash generated to a stream/pipe that sha1_file() would read as the input. A unidirectional stream is not kept in memory as a whole, only each packet is read into memory and then dropped.
So I guess the memory consumption bit has to be rewritten some other way.
www.fijiwebdesign.com - web design and development and fun
Cpanel Email - Let users Register email accounts on your website upon registration
Ajax Chat - Fully browser based chat!
Cpanel Email - Let users Register email accounts on your website upon registration
Ajax Chat - Fully browser based chat!
0
#18 24 Days Ago
But neither of our codes need the hash functions themselves to use a lot of memory. They both use arrays and strings we created to fill the memory.
I mean, using my last code, setting the
Or am I not getting your point?
P.S.
1Kb = 1024b ;-)
I mean, using my last code, setting the
$memorySize variable to 10Mb, the peak usage of the script goes to around 10.5Mb.Or am I not getting your point?
P.S.
floor(memory_get_peak_usage(true)/1000) 1Kb = 1024b ;-)
Please do not ask for help in a PM. Use the forums.
And use [code] tags!
And use [code] tags!
0
#19 24 Days Ago
•
•
•
•
But neither of our codes need the hash functions themselves to use a lot of memory. They both use arrays and strings we created to fill the memory.
I mean, using my last code, setting the$memorySizevariable to 10Mb, the peak usage of the script goes to around 10.5Mb.
Or am I not getting your point?
P.S.
floor(memory_get_peak_usage(true)/1000)
1Kb = 1024b ;-)
Example:
php Syntax (Toggle Plain Text)
// sha1_stream.php echo sha1_file('php://stdin');
Saved as file sha1_stream.php the above would read the input from STDIN and write the hash to STDOUT.
Then I used proc_open() to write the input to it bit by bit. So it never stays in memory.
PHP Syntax (Toggle Plain Text)
$salt = 'e5cbe45c71a0b805278f2b5f94eb108dba532af3'; $hash = 'test'; $descriptorspec = array( 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 1 => array("pipe", "w"), // stdout is a pipe that the child will write to 2 => array("file", "error-output.txt", "a") // stderr is a file to write to ); $cwd = './'; $env = array(); $options = array('bypass_shell'); $php_path = 'c:\xampp\php\php.exe'; // change to php path $file_path = dirname(__FILE__).'/sha1_stream.php'; $process = proc_open("$php_path $file_path", $descriptorspec, $pipes, $cwd, $env, $options); if (is_resource($process)) { $i = 1000; while(--$i) { // instead of saving the hashes in memory, we write each hash to the other PHP process which computes the overall hash for the whole string. fwrite($pipes[0], sha1($hash.$salt)); } fclose($pipes[0]); $hash = stream_get_contents($pipes[1]); fclose($pipes[1]); $return_value = proc_close($process); } else { throw new Exception('Could not create process. see: error-output.txt'); } echo $hash; echo '<br />';
With 100.000 iterations the full process still takes only about 1.12secs and memory usage of both processes was around 700Kb.
www.fijiwebdesign.com - web design and development and fun
Cpanel Email - Let users Register email accounts on your website upon registration
Ajax Chat - Fully browser based chat!
Cpanel Email - Let users Register email accounts on your website upon registration
Ajax Chat - Fully browser based chat!
![]() |
Similar Threads
- Qestion on Encoding and Decoding. (Python)
- Slow computer + about:blank homepage (Viruses, Spyware and other Nasties)
- Encoding/Decoding (C)
- homepage hijack "Search for..." about:blank in address (Viruses, Spyware and other Nasties)
- Trojan Problem (Viruses, Spyware and other Nasties)
- Hijacked Repeatedly "about:blank" - Please Help (Viruses, Spyware and other Nasties)
- Need review of HJT log (Viruses, Spyware and other Nasties)
- Browser Hijack (about:blank) (Viruses, Spyware and other Nasties)
- my HJT log, 2 of them for 2 comp (Viruses, Spyware and other Nasties)
Other Threads in the PHP Forum
- Previous Thread: To restrict users to choose date from date picker only
- Next Thread: Messaging System
| Thread Tools | Search this Thread |
advanced apache api array beginner binary broken cakephp check checkbox class cms code cookies cron curl database date datepart display dropdownlist dynamic echo email eregi error execution file files folder form forms function functions google head href htaccess html if...loop image include includingmysecondfileinthechain insert ip javascript job joomla jquery key library limit link login mail menu mlm multiple mysql oop password paypal pdf pdfdownload php phpvotingscript problem query radio random recursion remote screen script search server sessions smarty sms sorting source space sql startup stored syntax system table traffic tutorial unicode update upload url validator variable video web youtube zend






