Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I am wanting to re-write my login scripts for clients websites to make them more secure. I want to know what best practices I can implement into this. Password protected control panels are in their abundance, but very few seem to impliment best practices in terms of code writing, speed and security.

I will be using PHP and a MYSQL database.

I used to use md5, but I see that sha256 or sha512 would be better (together with secure hash and salt).

Some login scripts log the ip address throughout the session or even the user agent, but I want to avoid that as it isn't compatible with proxy servers.

I am also a little behind the best practice in using sessions in PHP 5 (last time I read up was PHP 4) so some best practices with this would be helpful.

Thanks.

share|improve this question
1  
Toss your script on codereview.SE! –  Chris May 2 '11 at 17:51
add comment

5 Answers

up vote 13 down vote accepted

The best think is to not reinvent the wheel. But, I understand, in PHP world it may be difficult to find a high quality component which already does that (even I'm pretty sure that the frameworks implement such things and their implementations are already tested, solid, code-reviewed, etc.)

If, for some reasons, you can't use a framework, here's some suggestions:

Security related suggestions

  • Use bcrypt if you can. It's done for that.
  • If you can't, at least don't use MD5/SHA1. Never. Forget about it. Use SHA512 instead, for example. Use salt too.
  • Protect yourself from brute force. If the user submits a wrong password when she already submitted another wrong password 0.01 seconds ago, it's a good reason to block it. I believe that people can be very fast, but not as fast as that. Another protection would be to set a per-hour failures limit. If the user submitted 3600 wrong passwords in an hour, 1 password per second, chances are this is a brute force.
  • Audit everything, every time. Store logons, but never store passwords in audit log. Make sure that the audit log cannot be modified (i.e. you can add data at the end, but not modify the existing data). Make sure that audit logs are subject to regular backup.
  • Don't remember user credential in cookies, unless the user asks to do it ("Remember me" check box must be unchecked by default to avoid human error).

Ease of use suggestions

  • Let the user remember the password if she wants to, even if most browsers are already this feature.
  • Don't use Google approach when instead of asking for user name and password, the user is asked sometimes for password only, the user name being already displayed in a <span/>. Browsers can't fill the password field in this case (at least Firefox cannot do that), so it forces to logoff, then to logon with an ordinary form, filled by the browser.
  • Don't use JavaScript-enabled popups for logon. It breaks browsers password-remember features (and sucks in all cases).
  • Let the user enter either her user name or her mail address. When I register, sometimes the user name I want to enter is already taken, so I must invent a new one. I have all chances to forget this name in two hours.
  • Always keep a link to "Forgot my password" feature near the logon form. Don't display it only when the user failed to log on: the user who don't remember her password at all have no idea that she must submit a wrong one in order to see the "Forgot my password" link.
  • Don't use security by post-it.
  • Don't invent stupid rules related to the password in order to make it weaker. Example: "Your password must start with a lowercase letter."; "Your password cannot contain spaces."
share|improve this answer
    
Not come across bcrypt. What's your reasoning for recommending that? Why not md5/sha1? Thanks for the rest of the suggestions! –  baritoneuk May 2 '11 at 17:02
    
The reasoning is simple: bcrypt is done by people who are highly qualified. It's also tested, reviewed, etc. So there is no reason to implement the same thing yourself. It's like creating your own cryptography algorithm for your website. *"Why not md5/sha1?": see for example en.wikipedia.org/wiki/MD5#Security –  MainMa May 2 '11 at 17:13
3  
bcrypt is slow and as mentioned implemented by professionals. md5 is a hashing algorithm not an encryption which are not quite the same thing. Hashing a file fast is good, md5 would be used here... hashing a password need not be so fast, bcrypt can help here. Reason I say this is that brute force becomes more difficult when the crypt algorithm is "slow". –  Chris May 2 '11 at 17:54
    
Just regarding the bit about frameworks. I've tended to avoid frameworks in php. However, I assume it could be possible to use one just in the case of a login script. It would be good to discuss which frameworks implement the best practices. Our perhaps a custom solution is best. –  baritoneuk May 2 '11 at 18:12
1  
@baritoneuk: minimum is cool. But I can't count how many sites I've been to that had restrictions like maximum lengths, no non-alphanumeric characters, etc. Bewildering, if you're going to hash the password anyway - which makes me worry that they are not, they're just storing it in their database in the clear. –  Carson63000 May 2 '11 at 21:28
show 4 more comments
  1. Your site should use HTTPS. At no point should you present a login page or accept logins from a non-encrypted connection.
  2. Your cookies should be restricted to HTTP-only and be restricted to secure connections if you use HTTPS.
  3. The login process should take no less than 2 seconds (1 if you think 2 seconds is too long). Use a secure hash when storing and verifying passwords, and use a salt that is harder to guess. Use bcrypt if possible. Otherwise, use some other kind of iterated hash.
  4. Never ever compose your database queries in any way that requires using functions like mysql_real_escape_string. Never use string concatenation to craft your query. Use prepared, parametrized queries. Most, if not all, DB drivers for PHP support it. If you don't know how to do it, spend some time learning how to prepare, bind, and execute using whatever DB you're using. It's the only sure way to guard yourself against SQL injection. PDO supports this.
  5. Encourage your users to not use the same password they use for their email. Remind them that if your site is compromised and if they use the same password in both places, someone can hijack their email.
share|improve this answer
    
+1 for HTTPS, since there is more and more traffic through public WiFi networks. But I disagree with the point 3: 2 seconds is too long from the users point of view. 0.5 s. is just fine, especially if other anti-brute-force techniques are used. –  MainMa May 2 '11 at 16:52
    
I'm confused on point number #4. How do you escape the convention of escaping data with mysql_real_escape_string? All user submitted data should not be trusted and filtered accordingly. –  chrisw May 2 '11 at 17:06
    
@chrisw: Yes, all user input should be handled as if it's malicious. But when you use parametrized queries, it is, bar none, the best way to stop SQL injection. If you're not using prepared and parametrized queries, you are vulnerable to SQL injection. mysql_real_escape_string is false security - it's not a guarantee. Parametrized queries are. –  greyfade May 2 '11 at 17:39
    
I agree now after doing some further reading into it. The function: mysql_real_escape_string is generally safe for the most part, I wouldn't consider it a HUGE liability, but I can see how using the prepared statements are much more efficient. –  chrisw May 2 '11 at 17:54
1  
You can use PDO. –  Felix Guerrero Jul 14 '11 at 6:12
show 1 more comment

Use a salted one-way hash (preferably SHA512 using http://au2.php.net/manual/en/function.hash.php)... that way, if someone does hack your database, even if they know the salt, they cannot extract the passwords (without a rainbow table, which would be looong for SHA512).

Use HTTPS.

Don't send username/password confirmations via email back to the user when they register.

If you allow cookies for 'remember me' - add an extra login to access administration and profile editing functions.

If a user gets a password wrong, don't say they got the password wrong (say they got the 'username or password wrong' at all times) - that way, less clues on what are valid usernames.

Try and implement screen name versus login - that way, less users will display their login name on user listings.

share|improve this answer
    
Completely agree with the bit about not sending emails in plain text by emails. I have lost count of the times web sites do this. If you have 1 password for all sites (which luckily I don't) then potentially all websites are compromised. Such websites probably store passwords in plain text too. sigh –  baritoneuk May 3 '11 at 8:25
add comment
  • Never use MD5 or SHA1 hashing algorithms. Always stick to newer ones like SHA512
  • Always use a strong random generated salt
  • Never email your users their passwords, even in case of "Forgot Password"
  • Never use the mysql_* functions. They are long depreciated. Stick to PDO
  • Never store passwords in sessions or cookies
share|improve this answer
add comment

Here's one bit of advice: make sure your cookies can't be accessed with JS to prevent theft, see Protecting Your Cookies: HttpOnly article by Jeff Atwood

...Through clever construction, the malformed URL just manages to squeak past the sanitizer. The final rendered code, when viewed in the browser, loads and executes a script from that remote server.

...whoever loads this script-injected user profile page has just unwittingly transmitted their browser cookies to an evil remote server!

As we've already established, once someone has your browser cookies for a given website, they essentially have the keys to the kingdom for your identity there...

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.