9

As per https://www.rfc-editor.org/rfc/rfc6749#section-10.12:

The client MUST implement CSRF protection for its redirection URI. This is typically accomplished by requiring any request sent to the redirection URI endpoint to include a value that binds the request to the user-agent's authenticated state (e.g., a hash of the session cookie used to authenticate the user-agent). The client SHOULD utilize the "state" request parameter to deliver this value to the authorization server when making an authorization request.

Once authorization has been obtained from the end-user, the authorization server redirects the end-user's user-agent back to the client with the required binding value contained in the "state" parameter. The binding value enables the client to verify the validity of the request by matching the binding value to the user-agent's authenticated state. The binding value used for CSRF protection MUST contain a non-guessable value (as described in Section 10.10), and the user-agent's authenticated state (e.g., session cookie, HTML5 local storage) MUST be kept in a location accessible only to the client and the user-agent (i.e., protected by same-origin policy).

Is it safe to use a secure, HTTPOnly, SameSite cookie (say, with a short timeout) to store the state parameter's value? Would validating the value in the cookie with the value in the state parameter be sufficient to verify the validity of the request?

neverendingqs
  • 191
  • 2
  • 5

1 Answers1

1

Is it safe to use a secure, HTTPOnly, SameSite cookie (say, with a short timeout) to store the state parameter's value?

You can make it secure.

Assuming you're writing server-side software (I'm assuming you do, since you mention setting the HTTPOnly option for the cookie): Encrypt the state parameter on the server and store the encrypted value in the cookie. (Details: Make sure you use a secure encryption algorithm with a random iv, and don't use ecb mode). Make sure you keep the encryption key secure on the server.

Why encrypt the cookie?

You have to encrypt the state value (or the whole cookie) for two reasons:

  1. On principle: Security people like to layer their security measures. If one fails, the other will still protect you. Even if you protect your traffic with SSL, the SSL encryption may be compromised. For example, where I work, we have a content-inspecting firewall which does MITM on SSL, so it breaks the security of the connection by design. (This is a really bad idea, all in all, but I believe this is quite common - and it means that you can't count on SSL protecting the connection from your server to your clients.

  2. To protect the state value from the client (user-agent). If there is malware sitting on your client, you don't want to expose the state value to it. It might get stolen. If the state value is encrypted, client software has no access to it, which is good.

Is this safe?

This is just as safe as storing a session id in the cookie and then using the session id as an index into a server-side database table, since neither the browser nor malicious javascript sitting in the browser nor anyone else sees anything more than an opaque, random-looking string in your encrypted cookie, and if they mess with it in any way, it won't decrypt to the original state value, which will cause the verification of the state to fail. But you might want to add a MAC (message authentication code) and only touch the encrypted state value from the cookie when the MAC checks out to secure your cookie contents against meddling.

Local in-browser javascript vs server-side processing

If you're writing browser javascript that makes oauth requests, you have to be extra careful (but javascript browser security is fundamentally broken; if you have a javascript browser application do oauth requests, I doubt you can make that really secure - you can make it reasonably safe, but probably can't defend against a determined adversary.

Is validating the state parameter sufficient?

Would validating the value in the cookie with the value in the state parameter be sufficient to verify the validity of the request?

Whether validating the state is enough to validate the oauth request (reply?) depends on which flow you are using and at what step in the protocol you are.

For example, if you're getting back a jwt id token, in pretty much all of the scenarios you must verify the id token to make sure it's valid and meant for you (you can do that yourself or send it to a verification endpoint. A jwt id token is digitally signed and you must make sure the signature is okay, the audience and your client id match, the token hasn't expired etc.).

The same goes for access tokens: If, for example, you don't verify the audience of the access token, bad things might happen (e.g. the access token might be intended for another client...)

There are a few cases where you don't need to verify tokens. When you're using using open id connect to get an id token from an identity provider and you don't route the request through the browser, and get back the id token from your identity provider via server side request, then I think you can trust the id token without verification. However, I still always verify, just to make sure I don't stumble over my own cleverness ;-)

Out of Band
  • 9,150
  • 1
  • 21
  • 30
  • 2
    I'm interested in why it needs to be encrypted. I thought SameSite + Secure is enough to make it only readable by the user-agent and the server. – neverendingqs Oct 28 '16 at 14:44
  • It needs to be encrypted for several reasons: The first one is "on principle": One of the guiding security principles is that you layer security; so if for some reason your SSL connection is compromised (at work, we have a content-inspecting firewall which does MITM on SSL), they still can't get at the state value. The second reason is to protect the state value from the client: If you don't encrypt it, then malware on the client can steal the state value. In fact, you shouldn't just encrypt it; you should also use a MAC to see whether someone tried to tamper with the cookie contents. – Out of Band Oct 29 '16 at 13:12
  • 2
    If the encryption/decryption happens on the client server, all you're doing is replacing `state` with `encrypt(state)` which is no better than it was before. The decrypted state value isn't going to be useful to anyone if the client server server will only accept the encrypted one anyway. – gengkev Dec 24 '18 at 16:12
  • @gengkev - I'd assume you're usually right, because usually the state isn't just opaque, it's actually random. But it doesn't have to be. You might get a state value that contains useful information, such as a concatenation of internal ids which shouldn't be leaked. – Out of Band Dec 26 '18 at 10:51
  • Hmm, I suppose that's fair, you wouldn't want the state to be the session ID unencrypted... – gengkev Dec 26 '18 at 22:40