Home > OpenID Connect OAuth Server by DnC > Develop > OpenID Connect > Verifying the origin of the request received by a resource server

Verifying the origin of the request received by a resource server

The transmission of tokens to protected resources implies that the latters are receptive to tokens of any origin. This presents an opportunity for an attacker (a foreign application) to exploit a compromised or stolen token.

Once more, the analysis reveals that we are only able to ensure data security in the case of the "Authorization Code" flow.

Consideration about the security of token transmission to resource servers (RS)

The proposed RFC 6749 standard is quite clear on the need to use access tokens only between authorized parties (§ 10.3.):

Access token credentials (as well as any confidential access token attributes) MUST be kept confidential in transit and storage, and only shared among the authorization server, the resource servers the access token is valid for, and the client to whom the access token is issued.

If all the resource servers (RS) to which the tokens are transmitted belong to the same organization (corporate realm), we can hope that the tokens will not be compromised, that is to say accessible to applications outside the organization. It can also be assumed that the resources are located in a subnet protected by a firewall that prohibits application requests from outside the subnet. It can then be assumed that any application directed to the resources is allowed to receive a response.

This is called "trusted applications". But note that, from the point of view of a resource server, trust does not result from a particular character of the application, but from the fact that it is located inside a space of confidence that we assume inaccessible to other applications.

However, even in this case, a well-crafted attack could lead to stealing the token and exploit it with a foreign application.

In any case, the assumptions are never very good in terms of security.

Of course, if you address a token to a foreign resource, you must consider the token as a compromise because it can be used by a foreign application.

Rather than rest on a notion of confidence area and trustable applications, the resource server must be able to verify the legitimacy of the token holder.

But how ?

The specification clearly describes this limitation (section 10.3):

This specification does not provide any methods for the resource server to ensure that an access token presented to it by a give client was issued to that client by the authorization server.

Two types of applications must be distinguished with regard to the origin of the request:
- applications "with back-end", ie sending their requests from a server: the request will be sent with the IP of the application server.
- The applications without back-end, issuing their request from a user-agent (the browser of the end user, the operating system of the host computer of a native application etc. .): the request will be issued with the IP of the user-agent connection, possibly masked by a proxy.

In the current state of development of OAuthSD, we will only consider "back end applications" whose IP is related to their identity. In other words, we are only able to ensure data security in the case of the "Authorization Code" flow.

OAuthSD offers two ways to verify the legitimacy of a "back-end" application presenting the token based on the IP of origin:
- by introspection with the IP of the applicant,
- with the additional declarations of the JWT token ’cip_hash’ or ’cip’.

The IP of the client application, to which the IP of the applicant will be compared, are determined as follows:
- if the client application is registered with a non-empty ’ip’ field of the ’clients’ table, OAuthSD will use this value,
- Otherwise, the IP (s) will be obtained by querying the DNS with the host name of the return URL (redirect URI) registered for the application [1] [2].

The general problem is developed here: Typology of applications with regard to data security.

Check by introspection with the IP of the applicant

The draft RFC 7662 implicitly recognizes the question, but does not give a standardized answer. It is indeed indicated in section 2.1:

The introspection endpoint MAY accept other OPTIONAL parameters to provide further context to the query. For instance, an authorization server may desire to know the IP address of the client accessing the protected resource to determine if the correct client is likely to be presenting the token. The definition of this or any other parameters are outside the scope of this specification, ....

OAuthSD do the following:
If the RS calling the Introspection endpoint transmits the IP address of its own requestor using the ’requester_ip’ parameter, introspection verifies that this IP address is in the same subnet as the client application identified by the ’aud’ declaration.

In order to avoid responding to a malware a protected resource receiving an identity token must validate it by passing the parameter ’requester_ip’.

On OAuthSD, for this verification to be effective, it is necessary to:
- fill in the IP of the client applications when they are registered (otherwise the domain IP of the return URI will be used),
- set to ’true’ the configuration constant CHECK_CLIENT_IP.

Notes:
- In general, the IP of the applications using the Introspect or UserInfo controllers is verified as indicated above, without being necessary to provide the ’requester_ip’ parameter, in order to verify that it is well of the registered client application. It will be understood that in the case of a request made to a resource server that calls the Introspect controller, it is necessary to provide the IP of the applicant, otherwise it would be the IP of the resource server that would be monitored, not the one of the requester.
- Note that there is also an optional verification of membership in the same domain or subdomain (see configuration constant CHECK_SAME_DOMAINS) performed by the Introspect controller.

Examples of calling introspection with the ’requester_ip’ parameter

By header:

PHP

  1. // Method Bearer + parameters by Post
  2. $data1 = array(
  3.     'requester_ip' => $_SERVER['SERVER_ADDR'],  
  4. );
  5. $authorization = "Authorization: Bearer ". $res1['id_token'];
  6. $h = curl_init($introspection_endpoint);
  7. curl_setopt($h, CURLOPT_HTTPHEADER, array(
  8.     'Content-Type: application/x-www-form-urlencoded',
  9.     'Authorization: Bearer ' . $res1['id_token']
  10. ));
  11. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  12. curl_setopt($h, CURLOPT_POST, 1);  
  13. curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data1));
  14.  
  15. $res = curl_exec($h);
  16.  
  17. ...

Download

By Post :

  1. // Post Methode  
  2. $data1 = array(
  3. 'token' => $res1['id_token'],
  4. 'requester_ip' => $_SERVER['SERVER_ADDR'],  
  5. );
  6.  
  7. $trace = $_SESSION['trace'];
  8. $trace .= '----- Step 3 : JWT Introspection -----' . "<br />";
  9. $trace .= 'access token : ' . $access_token . "<br />";
  10. $trace .= 'data : ' . print_r($data1,true) . "<br /><br />";
  11. $_SESSION['trace'] = $trace;    
  12.  
  13. $h = curl_init($introspection_endpoint);
  14. curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  15. curl_setopt($h, CURLOPT_TIMEOUT, 100);    //100 : DEBUG
  16. curl_setopt($h, CURLOPT_POST, true);
  17. curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  18. curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));  
  19. curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data1));
  20.  
  21. $res = curl_exec($h);
  22.  
  23. ...

Download

See also:
- API Open ID Connect : Introspection,
- More about Introspection,
- Envoi d’une requête à un serveur de ressource (RS).

Check with the JWT token ’cip_hash’ or ’cip’ additional claim

OAuthSD introduces a ’cip’ or ’cip_hash’ claim into the payload of the JWT token to enable verification of the IP of the applicant [3] [4].

This verification can be done locally, or by introspection called as described above with the parameter ’requester_ip’.

’cip_hash’
In the general case, an application has only one IP. In this case, it is this claim that will be generated, and not ’cip’, with the advantage of hiding the IP of the client application by transmitting it in the form of a hash. If the application has multiple IPs, and the FORCE_CIP_HASH configuration constant is true, the first IP in the list will be used to generate the declaration.

’cip’
The ’cip’ claim is generated when the IPs of the client application are multiple. Its value is a suite of space-separated IPv4.
However, if the FORCE_CIP_HASH configuration constant is true, the ’cip_hash’ claim will still be issued instead.

Notes:
- One of these additional claims will always be added to the JWT identity token payload of the OpenID Connect Authorization Code stream, but not in its OAuth 2.0 counterpart.
- This method has the advantage of allowing verification by the protected resource (remember that the payload of the JWT token is simply URL64-encoded ). This is useful for blocking an attack without introspection, or when validation of the JWT token is done locally without introspection.
- The Introspect controller will also perform IP verification with these claims. In the case of a resource server, it is necessary to transmit the parameter ’requester_ip’.
- In principle, there should not be a proxy in front of an authentication server, which must have access to the network (including its own router-firewall). Any proxy located between the network and the authentication server must faithfully relay the IPs. Of course, reverse proxies are prohibited in front of an authentication server.

Alternate method: with JWT token audience claim

The oauth2-server-php library reduces "aud" to the client_id client id.

Yet the specification provides:

It MAY also contain identifiers for other audiences. In the general case, the aud value is an array of case sensitive strings."

Some implementations of OpenID Connect consider that the audience claim is application-level, which means that its value is a convention established within the same organization (corporate realm) between the application and the resource server.

With this in mind, the "aud" claim is a table of strings that can contain the IPs of the applications authorized to present the token. This will allow the resource server to directly check the IP and thus block some malware without having to issue an introspection request.

Since the audience claim is defined in the oauth2-server-php library, we must modify it to integrate the IP of the client application (provided when the application was registered on the server) in the "aud" claim. This development is underway.

Taking into account a "trusted proxy"

The question is whether one can trust the HTTP_X_FORWARDED_FOR declaration by which a proxy retransmits the IP of the original request.

Any proxy located between the Internet and the authentication server is controlled by the organization and can be considered trusted.
OAuthSD allows to designate these proxies by their IP in the configuration constant TRUSTED_PROXIES. If the USE_PROXY configuration constant is true, OAuthSD will replace for IP verification these IPs by the one provided by the HTTP_X_FORWARDED_FOR declaration.

Notes:
- Among the trusted proxies we can find the reverse proxies whose function is to provide a cache system. The very principle of authentication excludes the use of cache.
- Among the trusted proxies are also load balancers. Some of these proxies do not have a fixed IP. In this case, it will be impossible to apply the client verification by IP.
- It will be necessary to verify that the trusted proxy only retransmits the real IP of the request by the declaration HTTP_X_FORWARDED_FOR, unless an identical mechanism makes it possible to recognize the trusted upstream proxies.
- Note that this concept of trusted proxy is the opposite of what a large number of Internet users refer to as the proxy they expect to hide their IP address. Such proxies call themselves "anonymous" and are referred to as "private proxy", which has nothing to do with the fact that they are private, but that they aim to ensure the anonymity (privacy) of the user.
OAuthSD has a "SAFEIP" feature to block questionable IP addresses, most of which are those of such proxies. We are in the presence of two complementary mechanisms: The taking into account of trusted proxies allows to authorize the proxies of the LAN / Web interface, while the "SAFEIP" function will block the suspicious proxies of the Web.

Footnotes

[1It is therefore important to ensure the integrity of the DNS. It would be best if the DNS of the applications and the DNS to which the server is accessing (preferably a local DNS) are in a trusted space, and / or protected by DNSSEC.

[2This method can introduce an unwanted response time of the resource, which is why OAuthSD offers the registration of the IP.

[3We extol the interest of the signed JWT identity token to link the identity of the end user, the application and the authorization scopes. However, a HTTP resource server can only identify the origin of the request by its IP in the server-server relationship. The identifier of the client transmitted by the token is not convincing since the token may have been "stolen". It is therefore essential to incorporate in the JWT token the IP of the legitimate application so that a resource server can check it.

[4Can the ’aud’ declaration be used to transmit this URL? This seems to be the case if we stick to the JWT specification for which ’aud’ can be a list of URIs. But the OpenID Connect is more restrictive:

aud
MANDATORY. Audience (s) for which this identifier token is intended. It MUST contain the client_id OAuth 2.0 of the trusted party as the audience value. It MAY also contain identifiers for other audiences. In the general case, the aud value is an array of case-sensitive strings. In the usual special case where there is an audience, the aud value MAY be a case-sensitive string.

As a result, the oauth2-server-php library sets the value of the ’aud’ parameter to the identifier of the client application. The ’aud’ declaration is not the right choice for OAuthSD to propagate client IP.