71

I'm currently in the process of building a JavaScript SPA and have been researching how to secure it. There is currently as RESTful API that is being completely interacted with through AJAX. We also have mobile clients that interact with this API, and currently it only supports HTTP BASIC Authentication over SSL. The JavaScript app will also communicate exclusively over SSL, but BASIC Auth won't cut it as that would involve storing the password (or a derivative of it) on the client. Lastly, the SPA app will be pure JavaScript and HTML, served on the same server as the RESTful API, but without any server-side framework.

Goals:

  • No server-side framework for the javascript client (it's just another client).
  • Maintain statelessness of the RESTful API, for the typical reasons (scalability, fault tolerance, simplified deployment, etc)
  • Any state should be maintained by the client. For the purposes of this question, this means the login credentials.
  • Login state maintained by the client must be secure and be resistant to session hijacking and similar attacks.

What I've come up with is based on my research of OAuth and similar schemes (Amazon, etc).

  1. The user will login using and HTTP POST over SSL.
  2. The server will compute a hash as follows:

    HMAC(key, userId + ":" + ipAddress + ":" + userAgent + ":" + todaysDateInMilliseconds)

  3. This token will be returned to the client and supplied with every subsequent request in place of the userName and password. It will most likely be stored in localStorage or a cookie.

Is this secure? My motivation for choosing the userId,ipAddress,todaysDateInMilleseconds is to create a token that is valid for only today, but does not require database lookup for every request AND is safe to be stored on the client. I cannot trust that the key will not be comprimised, thus the inclusion of IP Address in an attempt to prevent session hijacking.

Let me include the following link from a related post on StackExchange because I think it addresses a lot of the issues I'm trying to solve: REST and Stateless Session Ids

After the initial feedback here I've decided to use only the first two octects of the IP address to handle clients behind proxies and mobile clients better. It's still not perfect, but its a tradeoff for some additional security.

Anders
  • 64,406
  • 24
  • 178
  • 215
Jon Wingfield
  • 821
  • 1
  • 7
  • 5
  • 1
    FYI, we ended up implementing basically the exact above solution, except for including the IP address, which causes issues with mobile. – Jon Wingfield Jan 29 '14 at 16:36
  • Thanks for posting and updating. Very good thread. So you wanted to avoid sessions to avoid DB reads/writes, right? I'm doing something similar and your experience on this topic is invaluable. I am asking myself all of these questions. – Dmitry Minkovsky Mar 05 '14 at 06:37
  • I'm trying to do a similar thing - did your HTML & JS end up being public without any authentication, and only the API was secure? – Narayana Mar 20 '14 at 03:58

3 Answers3

30

The service offered by the token is that the server will somehow recognize the token as one of its own. How can the server validate a HMAC-based token ? By recomputing it, using its secret HAMC key and the data over which HMAC operates. If you want your token to be computed over the userID, password, IP and date, then the server must know all that information. However, you do not want your server to store the password, and the client will not send it back with each request. How can your system work, then ?

The basic idea, however, is sound:

  • Users "logs in" by any way which you see fit.
  • Upon such login, the server sends a cookie value, to be sent back with each subsequent request (that's what cookies do).
  • The cookie contains the user ID, the date it was issued, and a value m = HMAC(K, userID || date || IP).
  • When the server receives a request, it validates the cookie: the userID and date are from the cookie itself, the source IP is obtained from the Web server layer, and the server can recompute the value m to check that it matches the one stored in the cookie.

You could replace the whole cookie with a random session ID, if the server has some (temporary) storage space. Indeed, the server could remember the mapping from a random session ID to the user-specific information (such as his name and IP address); old session ID can be automatically expired, so the storage space does not grow indefinitely. The cookie described above is just a way to offload storage on the client itself.

Note: using the IP address may imply some practical issues. Some clients are behind proxies, even load-balanced proxies, so not only is the client IP address possibly "hidden" (from the server, you see the proxy's address, not the client's address) but the IP address you obtain server-side could move around erratically (if two successive requests from the client have gone through distinct proxies in a proxy farm).

Thomas Pornin
  • 320,799
  • 57
  • 780
  • 949
  • Thanks for your input on the IP address drawbacks. Do you have suggestions for other ways to avoid session hijacking? I could use things like user-agent, but these are easily spoofable from what I understand. I also updated the original post based on your obvious point about including the password. – Jon Wingfield Sep 01 '12 at 22:13
  • @JonWingfield: presumably, a "secure" cookie, sent over HTTPS, is "secure" -- if someone can unduly access a copy of that cookie, then there is very little you can do against it, either from the Javascript or from the server, except deprecating old cookies, which you already envision. – Thomas Pornin Sep 01 '12 at 22:19
  • Wanted to mention about HttpOnly cookie to secure it further (https://www.owasp.org/index.php/HttpOnly). HttpOnly is an additional flag included in a Set-Cookie HTTP response header. Using the HttpOnly flag when generating a cookie helps mitigate the risk of client side script accessing the protected cookie (if the browser supports it). – Jay Kumar Mar 14 '14 at 06:54
  • Is JWT an implementation of the token concept described above. Such JWT tokens in API requests would let the server 1. Verify that it issued the token. 2. Determine the user based on its payload. 3. Issue tokens which last for a specific time period. Thanks. – Akshay Rawat May 01 '15 at 08:57
9

There is a much simpler solution:

Use SSL sitewide, and then use your framework's standard session tracking.

That's all you need to do.

In more detail, the user initially logs in by supplying their username and password; it gets POSTed to the server, who can check its validity. If the authentication is successful, your server code sets a flag in the session state remembering that the user has successfully authenticated and remembering the user's username. All subsequent requests will be over the same session, so you can easily authenticate them and allow them to proceed.

(Still more detail: Each time you receive a request, you check the session state to see whether the user has successfully authenticated and is authorized to perform this action. If yes, you allow the request and perform the action; if no, you redirect the user to a login page or present some other error message.)

Notice that this meets all of your requirements. It does not require a database lookup on each request. It is compatible with a RESTful API. It is compatible with a single-page app. You don't need to code up anything fancy: you just use your framework's existing support for sessions (usually, it uses a session cookie with a unique session ID, and some mechanism to store state on the server side associated with that session ID).

As part of using SSL sitewide, you should set the secure flag on your session ID cookie, to protect it from eavesdropping (this will ensure it will never be sent over HTTP). You should also enable HSTS, to tell the browser to always use SSL on your site. Search this site for more information on how to deploy SSL sitewide.

You should not rely upon the client's IP address to be static. It may change, e.g., if the client is mobile and moves from one wireless network to another. Therefore, it is best to avoid using the client's IP address for anything.

D.W.
  • 98,420
  • 30
  • 267
  • 572
  • There is no framework for the web application. There is a framework (OpenRasta) for the RESTful API, but the JavaScript app is just plain old HTML and JS served from the same server to avoid XSS issues. The goal is to have NO concept of a session, but still allow some degree of security for the web client. Perhaps what I'm trying to do just isn't possible and I'll need to persist something to the database, but I'm trying to avoid this if possible. – Jon Wingfield Sep 02 '12 at 21:23
  • 1
    @JonWingfield, I'm talking about the server-side web application framework (not client-side stuff). These days almost any decent web application framework will provide support for session management, and almost every web application is built using a web application framework. – D.W. Sep 02 '12 at 23:07
  • *"The goal is to have NO concept of a session"* - I don't know why that is your goal. That seems like an unusual goal to have, and it's not something you set out in your question. (And it seems to be confusing the mechanism with the end goal: confusing the means with the end. A session is just a mechanism, just a means to an end. What is the ultimate requirement, that you think sessions are incompatible with?) – D.W. Sep 02 '12 at 23:09
  • OK, so in the sense of keeping the user logged in, I suppose you could categorize that as a session. However, the general concept of sessions in web apps is usually associated with server-side state across requests, which I want to avoid for many reasons. The client should maintain state between requests. As to frameworks, there just isn't a server-side framework for this web app. It's just plain HTML files served by a web server. All logic is client-side using various JS frameworks (JQuery, Backbone, etc). – Jon Wingfield Sep 03 '12 at 00:30
  • The ultimate requirement is to avoid the issues with keeping session state on the server. Things like server restarts, load balancing, scaling, etc. are all things that complicate having session state held by the server. – Jon Wingfield Sep 03 '12 at 00:32
  • 1
    @JonWingfield, OK. I think you need to do a major edit to your question, then, as that dramatically changes things! This is a solvable problem, but the no-server-side-state requirement makes it a very different problem than what you've currently got posted. You'll still need server-side code (e.g., to check authentication), so just plain HTML files is not gonna be enough -- but it is possible to do this without server-side session state, under certain conditions. Once you've edited the question to reflect the real requirements, I'll post a new answer. – D.W. Sep 03 '12 at 00:37
  • let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/4714/discussion-between-jon-wingfield-and-d-w) – Jon Wingfield Sep 03 '12 at 00:44
  • restful = no session on the server. as soon as you have something like a loadbalancer up front you should not use sessions anymore in anyway... – Preexo Jun 24 '14 at 14:08
3

Check this post Using OAuth2 in HTML5 Web App. @jandersen answer gives a good explanation on using Resource owner password credentials flow in single page applications. In any case, the prefered flow for this apps should be the Implicit grant. In fact, @jandersen 's response on the referenced post is about tweaking Resource owner password credentials to act as something close to the Implicit grant.