6

I'm developing a REST application using the Spring Framework, as as part of the requirements, we have to secure the different functions of the system to different user roles (pretty standard stuff). My current method of determining the roles for the currently logged in user is that every time they call a REST url from the frontend, I am adding a Base 64 encoded string to the request header. This string when decoded resolves to their username and a bCrypt generated password hash in this format username:hashedpassword.

I'm slightly concerned that this is not secure, even though the request will be made over a secure HTTP connection, because it could give a potential hacker access to at least the users username. They couldn't get the password because that is just a hashed value, but they could use that hashed value to call the REST API successfully.

How can I secure this system properly? Do I need to add in a session token or some kind of randomly generated key for the session?

My followup question is then how can I do that RESTfully? I was thinking that I could generate (using bCrypt) a hash that represented the username:hashedpassword together on login, save that to the database and check against that whenever a REST call is made. When the user logs out, just set that to null. Rinse and Repeat. That way any potential attacker would only get a single bCrypt string that wouldn't expose the username, but they could still use that string to call the REST API.

JamesENL
  • 163
  • 1
  • 2
  • 8
  • Please see http://stackoverflow.com/questions/3461298/password-hashing-non-ssl and http://security.stackexchange.com/questions/53952/rest-security-standards/53973 and http://security.stackexchange.com/questions/7057/i-just-send-username-and-password-over-https-is-this-ok – Michael Apr 14 '14 at 07:34
  • If you want to add an answer containing a link to that last post, I'll mark it as correct, that's exactly what I was looking for! Thanks so much! – JamesENL Apr 14 '14 at 07:42
  • why are you reinventing the wheel? It's not like you're the first person ever to use REST. There are several well-established **and extensively tested** methods for REST authentication. – Tom Jul 29 '19 at 15:04

2 Answers2

6

The following links may provide you with an in-depth answer:

Please keep in mind that it is better to not use the username-password combination in every request that you make. Better is to authenticate the user, generate a token server-side, communicate it to the client (e.g. in a cookie) and use that token as authentication for subsequent requests. This link can guide you in that process: https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Session_Management_Cheat_Sheet.md.

Tarec
  • 103
  • 3
Michael
  • 5,393
  • 2
  • 32
  • 57
0

If you only need to provide authentication but the data transmitted over the wire is not sensitive then there is a better way without the overhead/latency of SSL.

Say you have a mobile client app; first make the user sign up or register by providing their email(username) and password in a separate web form (not part of the app or REST service). Then upon successful registration you respond with a user key (this can be a shared secret key stored in the user account on the server (database) for symmetric encryption or a public key for asymmetric encryption where the corresponding private key is stored in the user account on the server (database). This is all done using a web form over SSL.

Now when the user opens the client app you must ask them for their credentials which will be sent with every request to the RESTful service. They must provide their name, password and encryption key which they received previously. This need only be done once. The app then provides some http header with each request which looks something like this:

AUTHENTICATE> username:timestamp:encrypted{password:timestamp} /AUTHENTICATE>

Note that both the password and timestamp inside the {} is encrypted using the user's key. The timestamp is updated with every single request.

Implement an authentication filter on the server that does the following:

First check the timestamp and if expired (say older than 1 second) send an UNAUTHORIZED HTTP response code. If the timestamp is valid lookup the username in your user account database. If not found send an UNAUTHORIZED HTTP response. If the username is found, fetch the stored encryption key for that user (remember this can be a shared secret key or the private key for the users public key). Decrypt the encrypted {password:timestamp}. The decryted password must match the users password stored in your database (the password itself could also be encryted in the database using another key for added security) and the decrypted timestamp must also match the non encrypted timestamp sent in the AUTHENTICATE header above. If not then send an UNAUTHORIZED HTTP response code. If successful the request has been authenticated without the use of cookies/sessions.

You can also cache the the user details to avoid doing a database lookup with every request.

Now if someone is snooping and intercepts the request they will not be able to re-use it to gain access because either the timestamp will be invalid or ,if they update the unencrypted timestamp to be valid, it will not match the encrypted timestamp (after the authentication filter decrypts it).

Another advantage of this approach over using a single app key is that you now have complete control over who can access your service by putting an expiry date on the user account in the database (effectively implementing a subscription based service). This is great because at first you may want to get as many users as possible with a trial subscription (free for say 1 year) then later block access to that user if they haven't payed up to extend the account expiry date :)