I am developing API for my real-time Web Application to make it work with Mobile App.
I want make my user's data available to access/modify with Mobile App.
To avoid sending user credentials to server every time to access user data, I am going to implement Access tokens.
So what?.. Instead of storing generated Access token details like user_id, expiry_time, user_ip, etc., in database, I am going to store them in Access token itself. so, I can avoid database queries every time user requests for update.
I am thinking of following API strategy:
Initially Mobile App asks User Email and Password from user and sends request to API Server to get Access Token.
API server checks user credentials against the database table.
If user exists,
gather user_id, timestamp, user_ip, etc.,
format a comma delimited string with above data.(or url_encode?)
encrypt the comma delimited string with encryption key (I keep it safe) using Mcrypt Library.
base64_encode the encrypted data and return it as Access Token.
Mobile App stores this returned Token for later use.
Further requests from App are sent with the Token.
When access request with Token is received by API Server,
decrypts the Token with same encryption key. (So, we will get the same comma delimited string)
Parse the decrypted delimited string. we will get user_id, timestamp, user_ip.
directly use,
user_id to fetch corresponding user data.
timestamp to check if Token is expired or not.
user_ip to decide allow/deny if user IP Address change.
If encryption is failed, we can conclude it as invalid Access Token.
Advantages: No database queries required every time when user request for fresh data. so, time and resources are saved.
I didn't find any vulnerabilities / problems with this approach. that's I am here! please provide your valuable feedback!
Note: I don't know whether this is the common way in which access tokens works or not. sorry, if it is.
Do not hand reversible encryption to a user. You will need to use the same encryption key for all users, which means you have one very very secret key which you must ensure stays secret. If this key ever leaks, any user can embody any other user by recreating your token scheme and choosing arbitrary user ids. Since you're handing the cypher data to the user, they can easily mount an offline attack on it (e.g., just try any and all combinations of keys on the data until it successfully decrypts). Once the key has been discovered this way, you have no recourse besides changing the key.
Instead, use a hash which is specifically designed to slow down such brute force attacks to such a degree that they become impossible in practice. Implement this using a signed token. That means, you use the same information (user id, ip, timestamp), you add a randomly generated value, you hash all this with your secret key, and you send all these pieces except the secret key to the user.
// user_id,ip,timestamp,random_token,hash
42,127.0.0.1,12345678,oiawd8juht4mp9384,q209c8yqc23n09rhcq823n9t87q432hnq9493q784gth
The information can all be in plaintext, because it's meaningless by itself, and it's authenticated by the hash, which cannot be faked without the secret key. Use an expensive HMAC hash for this purpose.
Related
I have multiple companies utilising my GET endpoint, each company needs to have their own API key which they will provide as a url parameter.
I need to keep generating these token whenever new company will join in (there is a table reflecting this).
Is it safe to just:
- create company_token pivot table
- generate token as str_random(32) (because GET request might have maximum url length)
? Or is there a safer, better method?
The best solution I guess would be to use JWT.
You can generate token for each company and distribute them.
Instead of sending the token in the URL as a query parameter, use the HTTP request authorization header and send the token as bearer token.
Database
The simpler and most secure way of keeping a company-level API token would be what you are already doing - using a secure PRNG to get a HTTP-safe token, which is what str_random() does, and having a UNIQUE constraint on the API token table to be sure you don't get duplicates.
UX
You could also supply a way of changing the API token by the company, after a suitable login, in order to allow them to get rid of obsolete or leaked tokens. Since you would update the API row and not keep old values, keeping track of obsolete tokens would not be a concern.
Security (as much as possible)
To avoid sending the token visibly in the URL, you could also receive them from a custom HTTP Header, as #AbdurRahman suggested.
Finally, you could use the token only once per session and per client: the API token can be consumed only by a /nonce API, which will reply with a longer token in the form of "IP:DATE:HASH" (e.g. "192.168.168.192:2020.02.16.15.35:ee2998d477ee4a1b50e886590d228239"), which would be around 64 bytes. The hash would be produced by MD5("IP:DATE:SECRET"), which guarantees that the information has not been tampered with. All subsequent APIs just verify that this token is untampered with, the IP matches and the date is not too far in the past (or the future).
The advantage is that the token being sent is now different for each client and changes every time, so even if it gets intercepted, little damage would be done. The "real" token that would be harmful if intercepted is sent only once per session; there are more secure schemes than this, but this would already be an improvement.
I've been reading up on API communication securities and trying to figure out the best way to build a secure API. I know that OAuth and such exist, but I'm also trying to educate myself in the process and not rely on libraries.
Basically I have a Web Service and in that web service users can register for API. They will be provided a Profile ID and secret key which they have to use to build the API request from another web system.
API request is built similarly to the way banks do it, all input data sent to API has to be sorted, hash calculated and then the hash sent to the server, like this:
// Profile data
$apiProfile='api123';
$apiSecret='this-is-a-good-day-to-be-a-secret-key';
// Input
$input=array();
$input['name']='Thomas Moore';
$input['profession']='Baker';
// To ensure that the order of variables checked and received is the same on both ends:
ksort($input);
// Using serialize() for simplifying things
// http_build_query() is another option, or just placing values in order
$input['hash']=sha1(serialize($input).$apiSecret);
// Making a request to URL:
// Using file_get_contents() as an example, would use cURL otherwise
$result=file_get_contents('http://www.example.com/api.php?'.http_build_query($input));
// SERVER CALCULATES COMPARISON HASH BASED ON KNOWN SECRET KEY AND INPUT DATA
This is really good and works. But! My problem is the potential replay attack. If someone snatches this request URL, they can send it to the server again, even though they cannot change the data itself.
Now I've read some things about it that you should also either check the time or add a one-time-use token to the request, but I am unsure how exactly should I do that? Is sending a timestamp with the request really secure enough? (Receiving server would make sure that the request has originated few seconds within the time the request was made, if the clocks are somewhat in sync).
I could also add IP validations to the mix, but these can change and can be spoofed somewhat and are more of a hassle for the user.
I would love this one-time-token type of system, but I am unsure how to do this without exposing token generation to the exact same replay attack problem? (Last thing I need is allowing to give out secure tokens for middle-men).
Opinions and articles would be really welcome, I've been unable to find material that answers my specific concerns. I want to say that my API is secure, without it being just marketing speak.
Thank you!
You need to only allow token exchange via a secure channel (https), and you should have a unique hash per message. Include things like a timestamp and the ip of the client. If you don't use https, you are vulnerable to a firesheep-style attack.
Other than that, you are doing the token generation and exchange correctly.
Sending the time (and including it into the cache) is really an option.
The other option would be 2-phase algorithm when you first request for the session token or a session key, then use it for the session, and its TTL is stored on the server (which can be time or number of requests allowed)
As for the session keys idea look at schemes like http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
Example of 1-time token algorithm:
1) client composes a request for the 1-time token, signs this request with the secret key and sends it to the server.
2) server generates the key, signs it with the same key and sends it to the client (together with the signature)
3) client verifies the token using the secret key
4) client composes the request, including the token, and signs the whole request body with the secret key, then sends to the server
5) server checks whole body integrity and the token validity, then sends the response (again it can be signed with the secret key for integrity and authorship verification)
I would like to implement an API key system to secure API calls to my app.
The way I think will work is my having a private key/secret per account. Each request contains the time, account id and a hash(time+secret).
The server can then do the same thing with the users secret from the database and check that against the hash the client sent.
Is this a reasonable way to do it? It is open to a brute force attack, but I'm thinking that as long as the secret is long (ie uuid) it shouldn't be too much of a problem...
A Thought
Any one could submit another request with the same time and hash and have it accepted, after all its valid, right?
The problem being that the nonce + hash can be replayed. A real authentication protocol requires at least two messages:
Server Client
---->challenge --->
<----response------
For example, the challenge could be the nonce, supplied by the server, and the client's response would be the hash of password with the nonce.
Unfortunately, this requires state, and the whole problem with RESTful protocols is that they do not want the hassle of keeping state. And yet they want to authenticate...
So you really have three options:
Option 1: Pretend the problem does not exist, and use the stateless "authentication" protocol. This is no different from using a cookie. The nonce + password-hash is no more secure than a cookie. Cookies can be stolen, etc, and replayed. The entire web is now plagued by these replay attacks.
Option 2: Try to bolt an authentication protocol onto a stateless communication method. Here, you would have the client send you a UTC time-stamp instead of a nonce. The use of the time-stamp provides limited defense against replay. Obviously your clock is not going to be synched with that of the client, so your server will allow any timestamp within some error margin, and that error margin will be the replay margin of the authentication protocol. Note that this violates REST, because the authentication message is not idempotent. Idempotent implies "can be successfully replayed by an attacker".
Option 3: Do not try to bolt an authentication protocol onto a stateless protocol. Use SSL. Use client certificates. Instead of having the client download a string, let them generate a certificate, or you can supply them with a key-pair. They authenticate via SSL and do not authenticate in your REST layer. SSL has lots of "overhead". It is not lightweight, precisely because it does address these replay issues.
So at the end of the day, it depends on how much you value access to your APIs.
For APIs that only retrieve data (other than private data), rather than create, modify, or delete data,
option 1 in this answer
may be adequate. See, for example, the Bing Maps REST API and Google
Maps Premier web services (where here, Google Maps also hashes the URL with a digital signature
and a special key known only to the API user, which, while providing protection against modifying
the URL, apparently still doesn't provide replay attack protection).
In fact, some APIs that retrieve data do not use an API key, but rather limit access in other ways (for example, the YouTube API allows retrieving publicly available data on videos and users' channels without requiring authentication, but limits the number of recent requests).
Options 2 and/or 3 are required for APIs that do more than just retrieve publicly-available data, for instance, if it modifies user profiles, posts content, or accesses private information: see for example, the YouTube data API authentication page, where OAuth is mentioned as one possible authentication scheme.
Especially for option 1, the API key here is used in order to track access by users to your API, and most importantly, limit access by those users. Option 1 may not be appropriate for APIs that allow unlimited data access.
(This is an answer since it's too long to be a comment.)
Server contains:
username
password hash
Client sends:
username
random string
hash of (password hash + random string)
When clients calls server, server creates hash of password hash (which it knows itself) + random string (given in GET by calling client) eand evaluates if that matches the hash (given in GET by calling client)
Even better would be to create 1 function that generates a secret hash from (password hash + nonce) where "nonce" (something random) is also stored on server. Then make it possible to call the server once with username + password, which returns the secret hash; then have subsequent calls solely depend on username + random string + hash of (secret hash + random string) with the same methodology as described above, but secret being what was then password.
This way, even if your secret would be intercepted and reversed, your pass would still be safe.
And obviously, good hashing algorithms: no rot13 and even solely md5 is questionable.
This is my first time building an authenticated API and I'm running into a few roadblocks.
How do I securely pass an API key from the remote client's page to my server (for the user to authenticate connecting his account to the client's page/app)?
-dylan
In my experience, API Keys are actually used as salt to hashes, and the key itself is not actually passed.
When a client generates a request to the server, it hashes a bunch of stuff together (request time, user_id) + the hash, and the hash is included in the request. On the server side, the same hash is recalculated based by retrieving the key for the server, and following the same steps. If the hash doesn't match, it means that client making requests to the server doesn't know 1) the steps to create the hash, and more importantly 2) the API key used as the salt to create the hash. In this way you can determine your client does in fact have the key, and that they know how to authenticate, all without sending the actual API Key.
Have you looked into SSL?
I'm asked to write a Web API for an application (pc executable, not web-app) that will allow sending emails.
A user clicks something, the app communicates with the API which generates an email and sends it out.
I have to make sure noone unauthorised will have access to the API, so I need to make some kind of authentication and I haven't got an idea how to do it correctly.
There will be more applications accessing the API.
First thought was - send username and password, but this doesn't solve the problem really. Because if someone decompiles the application, they'll have the request url and variables including user/password or simply it can just be sniffed.
so... what options do I have?
I'm fairly sure secure connection (SSL) is not available to me at the moment, but still, this won't help me against the decompiling problem, will it?
EDIT
I haven't said that initially, but the user will not be asked for the username/password. It's the application(s) that will have to be authenticated, not users of the application(s).
The distribution of your software is really the crux of the problem. Hashing user names and passwords and storing them in the software isn't any more useful than storing un-hashed values, as either one would work to access the API server. If you're going to implement usernames and passwords for your users, I think you can use that as a pre-cursor to API control without storing the values in the software itself. Let me describe this in two parts.
Request Signatures
The most common method in use for API request verification is request signatures. Basically, before a request is sent to an API server, the parameters in the request are sorted, and a unique key is added to the mix. The whole lot is then used to produce a hash, which is appended to the request. For example:
public static function generateRequestString(array $params, $secretKey)
{
$params['signature'] = self::generateSignature($params, $secretKey);
return http_build_query($params,'','&');
}
public static function generateSignature($secretKey, array $params)
{
$reqString = $secretKey;
ksort($params);
foreach($params as $k => $v)
{
$reqString .= $k . $v;
}
return md5($reqString);
}
You could create an API request query string using the above code simply by calling the generateRequestString() method with an array of all the parameters you wanted to send. The secret key is something that is provided uniquely to each user of the API. Generally you pass in your user id to the API server along with the signature, and the API server uses your id to fetch your secret key from the local database and verify the request in the same way that you built it. Assuming that the key and user id are correct, that user should be the only one able to generate the correct signature. Note that the key is never passed in the API request.
Unfortunately, this requires every user to have a unique key, which is a problem for your desktop app. Which leads me to step two.
Temporal Keys
So you can't distribute keys with the application because it can be decompiled, and the keys would get out. To counter-act that, you could make very short-lived keys.
Assuming that you've implemented a part of the desktop app that asks users for their username and password, you can have the application perform an authentication request to your server. On a successful authentication, you could return a temporal key with the response, which the desktop app could then store for the lifetime of the authorized session, and use for API requests. Because you mentioned that you can't use SSL, this initial authentication is the most vulnerable part, and you have to live with some limitations.
The article Andy E suggested is a good approach (I voted it up). It's basically a handshake to establish a short-lived key that can be used to authenticate. The same key could be used for signature hashing. You could also take your chances and just send the username/password unencrypted and get a temporal key (it would only happen once), but you'd have to be aware that it could be sniffed.
Summary
If you can establish a temporal session key, you won't have to store anything in the client program that can be decompiled. A username/password sent once to your server should be enough to establish that. Once you have that key, you can use it to create requests in the desktop apps, and verify requests on the API server.
I would recommend you check out OAuth. it should definitely help you out in sorting out the security issues with authorizing tools to access your API.
http://oauth.net
Someone is always going to be able to decompile and hunt for the variables. An obfuscator might be able to hide them a little better. Sniffing is also easy without SSL unless you use a private and public keyset to encrypt the request data client side and decrypt server side (but obviously this key will be stored in the client application).
The best thing to do is provide as many layers of protection as you think you will need, creating a secure connection and obfuscating your code. You could look at the following article, which demonstrates a secure connection without using SSL:
http://www.codeproject.com/KB/security/SecureStream.aspx
As mattjames mentioned, you should never store passwords in plain text format. When the user enters their password into the application, store a hash of the password. The same hash should be stored on the server. That way, if the hash is seen by an interceptor they at least wouldn't see the user's original password.
You will need to use SSL if you need to prevent people from seeing the plain text password that is sent from the app over the network to the API.
For the decompilation issue, you would want to store the hash of the password in the API, not the original password. See explanation here: http://phpsec.org/articles/2005/password-hashing.html.