I've just landed a PHP5 gig. I won't be handling the parts of the application that involve super sensitive data, but I still know embarrassingly little about security and encryption methods. I only know the very basics (don't store passwords in plaintext, don't allow users to run code using post data, etc). What do I need to know to keep my applications secure, and where can I learn it?
Learn the difference between hashes and encryption. Encryptions are generally two-way interpretations of a string. I can encrypt my password, and then decrypt it to plaintext again. The idea behind hashes are that they become a one-way 'encryption.'
On my sites I store passwords as hashes. Anytime a user signs on, I re-hash their provided password, test it against the hash stored in the database and approve if they match. I cannot send them their password if they forget it, since (generally) there is no way for me to know.Two different strings can translate into the same hash, which makes it (generally) impossible to find out what the original string was.
This is one issue that is good to get a firm understanding of, and discern when to use encryption vs. hashes.
Know not to write your own encryption functionality. An existing, trusted library is best way to go wherever possible. Avoid cool, bleeding edge technologies that lack many successful programmer-hours and user-hours behind them. Know not to trust the functionality you choose until you've thoroughly tested it yourself, first-person. Keep abreast of new developments which may antiquate your chosen functionality overnight. Know that just because you're using the best encryption technology available today that you've protected nothing if you leave the keys on the table (e.g., cleartext is not in a cache or stored in another table in the same database, private keys not left in the open)
Understand the difference between encrypting and hashing
Understand the reason for salts
Understand that HTTP is cleartext
Understand what HTTPS is
Understand that you will never (almost never) be able to create better hashing or encryption methods than what 3rd party libs and built-in libs already do
That technology is not the weakest link in security.
Employees the weakest link in IT security
Security's weakest link: People
Passwords: The Weakest Link?
Weakest Link Security
The Weakest Link
That it can be broken no matter what you do.
Where to learn about security: get Schneier's book Applied Cryptography.
The short answer
You can never be too secure
Use Salted Password Hashing for increased security
The longer answer (still not complete, though)
Security is not something to be learnt by a quick tutorial on the web. It requires in-depth knowledge of not only what vulnerabilities exist, but WHY they exist and HOW they work. One of the biggest problems (especially in open source), is that new methods are added all the time, therefore we must understand security concepts and theory.
Read books, take classes, and test the vulnerabilities yourself on a local machine. Then you'll slowly begin to grasp the concept behind how to secure a web application.
Check Out the following to start you off
Developer's Guide to Web Application
Security
Web Security Testing Cookbook
Applied Cryptography
Please pay attention to following points when you store passwords,
Hashed password is generally more secure because you don't have to keep a secret. However, it prevents you from using other hash-based scheme in your authentication flow. For example, you can't use HTTP Digest authentication with hashed password.
Simple hash is prone to rainbow table attak (http://en.wikipedia.org/wiki/Rainbow_table). Please add a non-reoccuring nonce to the hash or use the nonce as the key to HMAC. The nonce needs to be stored with the passwords. I prepend it to the digest.
If encryption is used, make sure a random Initial Vector is used so same password will be encrypted to different ciphertexts for different user. Otherwise, you are prone to pattern matching attack. MySQL has built-in encryption command. It doesn't inject IV so never use it for passwords.
Save key name/version with the ciphertext so keys can be rotated. Key-rotation is required for compliance with certain standards. Encryption without key information is impossible to decrypt when you are forced to change or rotate keys.
If you follow these advices, your passwords will be safe with any encryption/hash schemes.
Check out the Open Web Application Security Project. They have a lot of information on the current web app security issues and what you need to do to defend against them. OWASP is putting together a Development Guide that provides a lot of good information on web apps and web services development issues.
If you're looking at it from a PHP context, I'd recommend this book:
alt text http://ecx.images-amazon.com/images/I/51sKhc8YUlL._BO2,204,203,200_PIsitb-sticker-arrow-click,TopRight,35,-76_AA240_SH20_OU01_.jpg
Pro PHP Security on Amazon
The thing I really like about this book is it covers much more than just a list of the security-related functions in PHP. A large part of it covers general web security concepts and protection mechanisms. Permissions, principle of least privilege, encryption, hashing, cross-site scripting, cross-site request forgeries, session hijacking, etc. are all covered here, with examples of writing secure code in PHP.
Having taken graduate-level security classes in college, I'm impressed with the coverage in this book. I'd consider it required reading for any professional PHP developer.
First you have to get familiarized with this php methods:
MD5
SHA1
Here you have all cryptography extensions in PHP.
Related
I'm developing a password manager which has two forms of authentication.
First the user logs in using their password. I have this working securely using bcrypt.
The next part is far more tricky. The user's saved services are stored in a JSON format like so:
{
"name":"facebook",
"login":"myemail#email.com",
"signup-email":"myemail#email.com",
"password":"121654754321",
"notes":"my security questions answer is \"blah blah blah\""
}
I plan to store this in a DB table with two fields: id, and data(the AES256 encrypted JSON).
So far my design is using a 6 digit pin with a 32 char salt (unique to each user) to encrypt this. the pin is never stored. the salt is stored in the user table.
What are the possible flaws in doing it this way? Can anyone recommend a better way to implement this?
The risk
Your systems poses a great security challenge, since it acts as a central password repository, thus making it a choice target. Your best defence is ignorance: if you can't decode it, an attacker would have a hard time doing it too. Also, you don't need access to the data stored, but the user (and only the user) does.
What you've done/intend to do is on the right track since you don't have access to the full key. But why do you use a 6-digit pin instead of a "standard" password/phrase? You're responsible for the security of over 80% of the encryption key, and you've got all of them in one single place! An attacker just needs to figure out the (quite weak) pin to add to the salt in order to read the data. Plus, if your system gets compromized, one could manage to retrieve user requests in order to extract the pins and gain access to your user's accounts.
(some) Tips
What could you do then? I'm no expert in security, but I know some tricks. I'll try to answer to the best of my abilities.
Delegate decryption to the end-user
If the decryption key never gets close to your system, the data would be way safer. In order to retrieve a user's passlist, one would need to break into this user's computer and somehow retrieve the decrypted data off the page. That's totally doable, maybe easier than breaking into your server in some case, but this would only grant access to only one account. That's quite good for your other users :D
There's a growing number of applications using this approach to acheive similar results. You often see client-side AES decryption done with JavaScript for instance. The zerobin project describes such a mechanism.
Use strong encryption keys
As said earlier, leaving the user only 6 bytes in the encryption key isn't really robust. They need to be able to add as much entropy they like. Passwords with an upper size limit are often a bad idea (I personally hate those who tell me how short my password has to be).
Have a look at key stretching
(This point is related to the previous)
Key stretching describes an operation taking a standard "human" password and transforms it into a stronger encryption key. It has many applications, and is in use in several password managers of the kind you're building.
RSA's PBKDF2 is a well-known key stretching algorithm used in many security applications.
Side notes
Of course there are several other points you need to address in order to build a secure system:
Obviously, you NEED to enforce SSL (HTTPS) communications between your server and your clients ;
Access to your server needs to be well protected in order to protect the secrets used for encryption. Client-side decryption greatly reduces the threats posed by break-ins, but that doesn't mean they shouldn't be protected. This implies fine tuned firewalls, up-to-date applications, reactivity to security fixes, etc. ;
You need to teach a thing or two to your users about security. It's well known that both ends of a channel have to be secured in order for the channel itself to be secure too.
There are probably many other concerns, but hey, this isn't a lecture on security ;-)
The pin is known and inserted only by the user? Instead of using the 6 digits pin + salt for encrypting the data you can use PBKDF2 (Password-Based Key Derivation Function) to generate a secure encryption key based on that 6 digits pin (and the salt). This way each user will have a different (and secure) encryption key. Mind that along with this you should always use a different and cryptographically secure generated IV that can be appended (or prepended) to the encrypted data.
The security flaws of your implementation are that there is the possibility of not having enough entropy generated by your pin + salt and since you didn't talk about an IV I suppose you are not using one thus exposing possible patterns in your encrypted data.
For the security flaws my implementation has instead, depends on what are your security objectives. For most people PBKDF2 (+ one of the hash_algos() algorithm as per the PHP implementation) is secure enough, even if it is more exposed to attacks using FPGA or GPU clusters. If you want to avoid them, you can use bcrypt to generate a key that is harder to attack using these technologies.
I recommend you to read this answer that gives more in-depth reasons on why bcrypt is somewhat better than PBKDF2
I am creating a information system that will handle financial information, contacts, etc. I am developing the site from complete scratch using object oriented programming (classes, functions, etc). A majority of the data will be from a MySQL database. Users will be able to get and submit data to the database.
I am already using the hash function to encrypt data such as passwords, serial keys. I am also using preg_replace() function for all other data going to the database.
What other security measures do I need to take to insure that submitting and getting data from the database does not compromise security?
md5 is a cryptographic hash function. once hashed, it cannot be "un-hashed" back to the original value (one-way) as opposed to encryption which is two-way (encrypt-decrypt).
for security of your data, consider these scenarios and ways of prevention instead of just encryption:
cross-site request forgeries (CRSF) - prevent using form tokens
SSL connection (the "httpS://") to prevent data interception in transport
hash salting to further protect (but not totally) hashed passwords from dictionary attacks. weak and common passwords are the targets in this case.
hashing is not absolute. there is a limit to how many combinations of letters and numbers in a hash. at some point extremely different strings may have the same hash value. this is known as a collision
hashes are prone to brute-force/dictionary attacks. although hashes are one way, one can create a string-hash dictionary, match the hash and figure out the string behind it.
cross-site scripting (XSS) which can include (but not limited to) cookie stealing, click jacking, etc.
SQL injection - ways to trick your SQL when forms are unsanitized
expendable session ids to track user sessions - which should expire in a given amount of time, hence an auto log-out mechanism.
identify your user! user ip address, browser detection, etc to profile your user. any odd data (like sudden change in IP, location etc.) should be considered within a certain threshold. (facebook has this feature. i once accessed my facebook using a proxy - auto lockdown)
preg_replace() will not do much in terms of security. You should familiarize yourself with some basic security/crypto before doing this work. Also, consider the use of a standard cryptographic library for encrypting/decrypting data instead of arbitrarily using hash or regex functions.
Take a look at this: http://php.net/manual/en/book.openssl.php
First: good for you for giving attention to security issues. It's a big subject and one that many people overlook until it's too late. So, kudos to you for seeking more understanding about best practices. :-)
OWASP is a good resource for understanding web security issues.
Another good resource is the SANS report The Top Cyber Security Risks.
Specifically, Cross-Site Scripting (XSS) and SQL Injection are the top two security risks for most websites. You should read about how to design your code to minimize these risks.
I have also designed a presentation SQL Injection Myths and Fallacies that goes deeper into the nature of this issue and methods of defense.
Read the blog You're Probably Storing Passwords Incorrectly by StackOverflow founder Jeff Atwood.
I also cover SQL injection and password hashing in my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Hope you have good liability insurance if you are using md5 to secure financial information. Read about md5, http://en.wikipedia.org/wiki/MD5, paying close attention to the line
The security of the MD5 hash function is severely compromised.
You are doing it wrong, because:
md5 is considered broken,
preg_replace() will not give you much.
Consider using already developed, tested and secure frameworks (candidates include Zend Framework, Symphony, Kohana, Yii) for your system. You have a long way before you will achieve security at least nearly as good as standard framework's. Also consider using prepared statements instead of preg_replace() and salted sha1 instead of simple md5, if you still want to reinvent the wheel.
Furthermore:
secure your app against such acronyms as XSS, CSRF,
require SSL at all times (for every request, even for images / styles / scripts),
read security newsletters (you will need them if you want to build secure system for financial activities).
As others have pointed out, md5 is broken. Also, a SHA1 hash is very fast to compute which actually makes it worse as a hashing algo. Instead look at bcrypt. Assuming you're using PHP, the http://www.openwall.com/phpass/ is very a nice password to use that handles hashing and salting for you transparently.
Using preg_replace() for escaping data to the database is a very bad idea. Almost all databases include their own sanitization functions, PHP/MySQL is no exception with mysql_real_escape_string().
Some more points (please note none of these are set in stone):
Sanitize all input
Assume that everything the user sends to your server is designed to cause harm. This includes form submissions, but also URL routes, cookie values, server vars, EVERYTHING. Using a framework will often provide some insulation from this, automatically escaping a lot of data for you.
Escape all output
Assume that everything you display on your site is designed to cause harm. XSS and CSRF are amongst the most common techniques for attacking websites. Escape all text that you output to the browser. Look into using nonces to mitigate attacks.
Use TLS/SSL
If you want to protect your users data enroute, get yourself a signed SSL certificate and set it up. This allows visitors to go to https://yoursite.com securely (or at least more securely if they're the kind of person who does internet banking on coffee shop wifi).
Use a framework
Everyone begins by writing their own framework because they know how to do it right, or don't need the extra complexity or whatever reason they come up with. Unless you're writing a super-specific-niche-application for which PHP probably isn't the right answer anyway, use a framework. I prefer http://kohanaframework.org/, but there's a whole range out there from http://codeigniter.com/ through to http://framework.zend.com/. Frameworks handle session encryption, database escaping, input sanitization and more for you, and because they're used by many people the chance of a bug is much less than code that only one person has worked on.
Secure your infrastructure
This one tends to fly by most people, but make sure you take some time to look at the server(s) you're running on. Are you on a shared account? You don't want to be storing financial information on them then (in some countries it's even illegal too). Apply security patches for your OS/software, make sure you haven't left an old upload script lying around, check your file permissions, use SSH with keys and turn off password logins. Attackers are always looking for the easiest way in.
At the end of the day, the only way to stay secure is to sleep with one eye open, totally paranoid. Watch your logs, install Nagios and set-up some alerts, hire a professional to do a security audit. There's no such thing as 100% secure, but knowing that is half the battle.
I am an experienced PHP developer and I want suggest to you to take a look to this project OWASP_Development_Guide. Every web developer should be use it as a bible. It has been very useful for me and I hope it will be the same for you.
Here a brief description of the document:
The Development Guide provides practical guidance and includes J2EE, ASP.NET, and PHP code samples. The Development Guide covers an extensive array of application-level security issues, from SQL injection through modern concerns such as phishing, credit card handling, session fixation, cross-site request forgeries, compliance, and privacy issues.
Hey all of you PHP geeks, and hackers, if some of you listen ;o)
I'm creating a login system, for some upcoming site, which a lot of people will be using, I'm sure - or at least I hope so :)
So, my question is: WHEN is my login system and stuff like that secure enough? Is it ever gonna be "secure enough"?
I got form validation, where I check, what's send to the database. My passwords are md5 hashed. And people are told to create strong passwords.
Are there something called: "Secure enough"?
I'm thinking: All the big sites like facebook and stuff like that, must be having more then this? Is it something that I need? And if so, how?
Some links and stuff like that, would be nice. Thanks in forward :o)
Security is a never-ending process. Your code will never be "secure enough." You should apply all of the security techniques you know, and learn those you don't. Then stay current, monitor your logs, and update your code as necessary.
In general, a login system is a bad place to reinvent the wheel. There are plenty of commonly-used systems available that have been checked and rechecked by many eyes, and are probably more secure than any you or I will be able to write ourselves. This is a good opportunity to take advantage of the "wisdom of the masses" and use a well-tested third-party system.
If you're worried about security, you shouldn't use md5 hashes to store your passwords. Md5 is designed to be fast, meaning that if a hacker gets your hashes, they can be cracked quickly. You should use something like bcrypt for storing passwords.
http://codahale.com/how-to-safely-store-a-password/
You have all kinds of security vulnerabilities:
SQL injection
Cross-site scripting (XSS)
Cross-site request forgery (CSRF)
Session attacks
Using nonsecured connection (non-HTTPS)
Passwords in plaintext/weak encrypting
Weak passwords
I recommend you to look them all up and choose a framework for developing web applications that already resolves all or most of these issues for you automatically.
BTW: md5 is not safe anymore, I recommend you to use hash_hmac('sha256', $password, $salt) which also takes care of salting.
It's kinda ironic how I found this after reading over my own question.
Having written a couple of login libraries myself, heres the advice I can give:
Validate form input (server side), to prevent XSS (cross site scripting), CSS injection, & SQL injecttion
for correct length
ctype
If ctype is not alpha numeric add slashes
Add html entities to prevent XSS, if HTML is imperitive use an HTML filter
Use a password strength API to ensure decent passwords
Hash the passwords, and don't forget the salt! Also MD5 is weak, use SHA-256
Log the IPs incase anything goes wrong
Make backups of your SQL databases
With all that said I still suggest you use openID or a php library/class.
On my latest little website, I hope I somewhat avoid some level of complexity with security by using openid, instead of fully implementing my own auth system. Thus, I don't really store passwords.
I believe MD5 in considered weak, but I'm not convinced that really matters - if you can keep your hashes safe - by using openid, I took that component out of the equation for me. Granted, if openid gets owned, then, yeah, obviously that's a problem too.
Still, you want to do a fair bit of input filtering. Do what you can to limit the input you do take in, make sure you're running on a fully patched server (check in often) and be sure you're running processes that can't access things they don't strictly need access to. So, for example, if your web service runs as root, well, then you deserve to have your server turned in to a palace of pr0n.
Just some thoughts - granted, I'm not exactly a security guy.
I have read a lot of these but I have a few questions that I think aren't adequately answered. If there is a link, please refer me to it and I'll be grateful.
I have a PHP login system and it will be a community site where users will register and login. Now I need your help in these things:
What hashing algorithm is enough for most of sites (WP, Joomla, FB, et al)? simple MD5 with salt? or what?
What are attacks I have to deal with that are, apart form top secret sites, existing in community driven site (Juts list of them and may be short explanation of what they are)
What is the best among PDO and MySQLi (I saw PHP recommends the latter but I would like to hear from you guys)
Thanks a lot,
Stefano
1. What hashing algorithm is enough for most of sites (WP, Joomla, FB, et al)? simple MD5 with salt? or what
You should use MD5 with a salt as a bare minimum. Ideally you should use a different hashing algorithm, as MD5 has been proven to be fairly insecure in comparison to other available algorithms. Have a look at the different available ones here hash(). Personally I would use SHA512, with a per-user salt.
Using a per-user salt means that any attacker who gets ahold of your database would have to crack the passwords on a per-user basis, rather than cracking them all at once.
2. What are attacks I have to deal with that are, apart form top secret sites, existing in community driven site
The main attacks would be SQL Injection attacks (either to steal a database, or to inject malicious code into your site). This can also be coupled with a Cross-Site Scripting attack, which could allow an attacker to place their own code onto your site (such a <script> tag), to infect users with viruses.
These two attacks can be mitigated by escaping any variables that are going into your database, and also stripping out any HTML (or special characters) in any user-submitted data.
3. What is the best among PDO and MySQLi
This question I will leave someone else to answer - I'm not as clued up on the differences.
Hope this helps
2) You need to be particularly careful of SQL Injection attacks and XSS (Cross Site scripting). See https://www.owasp.org/index.php/OWASP_Top_Ten_Project for more attack vectors.
3) I've only used MySQLi but thought it was a decent library
I don't think MD5 is very good, hence this MD5 decoder: http://www.md5decrypter.com/
Personally, I use SHA1. The SHA1 decoder I found online does not work. Some say that SHA1 is slower but I cannot understand why you would care if you are just using it for logging in.
Mysqli should prevent injection attacks.
Be sure not to give away too much information on failed log in attempts.
Example of bad: "We recognize your username but your password is incorrect"
Example of good: "The username and password do not match" --> use this for all failed logins, even if the username is NOT in the database.
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...