This is part six of a tutorial blog series from Ben Finkel addressing the challenges, solutions, and implementation of sound authentication. By the end of this series, you will be confident in your ability to implement an authentication system — even with little-to-no background.
During the previous tutorial I introduced something I called the “state token,” but didn’t bother explaining what it was or where it came from. To be perfectly frank, it comes from the official OAuth 2.0 specification, RFC 6749: 4.1.1. Authorization Request.
RECOMMENDED. An opaque value used by the client to maintain state between the request and callback. The authorization server includes this value when redirecting the user-agent back to the client. The parameter SHOULD be used for preventing cross-site request forgery as described in Section 10.12.
This should really read “strongly recommended,” because the state token is important for addressing one vulnerability in the OAuth Grant flow — a Cross-Site Request Forgery (CSRF). Most OAuth providers will in fact require this value when you initiate a Grant Authorization for this exact reason. RFC 6749 has a short description of a CSRF attack and how to defend against it right here, but it’s not especially clear. I’d rather talk us through it now, before we carry on. Besides, the state token has a neat secondary use that we can learn about as well — remembering application state!
Cross-Site Request Forgery (CSRF)
One of the more obvious attack vectors for the OAuth dance is right at the beginning, during the Grant Authorization.
Recall that the Grant Authorization process is where the user’s browser is redirected to the provider’s endpoint before being redirected back to the consumer app. This redirection is important because it represents a sort of decoupled call and response. Later, during tasks like exchanging the authorization code for an access token, an HTTP request and response accomplishes everything. Because the user needs to be involved during the Grant Authorization process, however, an actual redirect is necessary.
This creates a space for an attack. During a request and response, the browser and server exchange tokens to ensure that they maintain a continuous line of communication. A redirect by definition creates a new “line,” so a malicious attacker could hijack the redirection toward their own ends. This is known as a CSRF attack and this post does a fine job explaining the ways this might happen.
In order to thwart this attack, we generate our own code that can be tied back directly to the session that initiated the Grant Authorization. We send that code to the provider in the state variable, although the provider does nothing except echo it back to us in the redirect back to our app. We can then validate that the state token delivered by the provider is the same as the token we originally sent, thus preventing a CSRF attack.
In our example, I use the following code to produce a state token based on two key variables:
- A private app-specific “key” that I just made up (you can use any value you’d like)
- The current date/time.
These values are hashed with SHA1 encryption, effectively preventing them from being guessed, then base64-encoded so they can be safely embedded in a URL query string.
Finally, check the previous post to see where I both include this value in my redirection to the Grant Authorization endpoint and store it in a session variable so it can be retrieved for verification.
Remembering Application State
The state token has a handy use above and beyond protection from CSRF. It’s right there in the name, “state.”
Often times, the user will be deep into our UX, having filled out information or changed the status of the interface in some way prior to realizing they need to authenticate. When they click that link to login with a separate app, they’re being redirected. In a basic implementation all of the work the user had done, the current “state” of the app, would be lost in the redirects.
We can use the state token to embed information about the state of the app when the user logged in, if we’d like. We can also include the URL they were on, and even any additional information. Just be sure to use a unique, unguessable value alongside all of that information to ensure it’s a secure token. There’s a nice example of this in Google’s official OAuth 2.0 documentation here (look at the HTTP/REST example).
In the next post, we’ll handle the redirection from Google’s OAuth provider and exchange our shiny new grant code for an access token!
You can read the full tutorial series in these weekly installments:
Part 1: Authentication for the Modern Web
Part 2: Delegating Authentication
Part 3: Let’s Play on the OAuth 2.0 Playground
Part 4: Let’s Start Running Code
Part 5: Authorization Grant, Our First OAuth Dance Steps
Part 6: Authorization Request, An OAuth “State” of Mind
Part 7: Authorization Grant, Final OAuth Dance Steps
Part 8: Expanding our Service with Github
Part 9: OAuth 2.0: Authorization, Not Authentication
The series will conclude in April 2016.