I'm building a REST API in PHP and I'm currently working on the security side of things.
I have read a lot about network authentication and implemented some suggested strategies.
I'll explain what I've done so far and where I got stuck.
To prevent a hacker from knowing all users' passwords if they got their hands on my database, I keep only a hashed version of the passwords in db. I use the php password_hash() function that creates a salt automatically. Also, the hashed password expires 30 minutes after logging in with the original password.
To prevent sniffers from seeing the hashed password in request headers, the client sends several headers in every request: a timestamp, some random string and a hashed checksum of the combination of hash+timestamp+random_string+url.
Now, my question is what happens when the hash token expires (after 30 minutes)? The user now needs to send their original password to get a new token, but the server only keeps a hash of the original password. Using the checksum method wouldn't work because the server needs the original password to get the right result for hash comparison. So it has no way of knowing the user password is correct.
P.S. I'm not worried about replay attacks at the moment, but if I were to implement security against this kind of attack, would I have to keep a history of all Nonce strings at the client side and all cNonce strings at the server side?
Thank you.
EDIT:
The documentation states that password_hash() uses bcrypt algorithm which handles the creation of a salt automatically.
As for the checksum string, I hash it using the SHA256 algorithm.
Now, my question is what happens when the hash token expires (after 30
minutes)? The user now needs to send their original password to get a
new token, but the server only keeps a hash of the original password.
Simply use the password-verify function to validate the plaintext password sent to you by the client matches your stored hash in your database.
$password is the user supplied password (not a hash).
$hash is the password hash stored in the user's database record, containing the algorithm, salt and hash.
Complexity is the enemy of security. Keep things as simple as possible. Ensure your connection is over HTTPS - this will encrypt and verify the password data in transit and HTTPS uses TLS/SSL which also prevents replay attacks.
Related
Please follow thread below in comments for further clarity if anyone else has the same question
I'm not very knowledgeable in web security setup. Is it reasonable to store a hashed username and password in a php file inside a variable?
For instance I have a login form, on submit it hashes the username and password into a cookie. Than matches the hashed cookie to the hashed variables on the sever to allow access.
These are also one way hashes.
The problem is not in storing the hash in PHP itself; although a database is generally recommended.
The problem is using the hash as a plain-text password/secret; this is no different than any plain-text password. If someone was able to view the PHP source code they would have the hash and thus 'password'. Remember, password hashes are not secrets even though they should generally be treated confidentially1.
Instead, accept the username/password as plain-text - although, do use SSL to encrypt the password over the connection - and verify this against the hash2. At no point is a hash from the user trusted - as it cannot be proved that it was generated from an actual secret.
Once the submitted username/password has been validated on the server against the stored hash then a session nonce is established; it is this unguessable per-session secret that is then used to re-validate/authorize the user each subsequent request3. This is 'automatically handled' in PHP when creating a new session.
See the "Do's and Don'ts" and "Don't write your own password checker" answers, which are especially pertinent.
1 In real-world applications there should be a high priority given to confidentiality and preventing unauthorized disclosure. This is because most user choose stupidly short or common passwords (at least when given the chance) that can be brute-forced quickly. This can be mitigated by encouraging secure passphrase usage and secondary validators.
2 The newer password_verify / password_hash (or an equivalent library / backport) functions should be used as these correctly handles basic details including
Applying a correctly generated salt which prevents rainbow table attacks, and
Using an appropriate (ie. bcrypt) hash function which mitigates brute-force attacks on strong passwords/passphrases.
(If planning on using SHA - don't! - for hashing passwords, stop and read the links..)
3 Unlike using a hash-as-a-password this is only susceptible to session-hijacking, even if the hash in the PHP source code were leaked: it is also unique per-session. The attacker needs access to the client's cookies (via local access or Cross-site scripting (XSS); XSS can be mitigated with HTTP-Only cookies) or a method of intercepting the HTTP request (which can be mitigated with HTTPS); then, within that session, an attacker could impersonate the authenticated client.
As a long aside: if there was a need to store data in cookie (or otherwise sent back with a server request) and to ensure that it came from the server to begin with, one solution would be to use an HMAC as an authentication code. But this does not apply here.
User name/password authentication has a well tested solution that should not be modified.
When you store a password, use PHP's password_hash function.
password_hash("rasmuslerdorf", PASSWORD_DEFAULT)
is a typical use case. This creates what people loosely refer to as a hash. It's not really a hash as it uses bcrypt, a key derivation function, but everyone refers to them as hashes. Store the password alongside the username. You can do this in a database or a flat file. You should do your best to keep this secure to prevent offline attack, but this hash is relatively secure against all but a very determined and well equipped attacker.
When the use logs in, pass their username and their clear text password to the server. The server should obtain the user's stored password hash using the username as the key. Then the server validates the password using PHP's password_verify.
All you've done here is substitute one password for another password. It is vulnerable to replay attacks. And that you are storing the effective password in a cookie opens up a world of exploits.
There are lots of good tutorials on PHP authentication on the internet, and a few bad ones. You should:
Use PHP sessions (with a session cookie)
Use https (and HSTS)
Send the submitted values serverside in a post for validation
On the server lookup the hashed password and random salt stored against the username
Hash the submitted password with the stored salt
If it matches the stored hash you have a successful authentication; regenerate the session id and continue.
Otherwise track and limit the number of failed attempts to prevent brute forcing.
provide an explicit logout function which removes the data in $_SESSION
Not enough but it's good as start.
Cookies still a security problem as it can be used in attacks since the hash is stable result whatever it was MD5 or whatever.
In Wordpress they use a good strategy which is "Salt Keys" this makes application secure and hard to get cracked
I'm designing a REST API and I have some problems in terms of security for authenticating the user.
For authentication I don't want the password to be send across the network in plain text.
To bypass this problem I could send a SHA-256 hash of the password (with the username as salt), so the password is never sent in plain text. In my database I will be storing the following hashes: SHA256(password + salt) and I'll compare if both of the hashes match.
The problem with this option is that I'll have a hash computed with a fast hash algorithm and the salt is not random.
In security the best practice is to use a slow signature algorithm, with a random salt (like bcrypt).
The slow algorithm is not a problem, i could use bcrypt on the client side, but for the salt i don't know what to do:
Bcrypt need a salt with a defined size so i can't put the username
If i'm using a random salt, how the client will know the value of this salt before computing the password's hash?
So i can see 3 options, but none are sastisfying:
I send the password in plain text (I'm using SSL) and i store bcrypt in the db => still vulnerable to man in the middle
I use SHA256 and send the hash where the salt is the username (still using SSL) => the hash in the db are less secure
I use bcrypt and I have a two step process: i ask for the saltfor a given user and then send the hash of this user (still using ssl) => by trying to log in with an other username i can obtain his salt, not awesome
Is anybody has a better solution or some advices?
I think you might be conflating/confusing a few issues here:
If you're storing the hash(password + username) on the server, and authentication involves sending hash(password + username), you haven't really achieved anything better than just storing the password on the server. The goal of only storing hashes long-term is that if you have a data breach (i.e., an attacker gains access to the database) they still can't produce the correct value to authenticate. But if you're doing a simple comparison, this is still an issue.
The correct use of hashing+salting is: (1) Server stores tuples of (Salt, hash(Password + Salt); (2) User sends (claimed Password); (3) Server computes hash(claimed Password + Salt); (4) if hash(claimed Password + Salt) == hash(Password + Salt), then they're authentic. In this way, even if an attacker gets access to the database, they can't produce a claimed password that such that hash(claimed Password + Salt) is valid.
Sending a plaintext password via SSL is not "in the clear". Per #NullUserException's comment, unless the attacker has broken SSL. Only the server will be able to obtain the value of the password (assuming the server's public key is valid, which is a whole 'nother story).
Hope this helps!
There a couple of advantages to the approach of hashing on the client side. One of them is the server never gets the real passwords, so if the server is compromised in any way, it still won't get the real password. The other one is, it can lighten the load on the server side if you're planning to use slow hashing.
However, hashing passwords is designed to protect you in case the database is breached and hashes are stolen. This means if someone gets a hold of the hashed passwords, they could still impersonate users by sending the hash. The implication is, even if you hash on the client side, you still need to re-hash on the server.
The other potential downside is that this could alienate part of your userbase that doesn't have JavaScript enabled.
To address your points:
Bcrypt need a salt with a defined size so i can't put the username
Don't use the username as a salt. Salts should be unique, and a username (and derivations thereof) is certainly not unique. By unique I don't mean just unique to the server, but unique everywhere. Use a cryptographic nonce instead.
If i'm using a random salt, how the client will know the value of this salt before computing the password's hash?
Just have the server send the salt (nonce) beforehand. You could do this on the client as well, but the Javascript doesn't have a CSPRNG as far as I know, and you'd still need to send the nonce back to the server.
I send the password in plain text (I'm using SSL) and i store bcrypt in the db => still vulnerable to man in the middle
SSL was designed to prevent man in the middle attacks. Unless it's broken somehow, that's not going to be a problem.
I use SHA256 and send the hash where the salt is the username (still using SSL) => the hash in the db are less secure
Don't use username as a salt. And like I said before, you have to hash on the server side regardless of whether or not you did it on the client side.
I use bcrypt and I have a two step process: i ask for the salt for a given user and then send the hash of this user (still using ssl) => by trying to log in with an other username i can obtain his salt, not awesome
Not awesome indeed.
Make the salt constant, let's say make it a hash of the username. So hash_val = HASH(HASH('username') + 'password') is stored server-side.
For authentication your server sends a single-use random value, ie: nonce = HASH(RAND())
Your client computes the following based on the input credentials client_hash = HASH( nonce + HASH(HASH('username') + 'password')) and sends it back to the server.
The server perfoms the same operation, compares the resulting hashes, and discards the nonce.
In this way the hash sent over the wire is used only once and you're protected from 'replay' and MITM attacks.
Also, look into something like PBKDF for storing passwords rather than just hashes, it makes both bruteforcing and rainbow tables completely impractical. Here's the PHP implementation I'm using since it's not in PHP yet.
If possible create API Key and secret key (API username/password, obviously unique for each user) to use API. You should give an option in your site interface to activate/de-active the API access as well as option to re-generate the API Key and secret key. Here, on this interface users will see the API/Secret key of the API.
I'm knowing this site http://www.openwall.com/phpass/, but idea is on salt on mainly system.
Example, ZEND use system('uname -a') and it's hashed to md5() for using ROW LEVEL user SALT encryption. This is combination of user password, user login name/email address and server name as sha1/md5/...
But, my idea is generate DYNAMIC SALT instead STATIC SALT such as system('uname -a'). Example, every time when user is logged in, SALT has been changed but not user password.
For more security reasons, i'm needing dynamicaly changes salt on database or external file on daily basis or using third-party such as checking data from another server for salting?
What are best method for securing user sensible data on users database table and currents login. Cookie also is very bad secure options for me. It's must works such as PayPal API Tokenize and user id...
I'm using current:
salt from every user
salt from system hashed
hashed combination of user password, user salt and system salt
SHA-512 crypt() or bcrpyt() class
dynamically salt ? idea?
You are doing it wrong.
I think you are missing a key fact about re-hashing the password. To do it, you would have to store it in a recoverable form. Thus, creating even greater security risk, if system is compromised.
Here is what i would do:
make passwords expire in 60 days (or, you can choose some other number, just not too often).
each time user sets new password, you generate a random salt
build hash with crypt(), using CRYPT_SHA512 or CRYPT_BLOWFISH hashing algorithms
set a bit higher amount of rounds .. 20'000 should be enough
store the whole result that crypt() returns in the hash field in db.
Also you might benefit for reading: Properly Salting Passwords, The Case Against Pepper.
Changing the salt doesn't improve anything.
The point is: you always need to store salt and hash together somewhere because when you compare the password input with the hash you need to hash the input - obvious, right?
So this is the point: even if you change the salt after every login and do some weird re-hashing of the password it changes nothing because as soon as an attacker gets the database he has both hash and salt (if it's stored there together, which is necessary if you always use a different salt for each user which is something you should do).
A far more better way is extending the hashing by using 1000-10000 rounds of hashing as well as a long salt (you can easy use 512 bytes for the salt). These are better tip's than doing some re-hashing.
But anyway: if you really want to improve your PHP application you should focus on avoiding security issues like SQL injection, XSS, CSRF, RFI, LFI, file disclosure, RCE, etc - if an attacker gets access to the server he can simply backdoor the login script to send him an e-mail containing the plaintext credentials every time someone tries to login. (Well, you can also avoid this if you use a challenge-response authentication implemented in javascript like CRAM-MD5 http://en.wikipedia.org/wiki/Challenge-response_authentication or using RSA (also implemented in JS) to securely send login data).
Salt is only used to prevent against precomputation attacks, such as Rainbow Tables. Thus if someone wants to bruteforce the hashes, they actually have to compute them one at a time at runtime. (and not merely do a lookup in a database of pre-computed hashed values)
It's not really clear what the problem is that you're trying to solve. You just say:
"For more security reasons, i'm needing dynamicaly changes salt"
If that problem is precomputation attacks, then just have a normal salt. If it is not a precomputation attack, then salt is almost surely the wrong solution.
I'm working on an application for iOS which will have the user fill out their password. The password will then be posted to a PHP page on my site using either POST or GET. (It must be plaintext because it is used in a script.)
Besides HTTPS, is there any way to secure the password? Encrypt it in Obj-C and then decrypt it in PHP?
NOTE: The username is not sent... only the password is posted to the server.
EDIT:
To clarify, David Stratton is correct... I'm trying to prevent malicious sniffers in public locations from simply reading clear text passwords as they are posted to the server.
Challenge response outline
Lets assume you have one-way hash function abc (in practice use md5 or sha1 a cryptographically strong hashing algorithm for PHP see: password_hash).
The password you store in your database is abc(password + salt) (store the salt separately)
The server generates a random challenge challenge and sends it to the client (with the salt) and calculates the expected response: abc(challenge + abc(password + salt))
The client then calculates: abc(user_password + salt) and applies the challenge to get abc(challenge + abc(user_password + salt)), that is sent to the server and the server can easily verify validity.
This is secure because:
The password is never sent in plaintext, or stored in plaintext
The hash value that is sent changes every time (mitigates replay attack)
There are some issues:
How do you know what salt to send? Well, I've never really found a solution for this, but using a deterministic algorithm to turn a username into a salt solves this problem. If the algorithm isn't deterministic an attacker could potentially figure out which username exists and which do not. This does require you to have a username though. Alternatively you could just have a static salt, but I don't know enough about cryptography to assess the quality of that implementation.
Reconsider not using HTTPS. HTTPS a good defense against a number of attacks.
There usually isn't a reason to transmit a password. By transmitting passwords, you are sending valuable data and their is extra risk associated with it.
Usually you hash the password and submit the hash. On the server side, you compare the hashes, if they match, great.
Obviously with this approach, the hash is important, and you have to secure against a replay attack. You could have your server generate a crypto-secure one-time use salt, pass that to the client, salt and hash the password, and compare the hashes serverside.
You also need to guard against a reverse hash attack on password. IE, I have a hash, and I can compare it to a bunch of pre-generated hashes to find the original password.
You could encrypt at the device and decrypt at the server, but if the data going across the wire is sensitive enough to warrant that much work, then IMHO, I believe you're better off just using https. It's tried, true, and established.
It's not perfect, mind you, and there have been successful attacks against older versions of it, but it is a heck of a lot better than "rolling your own" method of security.
Say your key gets compromized, for example: If you're using https with a cert from a trusted authority, then you just buy a new cert. HTe deveice, if it trusts the authority, will accept the new certificate. If you go your own route on it, then you have to update the keys not only on your web server, but at the client as well. No way would I want that sort of headache.
I'm not saying that the challenge is insurmountable. I am saying it may not be worth the effort when tools already exist.
I don't really know even what questions to ask here. My problem statement is simple: I need to store a password on the DB with a salt, validate an entered password against the stored password, and authenticate the password using a random challenge word whenever a user tries to log on. I am using php/javascript.
In trying to figure this out, the problem I am having is that if I pass up a challenge word in an html form, then hash the entered password with that word, I can authenticate the password on the server, but I can not separate the password from the challenge word so I can validate it against the salted password on the DB. If I send the password to the server in the clear or hash it without a challenge word, I can validate it but now I can not reliably authenticate it.
I think I need a 2 way algorithm of some sort so I can encrypt it with a key, and then authenticate the key while validating the password. How do I do it? or if it can't be done then what should I be doing?
Encrypting a password with client-side scripting is generally a bad idea. The proper way to do this is to use SSL.
Also, never store password in cleartext. If you must use a method like the one you describe above, hash the password twice: once for storing it in the database, another time for the two-way authentication.
To store a password, generate a random salt. Store HASH(password+salt) and salt. (Either the server or the client can do this computation.)
To perform an authentication, the server looks up the salt and HASH(password+salt). It then generates a random challenge and sends the salt and the challenge to the client.
On the client, prompt the user for the password. Compute: HASH( HASH(password+salt) + challenge). Send it to the server.
On the server, you already have HASH(password+salt) and you have challenge. So you can also compute: HASH( HASH(password+salt) + challenge). Compare this to what the client sent you. If they match, the password is correct.
Note that this is vulnerable to a MITM attack, so it should be used over a connection that is itself protected from a MITM, such as an SSL connection.