I'm new to cryptography and I'm trying to prevent against man-in-the-middle-attack in a web service I'm developing. The way the web service work is that a user registers on the service using his email address and password and creates an application. Each application is given an application id and an application key. The application id is public (that's how the public communicates with that application) but the application key is private. The user credits his application by loading a pin (a 16 digit numeric string). Loading the pin is done via a HTTP Get request.
Now here is my question: how can the user do a HTTP GET request with his application id (the way the server identifies the application) and his application key (the way the server authenticates him) without compromising his application key?
Because our server has SSL (and I read that SSL protects against man-in-the-middle-attack), I was thinking about simply having users submit their application id and application key as parameters in the GET request, but after reading around, I decided this may not be secure. This is also because after doing the HTTP GET request to load the pin, the user may configure his account that we submit the server response via another HTTP GET request to a URL of his choice. And since we want to do an echo back of his application id and application key so he can authenticate that the request was really from us, I was worried his key might be compromised.
So I decided we should have the user do a md5 hash of his app id and app key to provide a hashed parameter and submit that instead of his app key in the GET request. Then on our server, since we already know the user's app id and app key, we can simply do an md5 hash of both and compare it with the hash parameter the user submitted. But then I also thought that may be insecure because if someone intercepts the hash parameter, the attacker can use that same hash parameter to submit several requests since the app id and app key is static. So in the long run, the hash parameter is no different from the app key.
Now I'm thinking, we should have the user do a md5 hash of his app id, his app key and the pin he wants to load to get the hash parameter. This way, since the pin is always different each time, even if an attacker intercepts a request, the authentication process would not be compromised for other requests because the attacker would not be able to reuse that hash with other requests.
For example, if a user has the following credentials:
1. app_id: 1234
2. app_key: bghuTHY678KIjs78
And a user wants to load the pin: 1234567890123456
He generates the hash by doing an md5 hash of "1234:bghuTHY678KIjs78:1234567890123456". That gives him 210a4c92d85473af9d5f48b4ee182ddd. Then he does a HTTP Get request to the address below:
https://example.com/process?app_id=1234&pin=123456789012&hash=210a4c92d85473af9d5f48b4ee182ddd
Is this method secure? Or should I simply just have the users submit their app id and app key in the HTTP GET request since we have SSL?
The user secret should never be sent over the network. Instead, ask the user to sign his requests using his secret. HMAC is the relevant algorithm.
By the way, MD5 is obsolete and insecure for all crypto needs.
Use Secure Remote Password (SRP6a) and register a password verifier and salt for the 16 digit pin. The pin you never send to the server (you can store it in browser local storage for convenience of the user). Then authenticate the client using SRP6a which results in a strong shared secret session key for each successful authentication. Then use HMAC SHA256 to sign API calls using the session key. See the thinbus-srp JavaScript library and its demos of using SRP6a to authenticate resulting in a session key. See the JWS "HS256" (HMAC with SHA-256, 256+ bit secret) algorithm and any library implementing that as an example of signing a web API with a shared secure key.
The SRP6a authentication protocol is a secure zero-knowledge password-proof where the server does not know the password. The server issues a random challenge to the client which generates a password-proof based on the challenge. The server uses the verifier the client provided for their password to check the password-proof. If the 16 digit pin uses uppercase letters like a standard software license key it is infeasible to run a dictionary attack on the verifier. Use the modern browser webcrypto secure random number generator to generate the pin at the browser. Even you won't be able to obtain the password.
The overhead of using SRP6a to authenticate is that you need the client to first fetch the server challenge. The benefit for your use case is that if the client provides a good password-proof based on the challenge the both the client and server share a secure session key. No-one intercepting the traffic can know the session key. With that shared secret you can sign and verify every API call and verify the signature at the server. No-one intercepting any part of any exchange between you and the client end-to-end from registration through to usage can gain any advantage.
Related
I have a requirement to secure my JAX-RS resources and only accept requests that originate from authorized mobile applications. Is this possible? How can this be done?
All of my resources are protected already with user authentication, the goal here is to reduce user ID fishing attempts. I know one solution would be to keep the response error with an invalid user ID generic, but the application is very large and at the moment this isn't possible.
One idea I came up with is to use JWT tokens signed with a shared secret. Then I could add an Authorization filter on the server to check the signature. If it doesn't validate then discard the request. Does this sound like a viable option?
My concern is the security of the shared secret on a mobile device, could it be compromised with a rooted device?
Using tokens is the preferred way. But the secret key is not shared. Only the server has access to it. That secret key is used to generate the message authentication code(MAC) of the JWT. Since secret key is only known by the server, no one else can generate a JWT with a valid signature. Secret may be persisted or application scoped.
Once a client is authenticated using credentials, server must send a signed JWT to the client.
That JWT must contains necessary information to identify the client and state(if necessary).
Then client send that token in a header field along with all the other requests.
Server validates the JWT using secret key and process the request.
Even though client can change the JWT body, he cannot get it verified. That's the whole point of using a signature.
I would like to implement authentication for a web app api using a similar technique to the AWS Signature Process. The server will render html for user web browser clients. The app will also return json for non-browser clients (iphone, android, blackberry, ipad, etc.) I am trying to write authentication code that will allow a single user to be signed (remembered) in to the app from multiple clients. Logging out from any one client should not revoke the token/cookie for all the other clients.
When a user sign up or logs in, how does a client (native mobile client or web browser especially) get the private/secret key? My understanding is the key should not be sent over a network? Can it just be sent over ssl?
For browser clients is the private key stored in a cookie or in some other form of local storage?? I.e. for generic api token authentication would the token be stored in a cookie on the browser and in a db on the phone
When the client is a browser, how does it sign a request signature with private key? i.e. generate a hash of the id + private key + request. For native mobile clients or server to server communication I believe there are libraries that can be used to generate a signed request. When the request originates from a browser how does this work?
If a user is required to be remembered on multiple clients (tablet, iphone, work computer, home laptop, etc.) would the user need to have a set of private keys/tokens for each client?
I would have used Devise with Rails but it requires you to write your own token authentication code.
I am also looking at OAuth, which questions 2 and 4 both apply to, but would really like to investigate the AWS method.
You start with a solution (HMAC) but I do not think your "problem" fits your solution :).
I'll try to answer this to the best of my knowledge.
To my understanding ,a private key would be derived of a public key that is shared. Or you could share a private key, but only if authentication is already established (in other words: you already know for sure who you are talking to) and the connection is secured (encrypted). You still will be vulnerable to man in the middle attacks though.
I'm not sure. A cookie is not secure. Steal the cookie, steal the private key. My preference would be a locally stored key(file or storage), encrypted by a password.
You would have to devise some sort of a client side library (in javascript or something like it).
Yes.
Why share keys when you could share a token, like oAuth implements. The token is generated by your application and is valid for a limited time. This way you share access, but not the means to aquire access to your application. You login with username and password (or keyfile) and you get a token that grants you access untill the token is revoked.
I'm implementing a REST web service using C# which will be hosted on Azure as a cloud service. Since it is a REST service, it is stateless and therefore no cookies or session states.
The web service can only be accessed over HTTPS (Certificate provided by StartSSL.com).
Upon a user successfully logging into the service they will get a security token. This token will provide authentication in future communications.
The token will contain a timestamp, userid and ip address of the client.
All communication will only happen over HTTPS so I'm not concerned about the token being intercepted and used in replay attacks; the token will have an expiry anyway.
Since this is a public facing service I am however concerned that someone could register with the service, login and then modifying the token that they receive to access the accounts of other users.
I'm wondering how best to secure the content of the token and also verify that it hasn't been tampered with.
I plan on doing the following to secure the token:
The client successfully logs into the service and the service does:
Generate a random value and hash it with SHA256 1000 times.
Generate a one-time session key from private key + hashed random value.
Hash the session key with SHA256 1000 times and then use it to encrypt the token
Use private key to sign the encrypted token using RSA.
Sends the encrypted token + the signature + the hashed random value to the client in an unencrypted JSON package.
When the client calls a service it sends the encrypted token and signature in an unencrypted JSON package to the service. The service will
Recreate the session key from the private key + the hashed random value
Use the private key to verify the signature
Use the hashed session key to decrypt the token
Check that the token hasn't expired
Continue with the requested operation...
I don't really know anything about encryption so I have some questions:
Is this sufficient or is it overkill?
I read that to detect tampering I should include an HMAC with the token. Since I am signing with the private key, do I still need an HMAC?
Should I be using Rijndael instead of RSA?
If Rijndael is preferred, is the generated IV required for decrypted? i.e. can i throw it away or do I need to send it will the encrypted token? e.g. Encrypted Token + HMAC + IV + hashed random value.
Since all communication happens over HTTPS the unencrypted JSON package isn't really unencrypted until it reaches the client.
Also I may want to re-implement the service in PHP later so this all needs to be doable in PHP as well.
Thanks for your help
You are really over-thinking the token. Truthfully, the best token security relies on randomness, or more accurately unpredictability. The best tokens are completely random. You are right that a concern is that a user will modify his/her token and use it to access the accounts of others. This is a common attack known as "session stealing." This attack is nearly impossible when the tokens are randomly generated and expired on the server side. Using the user's information such as IP and/or a time stamp is bad practice because it improves predictability. I did an attack in college that successfully guessed active tokens that were based on server time stamps in microseconds. The author of the application thought microseconds would change fast enough that they'd be unpredictable, but that was not the case.
You should be aware that when users are behind proxy servers, the proxy will sometimes view their SSL requests in plain text (for security reasons, many proxies will perform deep packet inspection). For this reason it is good that you expire the sessions. If you didn't your users would be vulnerable to an attack such as this, and also possible XSS and CSRF.
RSA or Rijndael should be plenty sufficient, provided a reasonable key length. Also, you should use an HMAC with the token to prevent tampering, even if you're signing it. In theory it would be redundant, since you're signing with a private key. However, HMAC is very well tested, and your implementation of the signing mechanism could be flawed. For that reason it is better to use HMAC. You'd be surprised how many "roll your own" security implementations have flaws that lead them to compromise.
You sound pretty savvy on security. Keep up the good work! We need more security conscious devs in this world.
EDIT:
It is considered safe to include timestamps/user IDs in the token as long as they are encrypted with a strong symmetric secret key (like AES, Blowfish, etc) that only the server has and as long as the token includes a tamper-proof hash with it such as HMAC, which is encrypted with the secret key along with the user ID/timestamp. The hash guarantees integrity, and the encryption guarantees confidentiality.
If you don't include the HMAC (or other hash) in the encryption, then it is possible for users to tamper with the encrypted token and have it decrypt to something valid. I did an attack on a server in which the User ID and time stamp were encrypted and used as a token without a hash. By changing one random character in the string, I was able to change my user ID from something like 58762 to 58531. While I couldn't pick the "new" user ID, I was able to access someone else's account (this was in academia, as part of a course).
An alternative to this is to use a completely random token value, and map it on the server side to the stored User ID/time stamp (which stays on the server side and is thus outside of the clients control). This takes a little more memory and processing power, but is more secure. This is a decision you'll have to make on a case by case basis.
As for reusing/deriving keys from the IV and other keys, this is usually ok, provided that the keys are only valid for a short period of time. Mathematically it is unlikely someone can break them. It is possible however. If you want to go the paranoid route (which I usually do), generate all new keys randomly.
I'm creating an app for Windows Phone and Android. So right now Im building a webapi they both can use, but I want to secure it som non other then my applications can use it. How do I go about it? No one else then my apps is going to access these APIs.
I don't want to implement OAuth.
I've got two scenarios that I'm thinking of:
First (I store username and hashed password on the client):
basic-auth over https/ssl, thats it.
Second (I store accesstoken on the client):
basic-auth over https/ssl to receive a access token.
to get access token, user sends a request for requestoken a token that verifies that both the client and server knows the clientsecret.
for each call to the API the accesstoken has to be sent with to check access
The problem as I see the second approach is that the server sends accesstoken to the client, if anyone where to get this they would have the access of the user.
How is it done in the real world?
You could use a slight modification of First:
store username and password on client
basic-auth over https
Storing a password hash on the client, then sending the hash and comparing it with the hash in the database is equivalent to storing a plain text password in the database because the hash becomes the password. So, your apps should authenticate with a username and password like any human user would do.
But your concerns for the second approach apply too. If somebody intercepts the message, he has your credentials.
A more secure solution is HMAC authentication (now we're talking "real world").
a user has a secret key that is stored on server and client
each request gets canonicalized (transformed into a distinct string, which contains the request method, URI, parameters and timestamp)
the canonicalized request gets hashed with HMAC using the secret key, hash and user id are passed in the HTTP Authorization header
on the server, a hash is generated using the same algorithm (with timestamp from the HTTP Date header) and compared with the sent hash.
if the results are equal, the request is authenticated
An example is the Amazon S3 REST API - the linked documentation is also a good example how to implement it for your own API.
I'm currently developing an API for an online service.
I would like to give access for mobile and web developers to create their applications.
Developers will have the usual number reqs/minute limits for their applications.
What are the best practices for authenticating applications?
For web applications it's easy. We provide token, token is valid for a domain so even if somebody will try to use anywhere else it will fail.
How to do that for mobile applications?
We can provide token. Such token needs to be distributed with application on the device
and means that somebody will sniff that token he can write another application that will use the same token. This will mean that original user will have to revoke old token, create a new one and release new version (that his users will have to download again).
Do you know any solution for that?
I'm not sure that your developers would be able to securely do this without having some form of communication with their own host and some form of user account on their system. As you said, if you included a long-lived token in an app, no matter what obfuscation is done it could eventually be discovered by reverse engineering techniques.
There are 2 options that I can see:
1. Short lived token
In this scheme the mobile application contacts the developer's system to receive an short auth token.
During enrollment and periodically thereafter, developers generate a public-private keypair and give you the public key.
Each auth token would need to include an unencrypted "developer key ID" of some sort and an encrypted bit of data including the token's issue data and a salt of pseudo-random data. The developer's host would encrypt the data using a private key in a public-private keypair. This keeps the secret in a controlled and secure space. The encrypted data needs to include the salt in order to prevent known-plaintext attacks on your developers' keys.
The app sends the token to you, you can determine it's legitimacy by:
Use the unencrypted developer key ID to determine which key to use in decrypting the encrypted string.
Has the developer key ID been revoked or expired? (due to key compromise, dev API subscription expiration or abuse, etc). If it was revoked, deny access.
Does the encrypted data in the token decrypt correctly? If not, deny access.
Has the token expired? (based on the encrypted token date) If so, tell the client to get a new token from the dev server. Their software should do this before contacting your API, but you have to check just in case. I'd suggest that tokens be allowed to live for a relatively short time since copying a token between apps is a weakness.
Allow access
You could also use symmetric encryption instead of public-private key encryption, but then you and the dev both know the secret. It'd be more secure if only the dev knows it.
2. Pass API calls through dev host
It'd be possible for mobile applications to talk to their developer's host instead of your host for calls to the API. When the dev host receives one of the calls, it simply passes the call through to your API and adds their secret token.