Disclaimer: This is not a comprehensive or complete security guide. 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 authentication scheme 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 risks. 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 (client) and those of the party delegating its access (resource owner). 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 not available. 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.
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 client identifies itself using its client identifier and client secret, while the resource owner is identified by an access token and token secret. Each set can be thought of as a username-password pair (one for the application and one for the end-user).
But while the client credentials work much like a username and password, the User is represented by an access token which is different than their actual username and password. This allows the server and resource owner greater control and flexibility in granting client access. For example, the resource owner can revoke an access token without having to change passwords and break other applications. The decoupling of the resource owner’s username and password from the access token is one of the most fundamental aspects of the OAuth architecture.
OAuth includes two type of tokens: temporary credentials and access token. Each type has a very specific role in the OAuth flow. 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 resource owner and request authorization, and a back-channel used by the client to directly interact with the server.
By limiting the access token to the back-channel, the token itself remains concealed from the resource owner and its browser. This allows the access token to carry special meanings and to have a larger size than the front-channel temporary credentials which are exposed to the resource owner 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 methods are 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 client secret and token secret. In the RSA-SHA1 method, the client 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 client and server — use a one key to sign the request and another key to verify the request.
The keys — private key for the client and public key for the server — 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 server does not have access to the client’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 client secret either but that is adequately replaced by the client 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 client secret) does not allow access to other resources protected by other tokens (and their secrets).
When implementing OAuth, it is critical to understand the limitations of shared secrets, symmetric or asymmetric. The client secret (or private key) is used to verify the identity of the client by the server. In case of a web-based client such as web server, it is relatively easy to keep the client secret (or private key) confidential.
This does not prevent using OAuth within such application, but it does limit the amount of trust the server can have in such public secrets. Since the secrets cannot be trusted, the server must treat such application as unknown entities and use the client identity only for activities that do not require any level of trust, such as collecting statistics about applications. Some servers may opt to ban such application or offer different protocols or extensions. However, at this point there is no known solution to this limitation.
It is important to note, that even though the client credentials are leaked in such application, the resource owner credentials (token and secret) are specific to each instance of the client which protects their security properties. This of course greatly depends on the client implementation and how it stores token information on the client side.
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 client generates a unique string for each request sent to the server, and the server 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 the server 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 server 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 server 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. 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.