I was looking for an effective algorithm that can give me an accurate idea of how strong a password is.
I found that several different websites use several different algorithms as I get different password strength ratings on different websites.
This has grown to my general brain dump of best practices for working with passwords in PHP/MySQL.
The ideas presented here are generally not my own, but the best of what I've found to date.
Ensure you are using SSL for all operations involving user information. All pages that involve these forms should check they are being called via HTTPS, and refuse to work otherwise.
You can eliminate most attacks by simply limiting the number of failed logins allowed.
Allow for relatively weak passwords, but store the number of failed logins per user and require a captcha or password verification by email if you exceed it. I set my max failures to 5.
Presenting login failures to the user needs to be carefully thought out as to not provide information to attackers.
A failed login due to a non existent user should return the same message as a failed login due to a bad password. Providing a different message will allow attackers to determine valid user logins.
Also make sure you return exactly the same message in the event of a failure for too many logins with a valid password, and a failure with too many logins and a bad password. Providing a different message will allow attackers to determine valid user passwords. A fair number of users when forced to reset their password will simply put it back to what it was.
Unfortunately limiting the number of logins allowed per IP address is not practical. Several providers such as AOL and most companies proxy their web requests. Imposing this limit will effectively eliminate these users.
I've found checking for dictionary words before submit to be inefficient as either you have to send a dictionary to the client in javascript, or send an ajax request per field change. I did this for a while and it worked ok, but didn't like the traffic it generated.
Checking for inherently weak passwords minus dictionary words IS practical client side with some simple javascript.
After submit, I check for dictionary words, and username containing password and vice versa server side. Very good dictionaries are readily downloadable and the testing against them is simple. One gotcha here is that to test for a dictionary word, you need to send a query against the database, which again contains the password. The way I got around this was to encrypt my dictionary before hand with a simple encryption and end positioned SALT and then test for the encrypted password. Not ideal, but better than plain text and only on the wire for people on your physical machines and subnet.
Once you are happy with the password they have picked encrypt it with PHP first, then store. The following password encryption function is not my idea either, but solves a number of problems. Encrypting within PHP prevents people on a shared server from intercepting your unencrypted passwords. Adding something per user that won't change (I use email as this is the username for my sites) and add a hash (SALT is a short constant string I change per site) increases resistance to attacks. Because the SALT is located within the password, and the password can be any length, it becomes almost impossible to attack this with a rainbow table.
Alternately it also means that people can't change their email and you can't change the SALT without invalidating everyone's password though.
EDIT: I would now recommend using PhPass instead of my roll your own function here, or just forget user logins altogether and use OpenID instead.
function password_crypt($email,$toHash) {
$password = str_split($toHash,(strlen($toHash)/2)+1);
return hash('sha256', $email.$password[0].SALT.$password[1]);
}
My Jqueryish client side password meter. Target should be a div. It's width will change between 0 and 100 and background color will change based on the classes denoted in the script. Again mostly stolen from other things I've found:
$.updatePasswordMeter = function(password,username,target) {
$.updatePasswordMeter._checkRepetition = function(pLen,str) {
res = ""
for ( i=0; i<str.length ; i++ ) {
repeated=true;
for (j=0;j < pLen && (j+i+pLen) < str.length;j++)
repeated=repeated && (str.charAt(j+i)==str.charAt(j+i+pLen));
if (j<pLen) repeated=false;
if (repeated) {
i+=pLen-1;
repeated=false;
}
else {
res+=str.charAt(i);
};
};
return res;
};
var score = 0;
var r_class = 'weak-password';
//password < 4
if (password.length < 4 || password.toLowerCase()==username.toLowerCase()) {
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
return true;
}
//password length
score += password.length * 4;
score += ( $.updatePasswordMeter._checkRepetition(1,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(2,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(3,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(4,password).length - password.length ) * 1;
//password has 3 numbers
if (password.match(/(.*[0-9].*[0-9].*[0-9])/)) score += 5;
//password has 2 symbols
if (password.match(/(.*[!,#,#,$,%,^,&,*,?,_,~].*[!,#,#,$,%,^,&,*,?,_,~])/)) score += 5;
//password has Upper and Lower chars
if (password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)) score += 10;
//password has number and chars
if (password.match(/([a-zA-Z])/) && password.match(/([0-9])/)) score += 15;
//
//password has number and symbol
if (password.match(/([!,#,#,$,%,^,&,*,?,_,~])/) && password.match(/([0-9])/)) score += 15;
//password has char and symbol
if (password.match(/([!,#,#,$,%,^,&,*,?,_,~])/) && password.match(/([a-zA-Z])/)) score += 15;
//password is just a nubers or chars
if (password.match(/^\w+$/) || password.match(/^\d+$/) ) score -= 10;
//verifing 0 < score < 100
score = score * 2;
if ( score < 0 ) score = 0;
if ( score > 100 ) score = 100;
if (score > 25 ) r_class = 'okay-password';
if (score > 50 ) r_class = 'good-password';
if (score > 75 ) r_class = 'strong-password';
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
return true;
};
Fundamentally you want to prevent to major types of attacks
Dictionary attacks
Brute force attacks
To prevent the first, you want to consider passwords containing common words weak. To prevent the second, you want to encourage passwords of reasonable length (8+ characters is common) and with a reasonably large character set (include letters, numbers, and special characters). If you consider lower case and upper case letters to be different, that increases the character set substantially. However, this creates a usability issue for some user communities so you need to balance that consideration.
A quick google search turned up solutions that account for brute force attacks (complex password) but not for dictionary attacks. PHP Password Strength Meter from this list of strength checkers runs the check server-side, so it could be extended to check a dictionary.
EDIT:
By the way... you should also limit the number of login attempts per user. This will make both types of attacks less likely. Effective but not-user-friendly is to lock an account after X bad attempts and require a password reset. More user friendly but more effort is to throttle time between login attempts. You can also require CAPTCHA after the first few login attempts (which is something that Stack Overflow requires after too many edits, or for very new users).
Basically you probably want to use Regular Expressions to validate the length and complexity of the password.
A good example doing this using javascript can be found here:
http://marketingtechblog.com/programming/javascript-password-strength/
As Daren Schwenke pointed it out, you'd better work on the security yourself and not put this in the user hands.
But it's good to provide some hints to the user of how strong his password is, because the best way to get a password is still social engenering.
So you can hack a little client side script that checks the user password strenght as a courtesy indicator, in real time. It blocks nothing, but gives him a good warm feeling when it turns green :-)
Basically what you must check is commom sense : check if the password contains letters, numbers and non alphabetical caracters, in a reasonable quantity.
You can hack your own algo very easily : just make 10 / 10 mark :
0 is a zero lenght password;
+2 for every 8 caracters in the password (15 is supposed to be a safe lenght);
+1 for the use of a letter, +2 for the use of 2 letters;
+1 for the use of a number, +2 for the use of 2 numbers;
+1 for the use of a non alphabetical caracters, +2 for 2.
You don't need to check for godlike passwords (are there capitalized letters, where are positioned the special caracters, etc), your users are not in the bank / military / secret service / monthy python movies industry, are they ?
You can code that in an hour in without crazy javascript skills.
And anyway, valid the password and move all the security code on the server side. If you can delegate authentification (e.g : open ID), even better.
Don't Roll-Your-Own!
Cryptography experts discourage roll-your-own cryptography for reasons that should be obvious.
For the very same reasons, one should not attempt to roll his own solution to the problem of measuring a password's strength; it is very much a cryptographic problem.
Don't get into the ugly business of authoring some massive regular expression for this purpose; you will likely fail to account for several factors that influence a password's overall strength.
It's a Difficult Problem
There is considerable difficulty inherent to the problem of measuring a password's strength. The more research I perform on this subject, the more I realize that this is a "unidirectional" problem; that is, one cannot measure the "difficulty" (computational cost) of cracking a password efficiently. Rather, it is more efficient to provide complexity requirements and measure the password's ability to meet them.
When we consider the problem logically, a "crackability index" doesn't make much sense, as convenient as it sounds. There are so many factors that drive the calculation, most of which relate to the computational resources devoted to the cracking process, so as to be impractical.
Imagine pitting John the Ripper (or a similar tool) against the password in question; it might take days to crack a decent password, months to crack a good password, and until the sun burns-out to crack an exceptional password. This is not a practical means by which to measure password strength.
Approaching the problem from the other direction is far more manageable: if we supply a set of complexity requirements, it's possible to judge the relative strength of a password very quickly. Obviously, the supplied complexity requirements must evolve over time, but there are far fewer variables for which to account if we approach the problem in this way.
A Viable Solution
There is a standalone utility available from Openwall entitled passwdqc (presumably, standing for Password Quality Checker). Openwall developer, Solar Designer, does appear to be a bona fide cryptography expert (his works speak for themselves), and so is qualified to author such a tool.
For my particular use-case, this is a far more attractive solution than using an ill-conceived JavaScript snippet living in some dark corner of the Web.
Establishing parameters for your particular needs is the hardest part. The implementation is the easy part.
A Practical Example
I offer a simple implementation in PHP to provide a jump-start. Standard disclaimers apply.
This example assumes that we're feeding an entire list of passwords to the PHP script. It goes without saying that if you are doing this with real passwords (e.g., those dumped out of a password manager), extreme caution should be exercised with regard to password-handling. Simply writing the unencrypted password dump to disk jeopardizes the security of your passwords!
passwords.csv:
"Title","Password"
"My Test Password","password123"
"Your Test Password","123456!!!"
"A strong password","NFYbCoHC5S7dngitqCD53tvQkAu3dais"
password-check.php:
<?php
//A few handy examples from other users:
//http://php.net/manual/en/function.str-getcsv.php#117692
$csv = array_map('str_getcsv', file('passwords.csv'), [',']);
array_walk($csv, function(&$a) use ($csv) {
$a = array_combine($csv[0], $a);
});
//Remove column header.
array_shift($csv);
//Define report column headers.
$results[] = [
'Title',
'Result',
'Exit Code',
];
$i = 1;
foreach ($csv as $p) {
$row['title'] = $p['Title'];
//If the value contains a space, it's considered a passphrase.
$isPassphrase = stristr($p['Password'], ' ') !== false ? true : false;
$cmd = 'echo ' . escapeshellarg($p['Password']) . ' | pwqcheck -1 min=32,24,22,20,16 max=128';
if ($isPassphrase) {
$cmd .= ' passphrase=3';
}
else {
$cmd .= ' passphrase=0';
}
$output = null;
$exitCode = null;
$stdOut = exec($cmd, $output, $exitCode);
//Exit code 0 represents an unacceptable password (not an error).
//Exit code 1 represents an acceptable password (it meets the criteria).
if ($exitCode === 0 || $exitCode === 1) {
$row['result'] = trim($stdOut);
$row['exitCode'] = $exitCode;
}
else {
$row['result'] = 'An error occurred while calling pwqcheck';
$row['exitCode'] = null;
}
$results[$i] = $row;
$i++;
}
$reportFile = 'report.csv';
$fp = #fopen($reportFile, 'w');
if ($fp !== false) {
foreach ($results as $p) {
fputcsv($fp, $p);
}
fclose($fp);
}
else {
die($reportFile . ' could not be opened for writing (destination is not writable or file is in use)');
}
exit;
Resultant report.csv:
Title,Result,"Exit Code"
"My Test Password","Bad passphrase (too short)",1
"Your Test Password","Bad passphrase (too short)",1
"A strong password",OK,0
Wrapping-Up
I have yet to find a more thorough solution on the Web; needless to say, I welcome any other recommendations.
Obviously, this approach is not ideal for certain use-cases (e.g., a "password strength meter" implemented "client-side"). Even so, it would be trivial to make an AJAX call to a server-side resource that returns a pass/fail response using the approach outlined above, but such an approach should assume the potential for abuse (e.g., DoS attacks) and would require secure communication between client and server, as well as acceptance of the risks associated with transmitting the un-hashed password.
I can't think of a specific algorithm to check the strengh of a password. What we do is we define several criterion and when the password respect a criteria, we add 1 to its score. When the password reach a threshold, the password is strong. Otherwise it is weak.
You can define many different level of strengh if with different throeshold, or you can define different value for a specific criteria. For example, if a password has 5 character, we add 1, but if it got 10, then we add 2.
here is a list of criterion to check for
Length (8 to 12 is ok, more is better)
Contains lowercase letter
Contains uppercase letter
The upper case letter is NOT the first one.
Contains number
Contains symbols
the last character is NOT a human like symbol (ex : . or !)
Does not look like a dictionnary word. Some wise password crack contains library of word and letter substitutes (like Library --> L1br#ry )
Hope that help.
Related
I am working on a php login system and I was wondering how it is possible to make a secure password entry system that asks for say the 1st, 2nd and 8th characters of your password, like many online banking systems do. How could one make this and have the passwords stored as double-salted hashes?
An idea that comes into my mind is to store every character of the password hashed on separate field or serialized:
PSEUDOCODE:
$password is the user password, $secret_word is the word you use to check single characters
function get_hashed_characters($password, $secret_word) {
$char_store = ""
for every character $char in $secret_word
$hashed_char = some_hash_function($char + $password)
$hash_store = $char_store + $hashed_char
return $hash_store
}
function check_hashed_char($password, $hash_store, $char_index, $char) {
if len($hash_store) < $char_index * $HASH_LEN + $HASH_LEN return false
$hashed_char = substr($hash_store, $char_index * $HASH_LEN, $HASH_LEN)
return true if $hashed_char is equals to some_hash_function($char + $password), false otherwise
}
UPDATE: as C4ud3x pointed out, I hash both the character I want to store concatenated with the password
This is a completely different solution sometimes used on Linux systems: Challenge-Response authentication
HSBC use this style of password system on their website:
and in their app: )*
After contacting them they said:
Ensuring the security of our systems is and will continue to be our number one priority.
All the details that are sent to and from our systems are encrypted
using high encryption levels. As long as you keep your log on
information secret, we can assure you that the service is secure. As
you will appreciate, we cannot provide further details about the
additional security measures used by Online Banking, as we must
protect the integrity of the system.
Though this could just be a reference to their use of SSL, I think it probably suggests they:
Encrypt password data in the database
Encrypt the passwords in the database and then encrypt the whole database for better security.
I think the best solution is that provided by ColOfAbRiX, as it doesn't require encryption (which without the technologies available to banks such as HSBC is probably not very secure).
after several days of research and discussion i came up with this method to gather entropy from visitors (u can see the history of my research here)
when a user visits i run this code:
$entropy=sha1(microtime().$pepper.$_SERVER['REMOTE_ADDR'].$_SERVER['REMOTE_PORT'].
$_SERVER['HTTP_USER_AGENT'].serialize($_POST).serialize($_GET).serialize($_COOKIE));
note: pepper is a per site/setup random string set by hand.
then i execute the following (My)SQL query:
$query="update `crypto` set `value`=sha1(concat(`value`, '$entropy')) where name='entropy'";
that means we combine the entropy of the visitor's request with the others' gathered already.
that's all.
then when we want to generate random numbers we combine the gathered entropy with the output:
$query="select `value` from `crypto` where `name`='entropy'";
//...
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF).$entropy.microtime()))));
note: the last line is a part of a modified version of the crypt_rand function of the phpseclib.
please tell me your opinion about the scheme and other ideas/info regarding entropy gathering/random number generation.
ps: i know about randomness sources like /dev/urandom.
this system is just an auxiliary system or (when we don't have (access to) these sources) a fallback scheme.
In the best scenario, your biggest danger is a local user disclosure of information exploit. In the worst scenario, the whole world can predict your data. Any user that has access to the same resources you do: the same log files, the same network devices, the same border gateway, or the same line that runs between you and your remote connections allows them to sniff your traffic by unwinding your random number generator.
How would they do it? Why, basic application of information theory and a bit of knowledge of cryptography, of course!
You don't have a wrong idea, though! Seeding your PRNG with real sources of randomness is generally quite useful to prevent the above attacks from happening. For example, this same level of attack can be exploited by someone that understands how /dev/random gets populated on a per-system basis if the system has low entropy or its sources of randomness are reproducible.
If you can sufficiently secure the processes that seed your pool of entropy (for example, by gathering data from multiple sources over secure lines), the likelihood that someone is able to listen in becomes smaller and smaller as you get closer and closer to the desirable cryptographic qualities of a one-time pad.
In other words, don't do this in PHP, using a single source of randomness fed into a single Mersenne twister. Do it properly, by reading from your best, system-specific alternative to /dev/random, seeding its entropy pool from as many secure, distinct sources of "true" randomness as possible. I understand you've stated that these sources of randomness are inaccessible, but this notion is strange when similar functions are afforded to all major operating systems. So, I suppose I find the concept of an "auxiliary system" in this context to be dubious.
This will still be vulnerable to an attack by a local user cognizant of your sources of entropy, but securing the machine and increasing the true entropy within /dev/random will make it far more difficult for them to do their dirty work short of a man-in-the-middle attack.
As for cases where /dev/random is indeed accessible, you can seed it fairly easily:
Look at what options exist on your system for using /dev/hw_random
Embrace rngd (or a good alternative) for defining your sources of randomness
Use rng-tools for inspecting and improving your randomness profile
And finally, if you need a good, strong source of randomness, consider investing in more specialized hardware.
Best of luck in securing your application.
PS: You may want to give questions like this a spin at Security.SE and Cryptography.SE in the future!
Use Random.Org
If you need truly random numbers, use random.org. These numbers are generated via atmospheric noise. Besides library for PHP, it also has a http interface which allows you to get truly random numbers by simple requests:
https://www.random.org/integers/?num=10&min=1&max=6&col=1&base=10&format=plain&rnd=new
This means that you can simply retrieve the real random numbers in PHP without any additional PECL exension on the server.
If you don't like other users to be able to "steal" your random numbers (as MrGomez' argues), just use https with a certificate checking. Here follows an example with https certificate checking:
$url = "https://www.random.org/integers/?num=10&min=1&max=6&col=1&base=10&format=plain&rnd=new";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
$response = curl_exec($ch);
if ($response === FALSE)
echo "http request failed: " . curl_error($ch);
else
echo $response;
curl_close($ch);
If you need more information on how to create https requests:
Make a HTTPS request through PHP and get response
http://unitstep.net/blog/2009/05/05/using-curl-in-php-to-access-https-ssltls-protected-sites/
More on security
Again, some might argue that if the attacker queries random.org at the same time as you, he might get the same numbers and predict.. I don't know if random.org would even work this way, but if you are really concerned, you may lessen the chance by fooling the attacker with dummy request which you throw out, or use only a certain part of the random numbers you get.
As MrGomez notes in his comment, this shall not be considered as an ultimate solution to security, but only as one of possible sources of entropy.
Performance
Of course, if you need a blitz latency then doing one random.org request per one client request might not be best idea... but what about just doing one bigger request to pre-cache the random numbers like every 5 minutes?
To come to the point, as far as i know there is no way to generate entrophy inside a PHP script, sorry for this non-answer. Even if you look at well etablished scripts like phppass, you will see, that their fallback system cannot do some magic.
The question is, whether you should try it anyway or not. Since you want to publish your system under GPL, you propably don't know in what scenario it will be used. In my opinion it's best then to require a random source, or to fail fast (die with an appropriate error message), so a developer who wants to use your system, knows immediately, that there is a problem.
To read from the random source, you could call the mcrypt_create_iv() function...
$randomBinaryString = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
...this function reads from the random pool of the operating system. Since PHP 5.3 it does it on Windows servers as well, so you can leave it to PHP to handle the random source.
If you have access to /dev/urandom you can use this:
function getRandData($length = 1024) {
$randf = fopen('/dev/urandom', 'r');
$data = fread($randf, $length);
fclose($randf);
return $data;
}
UPDATE:
of course you should have some backup in case opening the device fails
should you have access to client side, you can enable mouse movement tracking - this is what true crypt is using for extra level of entropy.
as i have said before, my rand function is a modified version of phpseclib's crypt_random function.
u could see it in the link given on my first post. at least the author of the phpseclib cryptographic library confirmed it; not enough for ordinary apps? i don't speak of extreme/theoretical security, just speak about practical security to the extent really needed and at the same time 'easily'/'sufficiently low cost' available for almost all of the ordinary applications on the web.
phpseclib's crypt_random effectively and silently falls back to the mt_rand (which u should know is really weak) in the worst case (no openssl_random_pseudo_bytes or urandom available), but my function uses a much more secure scheme in such cases. it's just a fall back to a scheme that brute-forcing/predicting its output is much harder and (should be) in practice sufficient for all ordinary apps/sites. it uses possible (in practice very likely and hard to predict/circumvent) extra entropy that is gathered over time which quickly becomes almost impossible to know for outsiders. it adds this possible entropy to the mt_rand's output (and also to the output of other sources: urandom, openssl_random_pseudo_bytes, mcrypt_create_iv). if u are informed u should know, this entropy can be added but not subtracted. in the (almost surely really rare) worst case, that extra entropy would be 0 or some too tiny amount. in the mediocre case, which i think is almost all of the cases, it would be even more than practically necessary, i think. (i have had vast cryptography studies, so when i say i think, it is based on a much more informed and scientific analysis than ordinary programmers).
see the full code of my modified crypt_random:
function crypt_random($min = 0, $max = 0x7FFFFFFF)
{
if ($min == $max) {
return $min;
}
global $entropy;
if (function_exists('openssl_random_pseudo_bytes')) {
// openssl_random_pseudo_bytes() is slow on windows per the following:
// http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
if ((PHP_OS & "\xDF\xDF\xDF") !== 'WIN') { // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
extract(unpack('Nrandom', pack('H*', sha1(openssl_random_pseudo_bytes(4).$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
}
// see http://en.wikipedia.org/wiki//dev/random
static $urandom = true;
if ($urandom === true) {
// Warning's will be output unles the error suppression operator is used. Errors such as
// "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
$urandom = #fopen('/dev/urandom', 'rb');
}
if (!is_bool($urandom)) {
extract(unpack('Nrandom', pack('H*', sha1(fread($urandom, 4).$entropy.microtime()))));
// say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
// -4 % 3 + 0 = -1, even though -1 < $min
return abs($random) % ($max - $min) + $min;
}
if(function_exists('mcrypt_create_iv') and version_compare(PHP_VERSION, '5.3.0', '>=')) {
#$tmp16=mcrypt_create_iv(4, MCRYPT_DEV_URANDOM);
if($tmp16!==false) {
extract(unpack('Nrandom', pack('H*', sha1($tmp16.$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
}
/* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:
http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:
http://svn.php.net/viewvc/php/php-src/tags/php_5_3_2/ext/standard/php_rand.h?view=markup */
static $seeded;
if (!isset($seeded) and version_compare(PHP_VERSION, '5.2.5', '<=')) {
$seeded = true;
mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
}
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF).$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
$entropy contains my extra entropy which comes from all requests parameters' entropy combined till now + current request's parameters entropy + the entropy of a random string (*) set by hand at the installation time.
*: length: 22, composed of lower and uppercase letters + numbers (more than 128 bits of entropy)
Update 2: Code Review Warning to Everyone: Dont use The code in the original question. It's a security liability. If this code is online anywhere Remove it as it open the whole system, network and database to a malevolent user. Your not only exposing your code but all of your users data.
Do not ever Serialize user inputs. If in your code your already doing it, Stop your server and change your code. This is a great exemple of Not doing crypto by yourself.
Update 1: For real security you need to have UN-guessable randomess in your entropy. A suitable option to add entropy has your Question refer-to is to use the Delta of your script's execution time Not microtime() by itself . Because the Delta Rely on the load of your server. And so is a combination of the hardware environment, temperature, network load, power load, disk access, Cpu usage and voltage fluctuation which together are unpredictable.
Using Time(), timestamp or microtime is a flaw in your implementation.
Script execution Delta Exemple code coming:
#martinstoeckli stated correctly that a Suitable Random generation for crypto is from
mcrypt_create_iv($lengthinbytes, MCRYPT_DEV_URANDOM);
but is outside the requirements of not having a crypto module
In SQL use the RAND() in conjunction with your generated number.
http://www.tutorialspoint.com/mysql/mysql-rand-function.htm
Php offer as well the Rand() function
http://php.net/manual/en/function.rand.php
they wont give you the same number so you could use both.
rn_rand() should be getting used not rand()
I was wondering how mobile authenticators work (like Battle.net, rift, some banks have one, etc.), so I can make one for my own site (just for fun).
I understand the basics: authenticator has code related to the phone and code related to the website. Users enters the phone code on the website. Can then generate a token related (using the phone and website code).
I'm just wondering how the tokens are created. Is there a standard algorithm for this? How does the algorithm work? Any existing PHP libraries that can do something like this (as an example)?
Have a look at Google Authenticator. There are already iPhone, Android and Blackberry apps for that and it's an established protocol.
They have implemented it as an open-source PAM module which you may be able to use with the PECL PAM package.
There is a pure PHP version but I haven't used that so can't vouch for it.
The spec isn't that complex so you could probably implement it yourself, especially if you converted the C module. The specification linked there explains its working in full detail.
Edit: I guess to answer the original question, that's an RFC, so it's somewhat standardised, and it's a fully open specification and the tools to use it are fully open-source. The protocols are known as HOTP and TOTP. The former is HMAC based on a counter (so the nth password is used) whereas the latter is time-based (so the password cycles every 30 seconds).
Concerning the Blizzad Battle.Net authenticator, you can find an open source implementation in PHP : https://github.com/krtek4/php-bma
The implementation is used to provide a online authentication service for Battle.Net : https://authenticator.me
If you want to do something like it for your website, it's pretty simple. The only thing to share between the server and client part are the secret generated by the server. So when a client is requesting for a new secret, just store it and you will be able to compute the code at any moment to compare with what is sent to you.
I implemented this once. I use a 4 digit key with a subset of characters (notice that potentially confusing characters like 0oO and l1L are removed. I used 4 characters because the potential space of 4 digits from the characters set was larger than the 6 digits of an RSA key.
Anyway, I let the user log in with their username and password. If that is correct, generate a key and send it to the phone and save it in the session and show the user the next page, which requires the key be entered. The user gets the 4 digit key from their phone and enters it into the page. Then check what they entered against the session-saved key and there you go.
Some handy features to have: make the key expire after a few minutes, but long enough that text message delays don't make it impossible. Make it expire after a few bad tries. Give the users a link to resend the key or to send a new key.
//pick a random 4 digit string
$chars = "abcdefghjkrstwxyzABCDEFGHJKRSTWXYZ23456789";
$key = "";
for($i=0;$i<4;$i++){
//here, rand is used, but any generator could be used
//to choose the characters.
$key .= $chars[rand(0,strlen($chars)-1)];
}
//save it to the session
$_SESSION['test']['KEY'] = $key;
If it were me I'd go with generating a hash based on the previously used hash and a common nonce, the tricky bit would be keeping the two systems in sync. e.g.
<?php
class otp {
var $salt;
var $previous_hash;
var $user_id;
function __construct($user_id)
{
$this->user_id=$user_id;
list($this->$salt, $this->$previous_hash)
=unserialize(file_get_contents(BASE_PATH . $user_id));
}
function authenticate($submitted_otp)
{
if (!$this->salt) {
// user does not exist
return false;
}
$new_hash=$this->previous_hash;
// allow for the sequence to get out of sync by 5 steps....
for ($x=0; $x<5; $x++) {
$new_hash=md5($this->salt, $new_hash);
if ($new_hash===$submitted_otp) {
$this->update_token($new_hash);
return true;
}
}
// none of the next N iterations of the local password match
return false;
}
function create_user($user_id, $salt, $init_hash)
{
return file_put_contents(BASE_PATH . $user_id, array($salt, $init_hash));
}
function update_token($new_hash)
{
file_put_contents(BASE_PATH . $user_id, array($this->salt, $new_hash));
}
}
Of course, in practice you probably wouldn't want to use a whole 32 char md5 hash (just, say, the first 6 characters, and applying cleansing such as changing 'S' to '5' etc).
During registration, I'm debating how I should set user password:
Let the user choose it. If I do this, I have to enforce some standards (length, weakness, may involve regexes, etc.) What do you normally do when you choose this way and why? Is there a library available for PHP for this?
Auto-generate the password for the user and email it to them to the email they provided. They can't log in without getting the password so it's email verification too. Problem is the password may be too difficult for the user to remember. If I allow them to change it to something easier, that defeats the purpose of me choosing it for them in the first place. I'm also worried about the act of transmitting the password (as plain un-hashed password) in an email.
I'm leaning towards the second, but would prefer a more informed answer before choosing. There are probably things I'm not paying attention to like user convenience and other technical issues too. What do you do?
Edit: Based on the answers, I'm going with the first option then, letting the user choose. My question would then be, what password strength/length/etc. should I require, and how do I enforce it? Are there PHP libraries available for that?
I think there is only one answer to this. Let the user make her own password!
Everything else is programmer lazyness and bad interaction design and customer friendlyness (IMO).
Now I'd see a few exceptions, namely if it is some kind of low-importance intranet system with only a handfull of users who agree to this or if it is a one-shot account which people won't need to login later on.
You need to hash&salt your passwords anyways, even if you generate them yourself.
All you need to add, is some validation rules at the first submit of the user. That's probably even easier to make than a good password generation tool.
Password strength
A link to a post about 10 password strength meters
You could always suggest a random password if the user's imagination suddenly turns blank. Of course you made sure the generated password is "strong" (according to your rules), and you would have a "suggest a new password"-button.
Users that don't want complicated passwords or unique passwords for different sites will always change to the one they would have picked if you would have let them in the first place. In this case, you made them impatient because you:
sent out a valid password/activation code in an email
made them check their email inbox (and perhaps wait for your email to arrive)
made them change their password
Final advice: rather than forcing; encourage and emphasize the importance of a size password. The password-strength meter is one of the fun ways to do this.
PHP password strength. This page has some basic code that's clean code so you should be able to modify it to suit your needs. Based on code from: http://www.tutorialtoday.com/read_tutorial/113/
Tests for lowercase / uppercase / numbers / nonword / at least 8 chars. If all of the conditions are met strength will be equal to 5.
$password = **HOW YOU GET THE PASS***($_POST['pass'])????;
$strength = 0;
// letters (lowercase)
if(preg_match("/([a-z]+)/", $password)) {
$strength++;
}
// letters (uppercase)
if(preg_match("/([A-Z]+)/", $password)) {
$strength++;
}
// numbers
if(preg_match("/([0-9]+)/", $password)) {
$strength++;
}
// non word characters
if(preg_match("/(W+)/", $password)) {
$strength++;
}
// longer than 8 characters
if(strlen($password) > 8)) {
$strength++;
}
if ($strength >= 5)
print "woo hoo";
else
print "bah";
here the code to generate Password with alphanumaric values
function genRandomString() {
$length = 8;
$characters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$string = "";
for ($p = 0; $p < $length; $p++) {
$string .= $characters[rand(0, strlen($characters))];
}
return $string;
}
you do the following
$mailPass =genRandomString();
Personally I find it very irritating when access passwords are emailed as cleartext into the wild. Moreover, the user will in any case have the ability to change the password (I hope) and will therefore change it to something else than what you have generated. Thus, why not allow the user to pick a password he/she wants at registration time? Of course, it is neccessary to indicate weak passwords (and even maybe disallow their usage as a whole), but you do not really have to code the heart of this check, as there are dozens of ready-made js libraries that can do this for you.
Currently my database user and its password are pretty easily to guess, eg.
database user: dbadmin
database pwd : super + companyname
What and how to generate a secure a secure database password? Using md5 or sha1??
What are the things that I need to pay attention to secure my database?
I am using php, thanks
If what you're looking for is just a secure password generator, there are a number of tools for creating strong passwords. Keepass is one that I use, but you can also look into pwgen, apg, passgen, or others.
Keepass -- http://keepass.info/
apg -- http://www.debianadmin.com/automated-password-generator-in-debian.html
passgen -- http://www.linuxbuilt.com/passwords.php
pwgen -- http://sourceforge.net/projects/pwgen/
To keep the database secure you also need to consider where you're using the username/password combination in your scripts. One technique that I've seen used often is to put the credentials in a separate file and to import them everywhere else they're needed. This way you can put strict access regulations on that one file through your webserver and filesystem security settings.
Security is a layered approach, and the more precautions you take the more resistant your server will be to malicious activity.
Use some kind of pattern to create a password that is complex, but also possible for you to remember.
E.g. Think of a silly sentence and take the first letter of every word.
"16 Butterflys drew straws to see which 5 should become Caterpillars first."
forms the password "16Bdstsw5sbCf".
That's 13 chars long, contains 3 numbers and some upper case chars. That should be pretty strong, and it's much easier to remember than just a random string.
For even better strength, throw some punctuation in there too.
If you use phpMyAdmin it has, when you are creating a user, a 'generate password' option. Try that or you could do something like this:
function generatePassword($length) {
// start with a blank password
$password = "";
// define possible characters
$possible = "0123456789bcdfghjkmnpqrstvwxyz";
// set up a counter
$i = 0;
// add random characters to $password until $length is reached
while ($i < $length) {
// pick a random character from the possible ones
$char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
// we don't want this character if it's already in the password
if (!strstr($password, $char)) {
$password .= $char;
$i++;
}
}
// done!
return $password;
}
You can make a secure password simply by mashing the keyboard:
Z3w947CFqnY39cfo
That's as secure as you possibly need - it's immune to dictionary attacks, and long enough to be immune to brute force attacks.
(Of course you'll need to write it down, unless you have a trick memory, but that's always a compromise with passwords - and presumably it will appear in your application code or config anyway.)