[akka-http] akka-http-session project: client-side sessions, remember me, csrf

676 views
Skip to first unread message

Adam Warski

unread,
Jul 10, 2015, 10:41:17 AM7/10/15
to akka...@googlegroups.com
Hello,

I started recently working on a small side-project containing akka-http directives to handle client-side sessions, csrf protection and remember-me. The motivation is to fill in the missing piece necessary to use akka-http as a backend for SPA webapp.

I'd like things to be quite secure, hence:

* the sessions are signed, optionally encrypted and with an optional expiry date
* csrf uses headers as the preferred method of submitting the token (in addition to a cookie)
* remember-me hashes tokens, uses selectors in addition to tokens

Sessions are typed, so it's quite easy to store a simple case class (client-side).

which also contains a very-very simply example app:

The project is just over a week old, so code reviews / comments / etc. more than welcome :)

Adam

Magnus Andersson

unread,
Jul 10, 2015, 6:50:10 PM7/10/15
to akka...@googlegroups.com
Hi

You have something similar in concept to what Play Framework uses for sessions today. Nothing wrong with that per say, but writing secure protocols (session serialization and shared trust) is hard for non trivial implementations. 

For example: The security effect of the Hmac SHA-1 that you are using is a function of the key size (5.3.4 Security Effect of the HMAC Key) the user chooses. But you do not mandate what the minimal length is in your implementation. Even with a larger key, using Hmac SHA-256 instead, with a minimal key size of 256 bits is a safer bet.

Your implementation, like Play Framework, limits the signature to a symmetrical pre shared secret (HMAC and AES). This is effective for basic scenarios but limiting if you'd wish to not trust all services that consumes the session, since anyone verifying the session needs your secret and is therefore able to modify the session.

Without steering you off your current path, I just wish to mention JWT (Json Web Token) which is a token format typically sent as a OAuth2 Bearer Token (using the HTTP Authorization header). JWT has security considerations as well as a client side singed session (claims) addressed in a standardized way with support for both symmetrical and asymmetrical signature and encryptions. 

I'm not suggesting that you abandon your current path but reading the standards around OpenID Connect might be a source of inspiration as they are well written and talk about many subtle security considerations (scroll down and look at the underpinnings group, read about JWA, JWK, JWS, JWE, JWT)

If you do want to use JWT then know that implementing all of JWT (or OpenID Connect for that matter) from scratch is quite a task, but there are well written and easy to use libraries such as Jose4j available. One caveat is that the client needs to add the JWT itself to a header, that implies a web page where you call APIs via javascript.

Good luck going forwards! Libraries like these are very useful.
/Magnus

Adam Warski

unread,
Jul 11, 2015, 2:03:45 AM7/11/15
to akka...@googlegroups.com

You have something similar in concept to what Play Framework uses for sessions today. Nothing wrong with that per say, but writing secure protocols (session serialization and shared trust) is hard for non trivial implementations. 

That's exactly my intention, that is to extract the "essence" of session management as a possibly minimal library. Where possible I tried to base the impl on what's in Play, as I trust them to do the "right thing" (the comments in code point to pieces which are taken from Play)
 
For example: The security effect of the Hmac SHA-1 that you are using is a function of the key size (5.3.4 Security Effect of the HMAC Key) the user chooses. But you do not mandate what the minimal length is in your implementation. Even with a larger key, using Hmac SHA-256 instead, with a minimal key size of 256 bits is a safer bet.

Right, maybe it would be a good idea to mandate a minimal key, or at least warn. Currently I provide a method to generate a "good", long server secret. 
 
Your implementation, like Play Framework, limits the signature to a symmetrical pre shared secret (HMAC and AES). This is effective for basic scenarios but limiting if you'd wish to not trust all services that consumes the session, since anyone verifying the session needs your secret and is therefore able to modify the session.

Well the signature is a hash, the optional encryption of the data is symmetrical. You can also trivially provide your own signature/encryption implementation by substituting one class. The sessions are meant to be verified/decrypted only on the server, but if you'd like to verify them elsewhere, then yes, you need to share the key.
 
Without steering you off your current path, I just wish to mention JWT (Json Web Token) which is a token format typically sent as a OAuth2 Bearer Token (using the HTTP Authorization header). JWT has security considerations as well as a client side singed session (claims) addressed in a standardized way with support for both symmetrical and asymmetrical signature and encryptions. 

I read about JWT, from what I understand it's kind of a combination of how header-based CSRF protection works and keeping the content of the cookie in memory only, (instead in the cookie).

Also, if you want to implement "remember me", I suppose you need to resort to cookies anyway to have some kind of persistent client-side storage?
 
I'm not suggesting that you abandon your current path but reading the standards around OpenID Connect might be a source of inspiration as they are well written and talk about many subtle security considerations (scroll down and look at the underpinnings group, read about JWA, JWK, JWS, JWE, JWT)
 
If you do want to use JWT then know that implementing all of JWT (or OpenID Connect for that matter) from scratch is quite a task, but there are well written and easy to use libraries such as Jose4j available. One caveat is that the client needs to add the JWT itself to a header, that implies a web page where you call APIs via javascript.

Thanks for the links! I know the basic ideas behind the auth protocols, but it's always interesting to read up some details :)

I suppose adding JWT would be a good addition to the library at some point, as an alternative method of implementing sessions. Feel free to create a GH issue if you think that would be a good idea :)

Adam

Magnus Andersson

unread,
Jul 11, 2015, 5:08:51 AM7/11/15
to akka...@googlegroups.com
Hi

I recommend just keeping cookies out of the equation, that makes CSRF issues go away which gives you a simpler implementation. Some answers below, hope you don't mind the long answers :) 

Where possible I tried to base the impl on what's in Play, as I trust them to do the "right thing" 
Well, not a bad starting point, but I imagine Play has legacy and backwards compatibility to consider (cookies) and does server side rendering. I'm suggesting what is right for Play might just be OK for a green field library.

I read about JWT, from what I understand it's kind of a combination of how header-based CSRF protection works and keeping the content of the cookie in memory only, (instead in the cookie).

CSRF protection is only needed because of the use of cookies or Basic Auth where then the browser automatically attaches the authentication/cookie to each request for a domain. If you don't have to be backwards compatible with a cookie based client side sessions (as Play have to) then instead of using CSRF protection you might as well do away with cookies altogether, if you are already using header based CSRF then you actually gain simplicity.

With JWT you do commonly send the token as a header (Authorization: Bearer <your jwt>). But this is not in any way mandated by JWT or JOSE (Javascript Object Signing and Encryption). You can send a JWT as a POST body or as a Cookie if you wish, sorry if I was misleading on that. JOSE-JWT is a data format.

Well the signature is a hash, the optional encryption of the data is symmetrical.
HMAC uses symmetrical keys. You do have a point in that a MAC is not a digital signature. To verify the HMAC you need the same secret key that was used to create it. That means you can create a new signature for a different message. If you have a monolithic application that is fine (like Play apps) if you have multiple applications or multiple small services you may not wish to distribute that key to all clients that need to verify the session.

Also, if you want to implement "remember me", I suppose you need to resort to cookies anyway to have some kind of persistent client-side storage?

Yes browser Local Storage should fit well. The advantage of Local Storage over cookies is again no CSRF issues. In the case of JWT a "remember me" could just be a JWT with a long expiration.

/Magnus

Adam Warski

unread,
Jul 11, 2015, 7:53:10 AM7/11/15
to akka...@googlegroups.com

I recommend just keeping cookies out of the equation, that makes CSRF issues go away which gives you a simpler implementation.

I had the impression that cookies are still "the way things are done", and to be honest didn't really consider not using them at all. But it seems I should revise that :)
 
Some answers below, hope you don't mind the long answers :) 

Quite the contrary, it's great to get your opinions and review!
 
Where possible I tried to base the impl on what's in Play, as I trust them to do the "right thing" 
Well, not a bad starting point, but I imagine Play has legacy and backwards compatibility to consider (cookies) and does server side rendering. I'm suggesting what is right for Play might just be OK for a green field library.
 
I read about JWT, from what I understand it's kind of a combination of how header-based CSRF protection works and keeping the content of the cookie in memory only, (instead in the cookie).

CSRF protection is only needed because of the use of cookies or Basic Auth where then the browser automatically attaches the authentication/cookie to each request for a domain. If you don't have to be backwards compatible with a cookie based client side sessions (as Play have to) then instead of using CSRF protection you might as well do away with cookies altogether, if you are already using header based CSRF then you actually gain simplicity.

So you would need to send the session token (in any data format really, I guess it can also be the same content as the cookie I'm constructing currently) in a custom header, and you would get CSRF protection "for free", though it would only work for AJAX requests. 
 
With JWT you do commonly send the token as a header (Authorization: Bearer <your jwt>). But this is not in any way mandated by JWT or JOSE (Javascript Object Signing and Encryption). You can send a JWT as a POST body or as a Cookie if you wish, sorry if I was misleading on that. JOSE-JWT is a data format.

Well the signature is a hash, the optional encryption of the data is symmetrical.
HMAC uses symmetrical keys. You do have a point in that a MAC is not a digital signature. To verify the HMAC you need the same secret key that was used to create it. That means you can create a new signature for a different message. If you have a monolithic application that is fine (like Play apps) if you have multiple applications or multiple small services you may not wish to distribute that key to all clients that need to verify the session.

Ah, microservices, right ;) Although I suppose it's quite common to have a single "orchestrator" service which does the whole frontend job. But of course it doesn't have to be that way.
 
Also, if you want to implement "remember me", I suppose you need to resort to cookies anyway to have some kind of persistent client-side storage?

Yes browser Local Storage should fit well. The advantage of Local Storage over cookies is again no CSRF issues. In the case of JWT a "remember me" could just be a JWT with a long expiration.

When using Local Storage, would you still separate the "current session data" - containing the signed/encrypted user id or username (that would go to sessionStorage) and a "remember me token" (stored in localStorage) like you do with cookies? It has some nice properties, like varying access levels based on automatic/manual logins or knowing when users log in (automatically or manually).

Thanks,
Adam

Magnus Andersson

unread,
Jul 11, 2015, 9:23:19 AM7/11/15
to akka...@googlegroups.com
Hi again

 
I had the impression that cookies are still "the way things are done", and to be honest didn't really consider not using them at all. But it seems I should revise that :)
 
I agree it is still very common and if you wish you can combine different methods. But the newer authentication schemes like OpenID Connect or Mozilla Persona or Authorization like OAuth 2 don't rely on cookies by design.
 
So you would need to send the session token (in any data format really, I guess it can also be the same content as the cookie I'm constructing currently) in a custom header, and you would get CSRF protection "for free", though it would only work for AJAX requests. 

Correct. But if you use Header based CSRF you are limited to AJAX requests anyhow. You can of course send CSRF tokens in other ways but that goes for a JWT or other type of token as well. So I do believe you have a 1:1 correlation when it comes to limitations.
  
Ah, microservices, right ;) Although I suppose it's quite common to have a single "orchestrator" service which does the whole frontend job. But of course it doesn't have to be that way.

Yes microservices ("SOA done right") is one scenario. Any organization with many applications that are not managed by a single team is another (Enterprise organizations).
 
When using Local Storage, would you still separate the "current session data" - containing the signed/encrypted user id or username (that would go to sessionStorage) and a "remember me token" (stored in localStorage) like you do with cookies? It has some nice properties, like varying access levels based on automatic/manual logins or knowing when users log in (automatically or manually).

Hmm, I haven't considered that scenario so I don't have a strong opinion. Off the top of my head I would create a remember me token with longer expiration but lower access right and keep that in Local Storage. Then I would allow for an upgrade login and that token would be stored in Session Storage and shadow the "remember me" token.


 
Thanks,
Adam

Adam Warski

unread,
Nov 16, 2015, 6:11:33 AM11/16/15
to Akka User List
Hello,

Without steering you off your current path, I just wish to mention JWT (Json Web Token) which is a token format typically sent as a OAuth2 Bearer Token (using the HTTP Authorization header). JWT has security considerations as well as a client side singed session (claims) addressed in a standardized way with support for both symmetrical and asymmetrical signature and encryptions. 

that's quite an old thread, but just for reference I wanted to mention that I just released v0.2 of akka-http-session (https://github.com/softwaremill/akka-http-session) which contains some basic support for JWT - sessions can be now transported using headers (instead of cookies), and encoded in the specified json-based format.

Adam 
Reply all
Reply to author
Forward
0 new messages