235

Background:

I'm designing the authentication scheme for a REST web service. This doesn't "really" need to be secure (it's more of a personal project) but I want to make it as secure as possible as an exercise/learning experience. I don't want to use SSL since I don't want the hassle and, mostly, the expense of setting it up.

These SO questions were especially useful to get me started:

I'm thinking of using a simplified version of Amazon S3's authentication (I like OAuth but it seems too complicated for my needs). I'm adding a randomly generated nonce, supplied by the server, to the request, to prevent replay attacks.

To get to the question:

Both S3 and OAuth rely on signing the request URL along with a few selected headers. Neither of them sign the request body for POST or PUT requests. Isn't this vulnerable to a man-in-the-middle attack, which keeps the url and headers and replaces the request body with any data the attacker wants?

It seems like I can guard against this by including a hash of the request body in the string that gets signed. Is this secure?

Community
  • 1
  • 1
dF.
  • 74,139
  • 30
  • 130
  • 136
  • 6
    Amazon S3 can include a Content-MD5 as part of the header string to prevent the MITM attack you describe. – laz Jan 21 '09 at 21:03
  • 8
    MD5 is a very weak hash function and it's usage has been discouraged for many years now: http://en.wikipedia.org/wiki/MD5. Use SHA2 nowadays. MD5 is lipstick on a pig with an identity crisis. – Henrik Aug 30 '12 at 18:33
  • 6
    [Startcom](http://cert.startcom.org/) provides free SSL certificates that don't throw certificate warnings in major browsers – Plato Aug 08 '13 at 12:47
  • 1
    @Henrik MD5 is weak but the content hash will be worthless in a few minutes...far quicker than anyone (well 99.99999% of people) can make any practical use of it. – Sean Anderson Mar 23 '14 at 23:52
  • @SeanKAnderson Not necessarily, the tunnelling attack might be possible to use http://eprint.iacr.org/2006/105.pdf -- since the requests are "computer made" they might be possible to profile and create automated exploits for. – Henrik Mar 28 '14 at 10:38
  • 5
    @SeanKAnderson (rant: I find it absurd how people talk about 99.99999%s when the internet is under siege by spy agencies which have automated A LOT of attacks already at 2008 -- it's such a strange way to deal with a real issue -- "Naaah, won't be a problem; for my grandma to wouldn't be able to hack it" – Henrik Mar 28 '14 at 10:40
  • @Plato: *Startcom* certs are [not trusted](http://forums.whirlpool.net.au/archive/2605051) by Chrome starting with version 57, [unless](https://bugs.chromium.org/p/chromium/issues/detail?id=685826) your site is in Alexa Top 1M (or Chrome v58 with Alexa Top 500K). – Voicu Aug 02 '17 at 23:04
  • thanks @Voicu i was pretty shocked to see Google's announcement that startcom falsified certs last year – Plato Aug 02 '17 at 23:14
  • 2
    @Plato I'd recommend LetsEncrypt these days for free SSL certs – shuttle87 May 30 '18 at 12:47

6 Answers6

173

A previous answer only mentioned SSL in the context of data transfer and didn't actually cover authentication.

You're really asking about securely authenticating REST API clients. Unless you're using TLS client authentication, SSL alone is NOT a viable authentication mechanism for a REST API. SSL without client authc only authenticates the server, which is irrelevant for most REST APIs because you really want to authenticate the client.

If you don't use TLS client authentication, you'll need to use something like a digest-based authentication scheme (like Amazon Web Service's custom scheme) or OAuth 1.0a or even HTTP Basic authentication (but over SSL only).

These schemes authenticate that the request was sent by someone expected. TLS (SSL) (without client authentication) ensures that the data sent over the wire remains untampered. They are separate - but complementary - concerns.

For those interested, I've expanded on an SO question about HTTP Authentication Schemes and how they work.

Community
  • 1
  • 1
Les Hazlewood
  • 18,480
  • 13
  • 68
  • 76
  • 16
    Exactly. The only thing SSL verifies as far as your API is concerned is that the call it's dealing with hasn't been messed with en route. The API still has no idea who's talking to it or whether or not they should have access at all. – Tim Gautier Jul 18 '12 at 19:58
  • 1
    Good answer. I would also recommend having a look at these excellent resources .. https://www.owasp.org/index.php/Web_Service_Security_Cheat_Sheet and https://www.owasp.org/index.php/REST_Security_Cheat_Sheet (DRAFT) – dodgy_coder Nov 19 '12 at 02:16
  • 2
    Just a minor point, but using SSL also has the additional benefit of preventing eavesdropping and man in the middle attacks. – dodgy_coder Nov 19 '12 at 02:18
  • @Les Hazlewood Could you explain how HTTP Basic authentication over Https can help to determine server knows whom its talking to? – Spring Dec 26 '12 at 15:24
  • @Les Hazlewood here I asked it in a question; tnx http://stackoverflow.com/questions/14043397/http-basic-authentication-instead-of-tls-client-certificaiton – Spring Dec 26 '12 at 16:38
  • @Spring I'll submit an answer to the referenced question shortly (sometime today). – Les Hazlewood Dec 26 '12 at 17:12
  • @Les Hazlewood I would be glad if you also have a look at this http://stackoverflow.com/questions/14041496/rest-token-authentication-with-http-header – Spring Dec 26 '12 at 17:50
  • @Spring replied to your first question! – Les Hazlewood Dec 26 '12 at 21:37
  • @Les Hazlewood amazing tnx! looking forward for your other reply, so I hope this thing finally clears up for me! – Spring Dec 26 '12 at 22:01
60

REST means working with the standards of the web, and the standard for "secure" transfer on the web is SSL. Anything else is going to be kind of funky and require extra deployment effort for clients, which will have to have encryption libraries available.

Once you commit to SSL, there's really nothing fancy required for authentication in principle. You can again go with web standards and use HTTP Basic auth (username and secret token sent along with each request) as it's much simpler than an elaborate signing protocol, and still effective in the context of a secure connection. You just need to be sure the password never goes over plain text; so if the password is ever received over a plain text connection, you might even disable the password and mail the developer. You should also ensure the credentials aren't logged anywhere upon receipt, just as you wouldn't log a regular password.

HTTP Digest is a safer approach as it prevents the secret token being passed along; instead, it's a hash the server can verify on the other end. Though it may be overkill for less sensitive applications if you've taken the precautions mentioned above. After all, the user's password is already transmitted in plain-text when they log in (unless you're doing some fancy JavaScript encryption in the browser), and likewise their cookies on each request.

Note that with APIs, it's better for the client to be passing tokens - randomly generated strings - instead of the password the developer logs into the website with. So the developer should be able to log into your site and generate new tokens that can be used for API verification.

The main reason to use a token is that it can be replaced if it's compromised, whereas if the password is compromised, the owner could log into the developer's account and do anything they want with it. A further advantage of tokens is you can issue multiple tokens to the same developers. Perhaps because they have multiple apps or because they want tokens with different access levels.

(Updated to cover implications of making the connection SSL-only.)

mahemoff
  • 44,526
  • 36
  • 160
  • 222
  • 1
    After thinking about this for a while and implementing a first version, I tend to agree.... – dF. Oct 13 '09 at 14:40
  • 3
    You could get a GoDaddy ssl certificate for like $30 a year I think. I was shocked to see how much the Verisign SSL certs go for ($600 a year or something if I remember correctly?) But the GoDaddy option is perfectly feasible. – Brian Armstrong Apr 05 '10 at 06:27
  • 1
    Being a very small team and having not investigated it too much beforehand, we 'fell upon' SSL by default in a recent REST deployment. We're now being asked by another division for best practice regarding REST, seems like SSL is the general consensus. BTW - Go Mike [Mahemoff]!!! – Big Rich Mar 31 '11 at 09:54
  • 19
    Unless you are using SSL/TLS mutual authentication, and the cert used by the user/client is trusted by the server, then you have not authenticated the user to the server/application. You would need to do something more to authenticate the user to the server/application. – Pauld Aug 03 '11 at 21:07
  • This would mean that any site requiring authentication would force SSL traffic. Wouldn't that be difficult in terms of server processing power? – rybosome Aug 05 '11 at 18:29
  • 5
    Ryan: SSL encryption these days takes a pretty tiny amount of processing power compared to what you'd use to generate a response with a web app framework like Django or Rails etc. – Cameron Walsh Oct 28 '11 at 04:41
  • 4
    certs from startcom are free and widely recognized. cacert.org is an open alternative with less recognition – Dima Tisnek Mar 01 '12 at 13:08
  • 17
    This doesn't address the question, which is about authentication. – Sam Stainsby Oct 11 '12 at 21:49
  • Just FYI, if you use Google app engine for your service (written with jvm, python or go), you get SSL included by default ... say your service is running at myservice.appspot.com then all you do is append the https:// at the front. – dodgy_coder Nov 19 '12 at 02:13
8

Or you could use the known solution to this problem and use SSL. Self-signed certs are free and its a personal project right?

wowest
  • 1,974
  • 14
  • 21
  • 2
    Self-signed certs are free, but AFAIK you still need a static IP. – dF. Jan 31 '09 at 18:29
  • 8
    @dF there is no requirement of having a static IP except for certain licensing requirements of commercial paid for certificates. – Chris Marisic Jun 01 '11 at 15:23
  • If you have control of the certificate stores on both ends (clientes & server) this may be a viable option but... certificate management and distribution is probably much more complex in production than in a development environment. Be sure to understand the complexities to this alternative before commiting to it. – aled May 12 '14 at 19:46
5

If you require the hash of the body as one of the parameters in the URL and that URL is signed via a private key, then a man-in-the-middle attack would only be able to replace the body with content that would generate the same hash. Easy to do with MD5 hash values now at least and when SHA-1 is broken, well, you get the picture.

To secure the body from tampering, you would need to require a signature of the body, which a man-in-the-middle attack would be less likely to be able to break since they wouldn't know the private key that generates the signature.

ZaDDaZ
  • 131
  • 5
  • 9
    Coming up with a string that will generate the same md5 hash as the valid content may be much easier than it should be, but coming up with an evil version of valid content that hashes to the same value is still prohibitively difficult. This is why md5 isn't used for password hashes anymore, but is still used to verify downloads. – Tim Gautier Jul 18 '12 at 19:50
3

In fact, the original S3 auth does allow for the content to be signed, albeit with a weak MD5 signature. You can simply enforce their optional practice of including a Content-MD5 header in the HMAC (string to be signed).

http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html

Their new v4 authentication scheme is more secure.

http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html

djsadinoff
  • 5,519
  • 6
  • 33
  • 40
1

Remember that your suggestions makes it difficult for clients to communicate with the server. They need to understand your innovative solution and encrypt the data accordingly, this model is not so good for public API (unless you are amazon\yahoo\google..).

Anyways, if you must encrypt the body content I would suggest you to check out existing standards and solutions like:

XML encryption (W3C standard)

XML Security

LiorH
  • 18,524
  • 17
  • 70
  • 98