What to use for password security ?
Being a newbie at this (and coding in general), I've been looking at all sorts of different tutorials, articles etc. about PHP and security concerning passwords. This resulted in all sorts of different solutions, when using a mysql db and php. The unfortunate things is, that all of these different articles and / or tutorials seem to contradict one another. Some say md5 is fine for the "mainstream" user, others recommend sha1 or crypt(). Now, as far as I can see, only crypt() seems like a "viable" solution. Using md5 doesn't exactly seem safe, having all sorts of different online decryption sites. Using sha1, even with a salt, doesn't seem any better. A short demonstration is given here:
http://www.youtube.com/watch?v=lrGMxH8WNZ8
All of this leads me to my question. What would be the best solution for a mysql driven forum site ? It doesn't, in principle at least, contain any "personal information" (couldn't remember the correct english term). Is it necessary to make some SSL solution or......?
Thank you.
Everyone is going to tout bcrypt which is solid. but i prefer the new PHP5 API password hashing function which is standard in php 5.5.
read about it here
It is super easy and from what I can tell super secure.
Just set up a 60 length varchar in your db and your set
$hash = password_hash($password, PASSWORD_BCRYPT);
and to verify:
if (password_verify($password, $hash)) {
// password valid!
} else {
// wrong password :(
}
Since not all hosting servers offer 5.5 you can get the class here
As far as SSL goes, it is recommended.
md5 and sha are not to be used really - http://www.php.net/manual/en/faq.passwords.php#faq.passwords.fasthash
Also, whether you store boring non sensitive info or government secrets, you should use the most secure methods. What if your site plan changes once all this is implemented and suddenly you DO need to store sensitive data?
What if someone hacks your non-sensitive database through insecure methods and wipes everything? It may be nothing more than a pain losing all that data and having to restore form a back up, but for me this in itself is enough.
Also, as someone has hacked your DB, what if they return later and do it again, you'll end up having to update your login methods anyway.
Adding to that, why not learn best practice from the start then any site you do is best security approach? Why learn simple and not-so-secure methods for one site to learn a different way for another later?
Learn best practice, always, and always use it then you only need to learn and use one method throughout all your code and thus from practice makes you more efficient and knowledgeable with it.
A combination of crypt and Blowfish is pretty much the way I go now. It takes user password from registration and spits out a hashed string and unique salt together, always same char length so you can manage it in a database easily.
All users salts are different so someone obtaining all your DB data and working out how one password salt is formed, which is barely possible, gets only one password and in no way the method to obtain others.
Then when user logs in, you simply use the built in function to check their inputted password from login form to the one in the DB and the library works out the hash/salt/etc and checks the two. If match log them in, otherwise not.
Related
I have read on this great forum and several other places how difficult, if not impossible to decrypt with md5.
Unfortunately, I used md5 to hash our users' passwords:
// hash to sanitize the input further
$password = md5($password);
Now, I am a bit of trouble because users who cannot remember their passwords, are not able to utilize our Recover password feature.
When they attempt to recover their password, they receive the encrypted password which is useless to them because they can't use it.
Given how difficulty, almost impossible it is to decrypt an md5 hash, is there a simpler encryption / decryption mechanism that someone could suggest that I try?
Pretty much in hot water now.
Unfortunately, I used md5 to has our users' passwords
How is that unfortunate? That's what you're supposed to do. User passwords should be obscured behind a 1-way hash and not recoverable by anybody. Not even by you as the system owner/administrator.
users who cannot remember their passwords, are not able to utilize our Recover password feature
There should be no such thing as a "recover password feature". It's called a "reset password feature". You can change a user's password administratively. But you should never ever be able to read it.
When they attempt to recover their password, they receive the encrypted password which is useless to them because they can't use it.
But attackers can use it. Which is why you shouldn't be sending it out to anybody in the first place.
is there a simpler encryption / decryption mechanism that someone could suggest that I try?
Is doesn't get much simpler than:
md5($password)
It's one function call. Five keystrokes. It's really simple to use. And since you're already using it, you're good.
Once you stop publishing your password hashes, you'll be all set on handling user passwords (at least as far as we know here). Keep up the great work! There are tons of services out there which don't properly obscure user passwords. Thank you for at least attempting it.
Note: As users have pointed out (users who are far more familiar with PHP these days than I am), while using md5() directly is a step in the right direction, it's not the best you could be doing.
Instead, take a look at PHP's built in password handling functionality. (Or, if you're using an older, pre-5.5 version of PHP, there's a compatibility pack which maintains the same functionality.) Jay Blanchard has written a handy article on its use here.
The concept is the same, obscuring user passwords by means of a one-way hash. But the tooling has evolved considerably.
I was thinking of writing my own authentication script but I don't know much about security.
From the articles I've reading, it looks like it usually involves hashing the password with a salt and storing it in the database. Then when user requests to log in, password is hashed and compared with the database. If it matches, then the user's data is stored in $_SESSION.
However, I don't know if this is secure or not. I read something about storing session keys in the database but I'm not sure about how that works, or how to implement that.
Can someone explain how to implement secure authentication?
Also, are there any suggestions for PHP authentication libraries I can incorporate that are easy to learn instead of writing my own?
Check this answer here.
Although the answer is 3 years old, the suggested phpass library is up to date.
Also, +1 to Aron Cederholm. Password security is an extensive subject and you should look first at the related questions already discussed here on StackOverflow so you will be more familiar with the subject and best practices in security.
Although I like frameworks (Symfony, Zend, etc) as they generally implement these good practices, just using them don't make you a good programmer. You have to learn its inner workings. I always salute a programmer dwelving into coding his own secure authentication mechanism (as long as they don't implement it in a live site that really needs strong security), because that's the best way to learn and understand the inners of the subject. Always start from an existing implementation, and THEN use that as an example for creating your own codebase.
Things to keep in mind:
Authentication; verifying the user is who they say they are.
Authorization; ensuring the user is allowed to do what they are trying to.
Accounting; recording and auditing what they do.
For authentication, you'll need to track "users" connected to and (often) authenticated with the system. This requires knowing an identifier (a username, email, or some other unique token) and a pass-phrase. Store the username and pass-phrase somewhere, but never store the pass-phrase without securing it first: don't use a message digest algorithm (like MD5 or SHA1) with a salt. Use bcrypt instead. Although it's not a bad idea to use a framework here, do not always rely on a framework to do the right thing.
For authorization, you'll need to track what actions the user is taking and perform permission checks to see if they are allowed to do the action they are attempting. This can be accomplished in a number of different ways and all of them are domain specific -- you won't often find a cut-n-dried example of it, though you can find lots of frameworks to help you.
For accounting, you need to record what actions the user does. This is the most often neglected part of any application, but when bad things happen, it's utterly crucial knowledge to have and reconstructing it from web server access logs is a nightmare. Again, this is domain specific but a good framework should ease the implementation of it.
Lastly, tying all three of these together is your user's session. When you call 'session_start()' in PHP, it sends a session identifier as a cookie to the client and stores a file to the server's hard drive containing the contents of $_SESSION for that user. You can also configure PHP to override the default functionality and save session data using session_set_save_handler. You can then store that information to the database.
TL;DR: Use a framework like CodeIgniter, Drupal, Yii or some other actively developed solution. The vast majority of frameworks out there will do just about anything you need them to, and if they don't, they can be modified very easily. Don't create your own framework for this; use one that is already available.
I use tank_auth (a Codeigniter plugin) which is pretty good. The source code is a good reference for how to implement secure login.
I am just wondering for the sake of knowledge if this login system is secure, because i had planned on using it as a learning tool. I don't want to use anything that will teach me the wrong way. Can anyone help?
https://github.com/ivannovak/jpmaster77-s-Login-System-
When skimming code quick I don't think you should use this code, because it could be compromised.
P.S: I also don't think you should be learning that stuff(if you want to learn openid specifications/libraries, but leave it to the security experts. You should use openid/facebook connect/etc. I use rpxnow.com with much pleasure.
Old codebase
first of the code base is old. Last commit is August 11, 2009. I would look at a loginsystem which is more maintained(newer). For example it does not use the newer/safer PDO to access your database. I also find the codebase a little bit messy. no MVC?
SSL
Not sure if this codebase enforces SSL. If not than your passwords will be transmitted in plain-text.
Mysql Injection
This code might be unsafe because of mysql injection =>
https://github.com/ivannovak/jpmaster77-s-Login-System-/blob/master/mail.php#L118
$q = "SELECT * FROM mail WHERE UserTo = '$user' ORDER BY SentDate DESC";
If session->username has been comprimised(have not looked at all references) than your system is unsafe. A decent(modern) system uses PDO.
No salt
I don't believe the system does use salt so with a Rainbow table all password can be discovered when your database is compromised. =>
https://github.com/ivannovak/jpmaster77-s-Login-System-/blob/master/include/session.php#L157
$result = $database->confirmUserPass($subuser, md5($subpass));
Other things you should consider
CSRF
XSS attacks
localhost?
Also this is line is strange(not unsafe) =>
https://github.com/ivannovak/jpmaster77-s-Login-System-/blob/master/include/mailer.php#L34
You can't reach localhost from the internet.
Some points you may want to consider:
1) Does it use md5 encryption or sha1 (sha1 is better).
2) Does it use salting or not?
3) Does it ensure that only https access is allowed? (ideally the http:// login/password page should redirect to https:// version).
4) How does forgot-password work? The password reset link should ideally be sent to registered email id instead of being accessible directly online. If there are some inbuilt security questions - are they tough enough? Are the security question answers themselves properly encrypted?
regards,
JP
Looking at your code it seems you are storing username and userid in 3 locations, session (quite ok, but prone to session hijacking depending on your server), and 2 cookies ( looks very wrong as the username is already half of the work done for compromising a system.
password are not salted, which makes password easier to guess.
username are check with the database before scrubbing (remember bobby table, xkcd)
don't ever trust addslash or any magic quote function use placeholders to transmit variables to query (always !!!!)
Having any modification to the user table outside admin purpose is bad (security wise because you cannot separate dbusers for the different purpose) (performance wise because I know that writing a table requires table or line locking if you are lucky )
perform your read and update query preferably with different users.
try not to use code where the comment tells you where is the cool part, any sane coder would not put this kind of stuff in their comment.
the database layer has one function to check username+userid , but the session include makes a direct query to check the username in the login function ?
my advice would be to use something else.
I'll assume that you really want to learn and not just use the code.
I fear that if you ask this question here, and someone answers yes or no, you will learn nothing. Just look at the code, ask yourself what is the code doing on every step. Search for common security issues on login systems online, then check if the code has some.
There is no such thing as "teach me the wrong way". If you find out, by your own means, that the code is bad, you learn something. If you find out that the code is good, also, you learn something.
If you assume that the code is good or bad without looking at it in depth, you learn nothing.
I'm developing my own PHP framework. It seems all the security articles I have read use vastly different methods for user authentication than I do so I could use some help in finding security holes.
Some information that might be useful before I start. I use mod_rewrite for my MVC url's. Passwords are encrypted with 24 character salt unique to each user. mysql_real_escape_string and/or variable typecasting on everything going in, and htmlspecialchars on everything coming out.
Step-by step process:
Top of every page:
session_start();
session_regenerate_id();
If user logs in via login form, generate new random token to put in user's MySQL row. Hash is generated based on user's salt (from when they first registered) and the new token. Store the hash and plaintext username in session variables, and duplicate in cookies if 'Remember me' is checked.
On every page, check for cookies. If cookies set, copy their values into session variables. Then compare $_SESSION['name'] and $_SESSION['hash'] against MySQL database. Destroy all cookies and session variables if they don't match so they have to log in again.
If login is valid, some of the user's information from the MySQL database is stored in an array for easy access. So far, I've assumed that this array is clean so when limiting user access I refer to user.rank and deny access if it's below what's required for that page.
I've tried to test all the common attacks like XSS and CSRF, but maybe I'm just not good enough at hacking my own site! My system seems way too simple for it to actually be secure (the security code is only 100 lines long). What am I missing?
Edit: For calling functions from the controller, anything that uses anything other than SELECT queries will require $_POST data to confirm a delete, for example, in addition to the user rank requirements.
I've also spent alot of time searching for the vulnerabilities with mysql_real_escape string but I haven't found any information that is up-to-date (everything is from several years ago at least and has apparently been fixed). All I know is that the problem was something to do with encoding. If that problem still exists today, how can I avoid it?
The encrypt function I borrowed from somewhere and modified:
public function encrypt($str, $salt = NULL) {
if ($salt == NULL) $salt = substr(md5(uniqid(rand(), true)), 0, 24);
else $salt = substr($salt, 0, 24);
return $salt.sha1($salt.$str);
}
Do yourself a favour and use a standard library for hashing your passwords.
Because security tends to be a lot more complicated and with more invisible screw up possibilities than most programmers could tackle alone, using a standard library is almost always easiest and most secure (if not the only) available option.
The standard library:
Take a look at: Portable PHP password hashing framework: phpass and make sure you use the CRYPT_BLOWFISH algorithm if at all possible.
example of code using phpass (v0.2):
require('PasswordHash.php');
$pwdHasher = new PasswordHash(8, FALSE);
// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );
// $hash would be the $hashed stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
PHPass has been implemented in some quite well known projects:
phpBB3
WordPress 2.5+ as well as bbPress
the Drupal 7 release, (module available for Drupal 5 & 6)
others
The good thing is that you do not need to worry about the details, those details have been programmed by people with experience and reviewed by many folks on the internet.
Whatever you do if you go for the 'I'll do it myself, thank you' approach, do not use MD5 anymore. It is a nice hashing algorithm, but utterly broken for security purposes.
Currently, using crypt, with CRYPT_BLOWFISH is the best practice.
CRYPT_BLOWFISH in PHP is an implementation of the Bcrypt hash. Bcrypt is based on the Blowfish block cipher, making use of it's expensive key setup to slow the algorithm down.
For more information on password storage schemes, you could also read Jeff`s blog post about it: You're Probably Storing Passwords Incorrectly
I don't see any real prevention for CSRF attacks in there. CSRF is (from my understanding) the most commonly overlooked/screwed up vulnerability in websites. Many of the big guys have been hacked via CSRF vulnerabilities.
The current best solution to this problem is using synchronizer tokens.
You had also better be very religious about escaping/encoding user input properly. Things that are inserted into the database must be escaped, and things output back to the user have to be encoded based on their context: CSS, HTML, JavaScript, etc all have to be encoded differently.
Furthermore, you shouldn't combine SHA-1 with MD5. While SHA-1 is stronger than MD5, you are essentially weakening it by combining it with MD5. Additionally, neither of these algorithms are recommended for use in new applications. For the time being you should be using SHA-256 (it is available out-of-the-box in PHP).
By trying to over-engineer the approach you may be undermining the security.
What session handler are you using? Have you checked that session_regenerate_id() renames an existing session rather than making a copy of it? If the latter then you are creating lots of session files that are much easier to find with a random search.
How do you deal with users splitting sessions? This line of code is going to break depending on the browser and how the user splits the session.
The only place you should be using session_regenerate_id() is when you authenticate the user and when the user logs out.
mysql_real_escape_string and/or variable typecasting on everything going in
NO, no, no. This keeps coming up here.
You don't change the representation of incoming data. By all means /validate/ input, and you should always /sanitize/ output - you don't /sanitize/ input.
If user logs in via login form, generate new random token to put in user's MySQL row. Hash is generated based on user's salt (from when they first registered) and the new token. Store the hash and plaintext username in session variables,
Nothing here which improves your security
Store the hash and plaintext username in session variables, and duplicate in cookies if 'Remember me' is checked
So you store the plaintext password on the users machine! This is so not secure. If you want a remember me function, then encrypt the password and an expiry date with a salt and store that.
On every page, check for cookies. If cookies set, copy their values into session variables.
So users can manipulate any value stored in the session by returning a cookie with the same name? OK so they probably can't predict the hash so it'd be tricky to fake the user - but there are lots of other ways this could be used to compromise your system.
You've tried very hard to make your system secure - but at best all you've actually done is obfuscated the parts of your system that work properly.
C.
You may want to check the user.rank against a whitelist, to make sure that the value exists in the lookup table and is valid (active, not deleted etc), and also to force typecasting and range.
As for tokens, you will want to generate a new token for each page that expects to receive GET/POST data from a form. If a session is hijacked then a token for the session could be insufficient. Setting a shorter timeout for the session could mitigate.
Don't trust anything from a cookie right away, consider using http://php.net/manual/en/book.filter.php before stashing in the Session.
If you plan to have an admin panel for the system it's ideal to enforce password strength, manual admin login name selection (in preference to admin/password stock logins upon setup).
Blacklist the usual login names such as "a';DROP DATABASE..." and "Administrator", you know the drill.
Use least-privilege accounts when setting up your database, you don't want your visitors to be playing havoc with your tables.
"Passwords are sha1 and md5 encrypted"
Why would there be a need to use two hash functions? I guess you do it due to a lack of understanding (no offense!). You should read this article on secure password schemes (hint: use bcrypt).
with session_regenerate_id(); you'll make the "back" button in your browser unusable. But still combined with "remember me" feature it become useless.
I can't see the CSRF defence in your description
There is not a single problem with mysql_real_escape string
How do I write/put together a secure login in PHP? The website developer guide said I shouldn't roll my own, so referring to samples available via Google is useless.
How do you pros do it? Lets say you're building a world-class app in rails, would the same libraries / techniques be usable here?
Thanks
In Rails, one would generally use a pre-existing library. Authentication is easy to do wrong, and the problem's been solved so many times that it's rarely worth the effort to solve it again. If you are interested in writing your own implementation, then I'll describe how modern authentication works.
The naive method of authenticating a user is to store their password in a database and compare it to the password the user submits. This is simple but unbelievably insecure. Anyone who can read your database can view anyone's password. Even if you put in database access controls, you (and your users) are vulnerable to anyone who hacks around them.
Proper form is to use a cryptographic hash function to process the password when it is chosen and then every time it is submitted. A good hash function is practically irreversible -- you can't take a hash and turn it back into a password. So when the user logs in, you take the submitted password, hash it, and compare it to the hash in the database. This way, you never store the password itself. On the downside, if the user forgets their password, you have to reset it rather than send it to them.
Even this, however, is vulnerable to certain attacks. If an attacker gets hold of your password hashes, and knows how you hash your passwords, then he can make a dictionary attack: he simply takes every word in the dictionary and hashes that word, keeping it with the original. This data structure is called a rainbow table. Then, if any of the dictionary word hashes match a password hash, the attacker can conclude that the password is the dictionary word that hashes to that password. In short, an attacker who can read your database can still log in to accounts with weak passwords.
The solution is that before a password is hashed, it is combined (usually concatenated or xor'd) with a value called the salt which is unique to each user. It may be randomly generated, or it may be an account creation timestamp or some such. Then, an attacker cannot use a rainbow table because every password is essentially hashed slightly differently; he would have to create a separate rainbow table for every single distinct salt (practically for each account), which would be prohibitively computationally expensive.
I will echo the advice of the other answerers: this is not simple stuff, and you don't need to do it because it's been done before, and if you do it yourself you stand a very good chance of making a mistake and inadvertently compromising your system's security. But if, for whatever reason, you really, really want to write one yourself, I hope that I have provided an (incomplete!) outline of how it's done.
The Zend Framework has an 'Auth' module which would be a good place to start. Or, if your site will be hosting an install of WordPress or PHPBB, there are ways of leveraging those technologies' authentication modules to sign in to other pages of a site.
One thing to look at when you are trying to authenticate is what is your real goal.
For example, on SO I use my google login, and that works, as they just need to know who I am, and they can trust that Google has an idea. So, if that model will work for you, then look at using OpenID, as there are various tools for that.
If you must do your own, then there will be various tests to ensure that it is secure, again, depending on how paranoid you want to be.
Never trust anything from the user, unless you have used some strict verification.
Use https to help protect the password of the user, you owe them that much.
I will end my response here as Thom did a fantastic response.
by Soulmerge:
I think the accepted answer in your other question states it pretty well. Hash the passwords with a salt. Other than that, there are some security ideas on the transport layer:
Use https when sending passwords. This makes sure nobody can catch them on the wire (man-in-the-middle attack or the client uses an evil proxy)
An alternative is to hash the password using javascript when the login form is submitted. This makes sure that the password is never transported in plaintext. You should hash the hashed value again with a salt on the server. (md5($_POST['postedPwHash'] . $salt))
a good method to somewhat secure the client-server transaction (if no ssl is available) is to use a one-time random key to create a unique hash from the credentials, then only send that unique hash to the server. the server then compares this hash to its own generated hash instead of comparing it to the real credentials. this would provide a good defense against the man-in-the-middle attack. the downside is that to do this the user must have JS enabled (at least i dont know of a good method to encrypt client-side data without it). this means that you will need a sufficient fallback when it isn't on. you can even create the form in JS to make sure its enabled.
this library is a simple library i wrote once that does the procedure i described, though it probably needs some improvements.
note that this is in addition to using "salting" methods and other server-side security measures. it is also quite vulnerable to dictionary attacks as the entire hashing process is by definition procedural, predictable and visible to the user (as JS always is).
My answer is "Don't do it"
This is a very complex area, full of potential security gotcha's. If you are not an expert in this field, then you are really just asking for trouble and problems down the road.
I would recommend looking at getting an existing solution to do. Sadly I don't know any that I would be happy to recommend, other than openid. I'm sure you will get some good suggestions here though...