I'm calling HTTP endpoints from another server. I control both servers. There could be more instances later, but I will control them, too. I want to authenticate the server calling the other one so that I know the requests are coming from a legit source.
My idea was that I would set the same secret environment variable to both servers. Each call would then include an Authentication header with:
timestamp,
salt,
HMACSHA256(
timestamp,
salt,
secret
)
which the receiving server would verify and dismiss the request if the timestamp is older than 10 seconds or if the HMAC does not match.
What would be the standard way of doing this and what are the pitfalls of my approach?
Related
We are building an android application and one of its features is to book a cab service provider's cab (say an Uber).
We have an application specific user ID. Let us call it AUID. To book the cab, the application would Post a request to server and send AUID along with other relevant information (like lat, long etc). How do I make sure at the server end that the request is indeed coming from the correct user and it is safe to book the cab? In the current form, if a third party gets to know the AUID of another person, the third party can book a cab on behalf of that person.
One of the solutions I thought of was using asymmetric encryption. The application would hold the public key and the server would contain the private key. Instead of sending the user ID to the server, we'll instead send an encrypted key where the key would be AUID + timestamp encrypted using the public key. We'll then decrypt the message using private key at server end to obtain the AUID. If the timestamp at server does not lie within a certain interval of the timstamp sent by the client, we reject the request.
Is this a safe enough approach? Is there any other practice widely followed for such scenarios?
What you propose is sensible: encrypt the AUID on the client app and verify on the server. As comments suggest, SSL is vital.
The problem is that if how to encrypt the AUID is in your app, it can be figured out by anyone dedicated enough.
You can drastically reduce the risks of fake requests by issuing a separate encryption key for each user. This means that if someone cracks your code, they can still only spoof from one account. However, once an attacker had decompiled your app, they could theoretically start new accounts, get a valid encryption key and spoof requests.
What you need for 100% reliability is some form of authentication which is not stored in the client app - like a password or TouchID on iOS or fingerprint api on Android M. So when a user orders a cab, they need to enter some piece of information which you also encode with the AUID and check on the server. That secret information is not stored in your app, so no-one can fake requests.
Requiring a password from a user is pretty inconvenient. Fingerprint scanning is much easier and probably acceptable. You could also use a trust system - if the user has ordered cabs before and everything was OK, they can order without special authentication. Using Trust together with individual encryption keys is pretty effective because anyone trying to spoof requests would need to do a successful order before being able to spoof - which is probably too much hassle for them.
I want to authenticate my users based entirely on cookies and sql db.
What I do is:
1. Once they login, I generate a random string, create a hash from it, save it in the database along with the user id and his IP.
2. I send the hash to the user as cookie
3. Whenever he wants to access something, I verify if his cookie hash matches the one on the server and also if his IP matches. Of yes, he is valid or else, log him out.
4. (As pointed by Akhil) If he clears his browser cookies or anything does not match the information on the database, I clear all the rows with his username and log him out.
Note: I use a session cookie for storing the random hash, which again is generated using the timestamp, and as long as time doesn't repeat itself(I believe), its random in the corect way.
Is this fine? How can I make it better?
Once they login, I generate a random string
Make sure you use a cryptographically secure method to generate the random string. Do not use mt_rand use something such as openssl_random_pseudo_bytes.
create a hash from it,
Make sure to use a secure hashing algorithm (not MD5, and at least SHA-2).
save it in the database along with the user id and his IP.
One thing to bear in mind is that some internet connections share IP addresses or will sometimes change the client IP address (e.g. AOL or mobile).
I send the hash to the user as cookie 3. Whenever he wants to access something, I verify if his cookie hash matches the one on the server and also if his IP matches. Of yes, he is valid or else, log him out.
It sounds like a good way of doing it and there are no flaws in itself. I would implement a session timeout mechanism. For example, store the date last used in the DB for a sliding expiration and the query will only query records that have not expired. You could have a background process that runs to clear out old, expired records.
Also, use HTTPS and set the Secure and HttpOnly flags on the cookie. This will prevent them being leaked over HTTP, but I would not go as far as disabling HTTP on your system as there are workarounds for an attacker if it is anyway.
I would not be concerned with the cookie being stolen by another user on the same machine. If the cookie can be stolen in this way then the user's machine is probably compromised anyway and you cannot make your system protect data that is outside of your control. You could however renew the token (random string) on a periodic basis giving the user a rolling cookie. You would have to ensure only one user can be logged in at once under the same account though for this to be effective.
Your method only makes sure that the user possess the random string you generated and is using the same external IP address. There exists several way of abusing this system:
if your website doesn't enforce HTTPS then a user connecting using an unsecured public WiFi network could be at risk: if another user of the WiFi network is listening to all the packets being sent on the network, he could intercept your cookie and use it to access the website as your legitimate user. Your server would be unable to differentiate them because they'll both use the same IP address... (There is a Firefox extension available which enable anyone to intercept such login cookie easily: http://en.wikipedia.org/wiki/Firesheep)
This system is also more generally vulnerable to man in the middle attacks (without HTTPS)
If your cookie is stored on the user computer's hard drive it could be reused by another user.
So to answer your question, your system can be deemed as secured provided a few conditions:
you enforce the use of HTTPS on your website (unencrypted HTTP connections should be refused)
your random string is truly random (there exist right and wrong ways of generating random strings in PHP)
your cookie has a short expiry and preferably is set as a session cookie.
You should take a look at the following related question providing details about the proper way of doing what you want to do: How to secure an authentication cookie without SSL
One cannot say this is "bad". But in Web Development, and specifically in its security domain relativity talks. I recommend you to download a CodeIgniter (google it for more info) Session Class (standalone version) and use it. The basic idea is the same as yours, but it is properly more mature since it is developed in such a famous php framework. You can do your DB operations within that class too, since it allows session saving to DB.
On the current project I'm working on, we have the following problem.
Our software is a Location Based Services platform and applications can connect and use our services through our exposed webservices using SOAP. Until now our platform was only used by internal applications, but now we would like to open it for third party applications. For that we need an authentication mechanism.
Because of our customers' infrastructure and load balancing solution, we cannot use HTTPS. The original idea was that applications can just use HTTPS and send the password we authenticate.
A solution would be the next:
The application has the password. The application generates a random string (salt) and creates a hash. Then the application creates an HTTP request sending the hash, the salt and a timestamp. This three is enough for us to authenticate, as we can generate the same hash and compare.
My problem is that for this we need to store the password in our database in clear text, because we need to do the same process using the given salt so we can compare the result and authenticate the application. Storing passwords in clear text is unacceptable.
Do you know about any authentication/access control mechanism that would fit this situation? Generally, do you know about any good books/sources about application authentication/access control mechanisms?
Any help is highly appreciated. Thanks in advance!
The application (client) can hash the password two times. Note that the server should generate the other random salt, not the client! Otherwise the attacker can log with this hash also. You can also make it safer by storing password specific salt in the database.
The protocol:
0) servers retrieves salt for that particular password from database, generates salt2, and sends both to the client
1) client sends hash(hash(password, salt), salt2, timestamp) and timestamp.
2) server retrieves hash(password, salt) from the database and compares.
Note that if you are on the network where attackers can not only sniff, but also modify the traffic, as Paulo pointed out, you should sign EVERY message: hash(hash(password, salt), salt2, timestamp, message) and check it at server. (E.g. for the case when the attacker could modify the message to contain delete command...)
Note that there is still a problem when user needs to SET/CHANGE the password safely. You cannot do it safely with just hash function over unsafe network, you need some kind of cipher/decipher.
Also note that the slower the hash function is, the safer (because of the dictionary attack). If you don't have access to special slow hash function, you may also call normal fast hash function 100000 times.
Instead of inventing your own solution, you should use an established one. SOAP has support for cryptographic authentication such as WS-Security - see Craig Forster's comment on this answer for suggestions.
The best choice in other cases is usually oauth; it provides both authorization and authentication, and deals with a lot of cryptographic issues that you're not likely to spot when building your own.
Using an authentication solution which does not contain an integrity check of the whole message (or stream) is insecure.
While the hashing solution originally proposed by Thomas T. (hash(hash(password, salt), salt2, timestamp), where hash(password, salt) is stored in the database, and salt2 is newly generated) makes sure than an attacker can't get the password (or any data which will be useful for logging in after the timestamp expires), it alone does not prevent an active attacker to hijack the session after the authentication, and send any SOAP requests wanted (and intercept the responses).
What would be needed here is some way to make sure that no data is changed. This is known as a message authentication code (MAC). The usual definition of a MAC includes some (shared secret) key and the message as input, and an authentication token as output.
The usual way to use this would be to do some authenticated key exchange at the beginning of the communication (using either a shared secret or some known public key), and then use a part of the now shared secret as the MAC key, which is then used to authenticate following messages.
(Doing this then essentially is a reinvention of SSL/TLS (or parts thereof), potentially doing the same mistakes again.)
If you have only one message to send, you can instead use the MAC as a kind of symmetric signature, using the password hash (salted and generated with a slow hash function) as a MAC key.
Another way to view this would be to take the message to authenticate as an input to the outer hash in Thomas T's authentication scheme. (Make sure to authenticate everything that is worth authenticating.)
I've read a lot of things about authentication in CouchDB, especially regarding the Cookie Authentication.
I'm still making some tests and all seems working well, for instance with this command :
curl -vX POST $HOST/_session -H 'application/x-www-form-urlencoded' -d 'name=foo&password=bar'
I get a Cookie that I can use.
But my point is, anytime I see think kind of sample on the Web, the username and password are always sent in plain text.
I'm really new to security but what's the interest of the Cookie Auth method if I first have to send my credentials in clear ?
Is there a way to send at least the password hashed ?
With something like that IDK :
curl -vX POST $HOST/_session -H 'application/x-www-form-urlencoded' -d 'name=foo&hashed_password=hashed_bar'
Cheers
Arnaud
If you send your password hashed than all the attacker needs to know is your hashed password so it wouldn't solve the problem of sending your password in cleartext - now you would have a problem of sending your hash in cleartext.
Also remember that even if that solved the problem you would still be sending your cookie in cleartext being vulnerable to session hijacking.
(There's also the HTTP digest access authentication but not without its own problems - but CouchDB didn't support it last time I checked anyway.)
What you should do is to always use HTTPS for any authenticated CouchDB access with any network involved, except maybe the 127.0.0.0 network.
(And yes, pretty much all of the examples on the web and in books show using basic or cookie authentication over HTTP which in my opinion is a disaster waiting to happen.)
Using Https is the right answer.
I'll add a clarification on the importance to compute a hash on the server side.
The hash is a one way function transforming the input into the key value stored in the server. If someone hacks the server and gets the hashed input (key value) he won't be able to deduce the input value from it to impersonate you.
If you compute the key value on the client side and no one way tranformation is performed on the server, it is equivalent to store passwords in clear text. Someone who managed to get a copy of the key value stored on the server can easily impersonate you by simply sending the key value.
Thus applying on the server side a cryptographically secure one way function (i.e.sha256) with a salt/random seed on the submitted password is required to secure the password database.
Obfuscating the sent password by hashing it, in addition to hashing it on the server side, won't help mutch if the sent hashed value is always the same. However spying data sent through an SSL connection is not trivial.
There is however a significant benefit to hashing password on the client side. A brute force attack on the server by trying to guess the password using a common password dictionary would become hopeless because the hashing on the client side randomized the password.
We may add some salt to the hash to protect against use of hashed password dictionary. When the user typed his user id, ask for the user specific salt value to the server. Then use the returned salt or hash seed value to generate the hashed password on the client side.
Brute force password guessing maybe hindered on the server side by increasing the time interval between retries. But this generally works for one specific connection. The attacker may reconnect after every two attempts. It is then required to keep track of ip addresses to recognize such type of attacks.
As of version 1.1, CouchDB, supports API access via HTTPS. Instead of using an HTTPS proxy, you can use HTTPS directly, protecting passwords transmitted over the wire. See the Feature Guide for 1.1.
The question says pretty much everything. My point is, is the user able to change his cookie's values in order to appear "logged", when he's not really logged?
Cookies aren't secure. As others here have pointed out, you shouldn't trust any data received from the client, period. That said, cookies are often used to store Session IDs for logged in users, which is sort of what you're asking.
Signing your cookies will help you detect if they've been tampered with on the client. Basically, you create a HMAC of the keys/values and a secret key, known only to the server. On each request, you re-compute the MAC: if it matches the previous value, all is well; if not, you reject the request.
For more sensitive data, you can optionally encrypt the cookies. Most web frameworks will allow you transparently do these using some kind of "middleware" external to your application code, so the signing/validation and encryption/decryption happens for each request.
Also, you should know that simply securing your cookies doesn't guarantee, er...security :) You might still be vulnerable to Cross-site Request Forgeries.
For more information on cookies, check out this article.
So then if I change the user_id=1 do I become the administrator?
What if i type this into the address bar:
javascript:document.cookie=user_id=1&logged_in=true
In general it is a horrible idea to re-invent the wheal. Especially when it comes to security, a cookie should always be a very large random value. Whatever platform you are using should have a session handler already built for you.
Usually a server generated token is stored in a cookie. When the cookie expires the token is lost and the user needs to sign in again. You can't fake the token. It's not a boolean value stating whether the user is signed in or not.
Anything you get from the client (including cookies) is unsafe. The safe way is to set a cookie with a random hash, log the hash in the database together with an ID and a timestamp (and perhaps even IP) and then check the incoming cookies against the stored hashes. If you set the cookies to expire after some time, make sure you also reject them on the server if they arrive when they should not.