As an authorization delegation protocol, OAuth must be secure and allow the Service Provider to trust the Consumer and validate the credential provided to gain access. To accomplish that, OAuth defines a method for validating the authenticity of HTTP requests. This method is called Signing Requests and in order to understand it, we must first explore the security features and architecture of the protocol, which will be the focus of this part of the Beginner’s Guide.
In the following part we will explore how all this comes together and translates into the OAuth signature workflow using interactive examples. The examples in this post cannot be viewed in a feed reader.
Disclaimer: This tutorial is not a comprehensive, complete, or accurate security guide. It should not be used for making any security related decisions, and any implementation must be reviewed by security experts to ensure its safety. The OAuth specification provides a good starting point for considering the security ramification of any implementation, but as is usually the case when it comes to security, the specification must not be viewed as complete. This guide takes some liberties explaining complex security concepts for the purpose of making them more accessible, but includes references for more in-depth reading.
HTTP defines an authorization method called ‘Basic’ which is commonly used by many sites and APIs. The way ‘Basic’ works is by sending the username and password in plain text with each request. When not used over HTTPS, ‘Basic’ suffers from significant security flaws and limitations, but for this discussion we will focus on three. First, it transmits passwords unencrypted which allows anyone listening to capture and reuse those credentials. Second, there is nothing linking the credentials to the request, which means once compromised, they can be used with any request without limitations. Third, ‘Basic’ does not provide a placeholder for delegation credentials and only supports a single username-password pair. Delegation requires being able to send both the credentials of the caller (Consumer) and those of the party delegating its access (User). The OAuth architecture explicitly addresses these three limitations.
The OAuth signature method was primarily designed for insecure communications — mainly non-HTTPS. HTTPS is the recommended solution to prevent a man-in-the-middle attack (MITM), eavesdropping, and other security risks. However, HTTPS is often too expensive for many applications both to setup and maintain. When OAuth is used over HTTPS, it offers a simple method for a more efficient implementation called PLAINTEXT which offloads most of the security requirements to the HTTPS layer. It is important to understand that PLAINTEXT should not be used over an insecure channel. This tutorial will focus on the methods designed to work over an insecure channel: HMAC-SHA1 and RSA-SHA1.
Direct & Delegated Access
OAuth’s stated objective is to create an ‘authorization delegation protocol’. Allowing one party to access someone else’s resources on their behalf is the core of the OAuth protocol and the void it seeks to fill. In this delegated access scenario, also known as the 3-legged scenario, the three parties (legs) involved are the Service Provider, Consumer, and User. Since requests are only made by the Consumer, it needs a way to authenticate itself with the Service Provider, but also to authenticate its authorization to access User data. This requires OAuth to support an HTTP request with two sets of credentials.
OAuth can do an equally good job at addressing the direct access scenario, also known as the 2-legged scenario. This is the case most people are familiar with in which one side authenticates with the other, for example, a person logging to a site using a username and password. In this scenario, no one else is involved and no access is being delegated. The Consumer is accessing the resources on behalf of itself, making the Consumer and User the same entity.
This can be a bit confusing when a Consumer is making separate requests, both 2-legged requests on behalf of itself, and 3-legged requests on behalf of a User. When thinking about the signature workflow, it helps to understand what each credential is designed to accomplish. Since the Consumer credentials are used to authenticate the party making the request, if that party is making the request on its own behalf, it is also the end-user of the request. Application wishing to use OAuth as an alternative to HTTP ‘Basic’, should use the the Consumer Key and Consumer Secret to hold the username and password, and leave the Token and Token Secret empty. This of course requires direct application support and not something clients can just try to use where HTTP ‘Basic’ is accepted.
Since this guide is focused on OAuth’s primary use case which is delegated authorization, the rest of this tutorial will focus on the 3-legged scenario (unless stated otherwise).
In everyday web transactions, the most common credential used is the username-password combination. OAuth’s primary goal is to allow delegated access to private resources. This is done using two sets of credentials: the Consumer identifies itself using its Consumer Key and Consumer Secret, while the User is identified by a Token and Token Secret. Each set — Consumer Key-Secret and Token-Secret — can be thought of as a username-password pair (one for the application and one for the end-user). But while the Consumer credentials work much like a username and password, the User is represented by a Token which is different than their actual username and password. This allows the Service Provider and User greater control and flexibility in granting Consumer access. For example, the User can revoke a Token without having to change passwords and break other applications. The decoupling of the User’s username and password from the Token is one of the most fundamental aspects of the OAuth architecture.
OAuth includes two kind of Tokens: Request Token and Access Token. Each Token has a very specific role in the OAuth delegation workflow. While mostly an artifact of how the OAuth specification evolved, the two-Token design offers some usability and security features which made it worthwhile to stay in the specification. OAuth operates on two channels: a front-channel which is used to engage the User and request authorization, and a back-channel used by the Consumer to directly interact with the Service Provider. By limiting the Access Token to the back-channel, the Token itself remains concealed from the User. This allows the Access Token to carry special meanings and to have a larger size than the front-channel Request Token which is exposed to the User when requesting authorization, and in some cases needs to be manually entered (mobile device or set-top box).
The request signing workflow treats all Tokens the same and the workflow is identical. The two Tokens are specific to the authorization workflow, not the signature workflow which uses the Tokens equally. This does not mean the two Token types are interchangeable, just that they provide the same security function when signing requests.
Signature and Hash
OAuth uses digital signatures instead of sending the full credentials (specifically, passwords) with each request. Similar to the way people sign documents to indicate their agreement with a specific text, digital signatures allow the recipient to verify that the content of the request hasn’t changed in transit. To do that, the sender uses a mathematical algorithm to calculate the signature of the request and includes it with the request.
In turn, the recipient performs the same workflow to calculate the signature of the request and compares it to the signature value provided. If the two match, the recipient can be confident that the request has not been modified in transit. The confidence level depends on the properties of the signature algorithm used (some are stronger than others). This mechanism requires both sides to use the same signature algorithm and apply it in the same manner.
A common way to sign digital content is using a hash algorithm. In general, hashing is the process of taking data (of any size) and condensing it to a much smaller value (digest) in a fully reproducible (one-way) manner. This means that using the same hash algorithm on the same data will always produce the same smaller value. Unlike compression which aims to preserve much of the original uncompressed data, hashing usually does not allow going from the smaller value back to the original.
By itself, hashing does not verify the identity of the sender, only data integrity. In order to allow the recipient to verify that the request came from the claimed sender, the hash algorithm is combined with a shared secret. If both sides agree on some shared secret known only to them, they can add it to the content being hashed. This can be done by simply appending the secret to the content, or using a more sophisticated algorithm with a built-in mechanism for secrets such as HMAC. Either way, producing and verifying the signature requires access to the shared secret, which prevents attackers from being able to forge or modify requests.
The benefit of this approach compared to the HTTP ‘Basic’ authorization scheme is that the actual secret is never sent with the request. The secret is used to sign the request but it is not part of it, nor can it be extracted (when implemented correctly). Signatures are a safer way to accomplish the same functionality of sending the shared secret with the request over an unsecure channel.
In OAuth, the shared secret depends on the signature method used. In the PLAINTEXT and HMAC-SHA1 methods, the shared secret is the combination of the Consumer Secret and Token Secret. In the RSA-SHA1 method, the Consumer Private Key is used exclusively to sign requests and serves as the asymmetric shared secret. The way asymmetric key-pairs work, is that each side — the Consumer and Service Provider — use a one key to sign the request and another key to verify the request. The keys — Private Key for Consumer and Public Key for the Service Provider — must match, and only the right pair can successful sign and verify the request. The advantage of using asymmetric shared secrets is that even the Service Provider does not have access to the Consumer’s Private Key which reduces the likelihood of the secret being leaked.
However, since the RSA-SHA1 method does not use the Token Secret (it doesn’t use the Consumer Secret either but that is adequately replaced by the Consumer Private Key), the Private Key is the only protection against attacks and if compromised, puts all Tokens at risk. This is not the case with the other methods where one compromised Token Secret (or even Consumer Secret) does not allow access to other resources protected by other Tokens (and their Secrets).
This does not prevent using OAuth within such application, but it does limit the amount of trust Service Provider can have in such public secrets. Since the secrets cannot be trusted, Service Provider must treat such application as unknown entities and use the Consumer identity only for activities that do not require any level of trust, such as collecting statistics about applications. Some Service Provider may opt to ban such application or offer different protocols or extensions. However, at this point there is no known simple solution to this limitation.
It is important to note, that even though the Consumer credentials are leaked in such application, the User credentials (Token and Secret) are specific to each instance of the Consumer which protects their security properties. This of course greatly depends on the Consumer implementation and how it stores Token information on the client side.
This is somewhat different in a 2-legged scenario since the Consumer credentials are basically just a username and password and there is no Token or Token Secret. When OAuth is used as a direct replacement for HTTP ‘Basic’ making the Consumer and User the same entity, the application can be written to prompt Users to enter their credentials, hence removing the need to hard-code them into the application itself. Using OAuth for simple sign-in has the same user experience as HTTP ‘Basic’, but when used over an insecure channel OAuth provides much greater security.
Timestamp and Nonce
The signature and shared secret provide some level of security but are still vulnerable to attacks. The signature protects the content of the request from changing while the shared secret ensures that requests can only be made (and signed) by an authorized Consumer. What is missing is something to prevent requests intercepted by an unauthorized party, usually by sniffing the network, from being reused. This is known as a replay attack. As long as the shared secrets remains protected, anyone listening in on the network will not be able to forge new requests as that will require using the shared secret. They will however, be able to make the same sign request over and over again. If the intercepted request provides access to sensitive protected data, it can be a significant security risk.
To prevent compromised requests from being used again (replayed), OAuth uses a nonce and timestamp. The term nonce means ‘number used once’ and is a unique and usually random string that is meant to uniquely identify each signed request. By having a unique identifier for each request, the Service Provider is able to prevent requests from being used more than once. This means the Consumer generates a unique string for each request sent to the Service Provider, and the Service Provider keeps track of all the nonces used to prevent them from being used a second time. Since the nonce value is included in the signature, it cannot be changed by an attacker without knowing the shared secret.
Using nonces can be very costly for Service Providers as they demand persistent storage of all nonce values received, ever. To make implementations easier, OAuth adds a timestamp value to each request which allows the Service Provider to only keep nonce values for a limited time. When a request comes in with a timestamp that is older than the retained time frame, it is rejected as the Service Provider no longer has nonces from that time period. It is safe to assume that a request sent after the allowed time limit is a replay attack. OAuth provides a general mechanism for implementing timestamps but leaves the actual implementation up to each Service Provider (an area many believe should be revisited by the specification). From a security standpoint, the real nonce is the combination of the timestamp value and nonce string. Only together they provide a perpetual unique value that can never be used again by an attacker.
OAuth defines 3 signature methods used to sign and verify requests: PLAINTEXT, HMAC-SHA1, and RSA-SHA1.. PLAINTEXT is intended to work over HTTPS and in a similar fashion to how HTTP ‘Basic’ transmits the credentials unencrypted. Unlike ‘Basic’, PLAINTEXT supports delegation. The other two methods use the HMAC and RSA signature algorithm combined with the SHA1 hash method. Since these methods are too complex to explain in this guide, implementers are encouraged to read other guides specific to them, and not to write their own implementations, but instead use trusted open source solutions available for most languages.
When signing requests, it is necessary to specify which signature method has been used to allow the recipient to reproduce the signature for verification. The decision of which signature method to use depends on the security requirements of each application. Each method comes with its set of advantages and limitations. PLAINTEXT is trivial to use and takes significantly less time to calculate, but can only be safe over HTTPS or similar secure channels. HMAC-SHA1 offers a simple and common algorithm that is available on most platforms but not on all legacy devices and uses a symmetric shared secret. RSA-SHA1 provides enhanced security using key-pairs but is more complex and requires key generation and a longer learning curve.
Signature Base String
As explained above, both sides must perform the signature process in an identical manner in order to produce the same result. Not only must they both use the same algorithm and share secret, but they must sign the same content. This requires a consistent method for converting HTTP requests into a single string which is used as the signed content — the Signature Base String.