10

for securing a RESTful API I thought of the following authentication procedure for each call:

  • All data is SSL-encrypted
  • Every HTTP request made by the client will have a signature such as sha512(keysort(POSTDATA.push({"secret": API_SECRET, "timestamp": utc_time()}))
  • The Server knows the clients API_SECRET as well and checks if the signature matches the one sent by the client. If that's the case the request is legit and will be processed.

The problem I'm facing here is the following - Since the API will be called by an iPhone App as well as a WebApp I can't think of a way of not transmitting the API_SECRET once. So let's say the user logs in, sends username=john&password=doe (SSL encrypted) and receives an API_SECRET in response from the server. This API_SECRET will then be stored by the client and used to sign all further API calls.

I'm using the signing to reduce the risk of MITM attacks, but if I'm transferring the key once it seems like a problem, but I can't think of a way of solving this.

Thanks for your help!

doque
  • 203
  • 1
  • 2
  • 6
  • 3
    I'm confused. If it's over SSL, and your app doesn't allow downgrades to HTTP, why are you worried about MitM attacks? – Polynomial Aug 13 '12 at 12:39
  • I thought MITM attacks were a weakness of SSL. If someone intercepted the login call and the response from the server he would have the login details as well as the authorization key. – doque Aug 13 '12 at 12:50
  • 2
    Nope. MitM is only an issue with fake certs. If you make the client enforce that the HTTP cannot be downgraded to HTTP, and that the server's cert is signed by a known CA, you're safe against MitM attacks such as sslstrip. – Polynomial Aug 13 '12 at 13:10

2 Answers2

9

You're asking two different questions here: about the method for generating a "signature" in each request, and about the method for provisioning the API_SECRET to clients on first use. Strictly speaking, these should have been split up into two questions, but I'll answer both of them.

Your signature format. You've got the basic concept about right, but I have a few constructive suggestions/criticisms:

  • Use a MAC. You should be using a message authentication code, not a hash. Using a hash, as you have done, is vulnerable to length extension attacks. For instance, you might use MAC(K, D) where K is your API_SECRET, D is the data of the request and the timestamp, and MAC is a secure message authentication code algorithm. AES-CMAC is a good choice of MAC algorithm. SHA1-HMAC is another standard choice.

  • Include the timestamp in the request. You'll probably need to include the timestamp in the request. The server needs to know the timestamp used by the client, to validate the request. Since you cannot rely upon the client's and server's clocks to be perfectly synchronized, you'll need to send the timestamp in the request.

  • Watch out for duplicate parameters. If you have two request parameters with the same key, then you can run into host-parameter pollution (HPP) attacks. I suggest checking that the keys are all unique, and that the request does not have any parameter with a key of secret or timestamp (if you will be adding those parameters to the POST parameters).

  • Beware of concatenation issues. You didn't say how you take the sorted list of parameters and turn them into a string. The details matter. If you just concatenate all of the values, you will be vulnerable, since the boundary between fields is ambiguous. So, don't use concatenation; use some unambiguously decodeable encoding when preparing the data for hashing (or signing by the MAC).

  • Other resources. See also How to implement an API-Key-Mechanism for a related question.

Provisioning cryptographic secrets. So, you need a way to get the API_SECRET to each client. Here's one standard way to do it. On first login, the user types their password into the client. The client opens a SSL connection to the server and sends the user's username and password. The server validates the user's credentials, and if they are correct, sends this user's API_SECRET value to the client. The client now stores the API_SECRET in private storage (but does not store the user's password). On subsequent connections, the client can now use its API_SECRET to authenticate itself.

An alternate approach. If you are going to be connecting only from dedicated clients (e.g., a mobile app, a desktop application; but not from Javascript running in the browser), then you might consider the following alternative. Instead of using symmetric-key crypto, use public-key crypto. In other words, instead of using an API_SECRET, have each client generate its own SSL client certificate. The client stores the private key securely. On first connection, the client authenticates using the user's password and sends its public key (actually, its cert), and then forgets the user's password. The server remembers the client's cert and all subsequent connections are authenticated by using SSL with client certs.

This is a strong approach. It takes care of all of the potential security approaches listed above. The only downside is that you cannot easily write a Javascript/AJAX client that runs in the browser.

D.W.
  • 98,420
  • 30
  • 267
  • 572
  • 1
    regarding the signature format, (if this is deemed necessary): I would probably suggest using the [OAUTH](http://oauth.net/) protocol, which pretty much combines those elements (MAC, timestamp, nonce etc), and avoids having to re-implement all these yourself. – Yoav Aner Aug 15 '12 at 07:12
7

Hrm. On top of the SSL I'm not sure what you're really getting from the API_SECRET here.

I would think that having a server SSL certificate from a known, trusted source would be the best you can do here, assuming that your client app is distributed in a trust worthy manner. At that point, the client can check the server's authentication via the SSL session, and the server authenticates the client with the username/password.

As @Polynomial says - SSL should encrypt the session such that MITM attacks between the SSL endpoints is not an issue. Trick is knowing where those endpoints are located and being sure that they are located in your infrastructure in a well-thought-out location. For example, if there is an SSL end point in front of the apps server - what protects that last mile of transmission?

Similarly - is the application (mobile or web) properly checking that the server's SSL cert is from a trusted source?

Addition for question in the comment:

I took a look at the Class Reference for NSURLReference on the Mac Development pages and couldn't find any reference to SSL at all... in fact the comments specfically say it's protocol independant, which makes sense given that it's called URL Request.

I don't think you can assume you even have HTTPS with this class - you're going to have to do something to set up the HTTPS session instead of defaulting to HTTP. Here's the Mac Developer overview on SSL. It's bugging me a bit, because there is no obvious function for getting the server's certificate.

Then you'll also need the Certificate, Key and Trust services - to verify the certificate.

I would NOT assume that the Apple development environment will just do this for you. Previous experience with other APIs has given me no reason to trust this and I'm not seeing the documentation that says what it does explicitly. I'm not even particularly sure how trust stores work in this development environment. And this isn't a place where you want to make blind assumptions. I'd look into under what conditions the environment forces SSL, verifies the authenticity of server certificates and verifies that the certificate is signed by a trusted CA. If you don't have documentation telling you when and how these things happen, you can't really assume your service is secure.

bethlakshmi
  • 11,606
  • 1
  • 27
  • 58
  • Thank you very much for clarifying! > Similarly - is the application (mobile or web) properly checking that the server's SSL cert is from a trusted source? I believe that since it's an iPhone App it will be done so automatically if I'm using NSURLRequest, will that be enough? – doque Aug 14 '12 at 08:48
  • 1
    The secret/signing process protects API message integrity. A timestamp and expiry prevents replay. They're used to prevent anyone from capturing the request, and then attempting to reissue it, either with modifications or as is. – Alan Jun 22 '15 at 18:33