This site is the archived OWASP Foundation Wiki and is no longer accepting Account Requests.
To view the new OWASP Foundation website, please visit https://owasp.org

Difference between revisions of "CORS OriginHeaderScrutiny"

From OWASP
Jump to: navigation, search
m
(That's really all you need to know - don't trust the origin header, do your own authentication. Full stop. This additional rambling about having to manage users and passwords is muddying the waters, and isn't neces. true (not all auth is password auth))
 
(8 intermediate revisions by 5 users not shown)
Line 1: Line 1:
Last revision (mm/dd/yy): '''10/12/2012'''
+
{{taggedDocument
 +
| type=pls_review
 +
}}
  
 
== Introduction ==
 
== Introduction ==
 +
 
'''CORS''' stands for '''C'''ross-'''O'''rigin '''R'''esource '''S'''haring.  
 
'''CORS''' stands for '''C'''ross-'''O'''rigin '''R'''esource '''S'''haring.  
  
 
+
Is a feature offering the possibility for:
Is an feature offering the possbility to:
 
 
* A web application to expose resources to all or restricted domain,
 
* A web application to expose resources to all or restricted domain,
* A web client to made AJAX request for resource on other domain than is source domain.
+
* A web client to make AJAX request for resource on other domain than is source domain.
 
 
 
 
This article will focus on role of the '''Origin''' header in exchange between web client and web application.
 
 
 
  
The basic process is composed by steps below (sample HTTP resquest/response has been taken from [https://developer.mozilla.org/en-US/docs/HTTP_access_control Mozilla Wiki]):
+
This article will focus on the role of the '''Origin''' header in the exchange between web client and web application.
  
 +
The basic process is composed of the steps below (sample HTTP request/response has been taken from [https://developer.mozilla.org/en-US/docs/HTTP_access_control Mozilla Wiki]):
  
 +
* '''Step 1 : Web client sends a request to get a resource from a different domain.'''
  
* '''Step 1 : Web client send request to get resource from a different domain.'''
 
 
<pre>
 
<pre>
 
GET /resources/public-data/ HTTP/1.1
 
GET /resources/public-data/ HTTP/1.1
Line 32: Line 31:
 
[Request Body]
 
[Request Body]
 
</pre>
 
</pre>
The web client inform is source domain using the HTTP request header "'''Origin'''".
 
  
 +
The web client tells the server its source domain using the HTTP request header "'''Origin'''".
 +
 +
* '''Step 2 : Web application response to request.'''
  
* '''Step 2 : Web application respond to request.'''
 
 
<pre>
 
<pre>
 
HTTP/1.1 200 OK
 
HTTP/1.1 200 OK
Line 48: Line 48:
 
[Response Body]
 
[Response Body]
 
</pre>
 
</pre>
The web application informs web client of the allowed domain using the HTTP response header '''Access-Control-Allow-Origin'''.
 
The header can contains a '*' to indicate that all domain are allowed OR a specified domain to indicate the specified allowed domain.
 
  
 +
The web application informs the web client of the allowed domains using the HTTP response header '''Access-Control-Allow-Origin'''.
 +
The header can contains either a '*' to indicate that all domains are allowed OR a specified domain to indicate the specified allowed domain.
  
 
* '''Step 3 : Web client process web application response.'''
 
* '''Step 3 : Web client process web application response.'''
According to the CORS W3C specification, it's up to the web client (usually a browser) to determine, using the web application response HTTP header '''Access-Control-Allow-Origin''', if the web client is allowed to access response data....
+
 
 +
According to the CORS W3C specification, it's up to the web client (usually a browser) to determine, using the web application response HTTP header '''Access-Control-Allow-Origin''',  
 +
if the web client is allowed to access response data.
  
 
== Risk ==
 
== Risk ==
''A reminder : Into this article we focus on web application side because it's the only part in which we have full control.''
 
  
The risk here that a web client can put any value into the '''Origin''' request HTTP header in order to force web application to provide it the target resource content. In the case of a Browser web client the header value is managed by the browser but the computer can contains malware or another web client can be used like Curl,OWASP Zap Proxy,....
+
''A reminder : This article will focus on the web application side because it's the only part in which we have the maximum of control.''
 +
 
 +
The risk here is that a web client can put any value into the '''Origin''' request HTTP header in order to force web application to provide it the target resource content.  
 +
In the case of a Browser web client, the header value is managed by the browser but another "web client" can be used (like Curl/Wget/Burp suite/...) to change/override the "Origin" header value. For this reason it is not recommended to use the Origin header to authenticate requests as coming from your site.
  
 
== Countermeasure ==
 
== Countermeasure ==
We must scrutiny the '''Origin''' value on server side.
 
  
To achieve it we will use JEE Web Filter that will ensure that the domain received is consistent with the HTTP request  domain source.
+
Enable authentication on the resources accessed and require that the user/application credentials be passed with the [https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS#Requests_with_credentials CORS requests].
  
'''Sample implementation: Filter class'''
+
It is not possible to be 100% certain that any request comes from an expected client application, since all information of a HTTP request can be faked.
<pre>
+
== Informations links ==
import java.io.IOException;
 
import java.net.InetAddress;
 
import java.net.UnknownHostException;
 
import java.util.Enumeration;
 
 
 
import javax.servlet.Filter;
 
import javax.servlet.FilterChain;
 
import javax.servlet.FilterConfig;
 
import javax.servlet.ServletException;
 
import javax.servlet.ServletRequest;
 
import javax.servlet.ServletResponse;
 
import javax.servlet.annotation.WebFilter;
 
import javax.servlet.http.HttpServletRequest;
 
import javax.servlet.http.HttpServletResponse;
 
  
import net.sf.ehcache.Cache;
 
import net.sf.ehcache.CacheManager;
 
import net.sf.ehcache.Element;
 
import net.sf.ehcache.config.CacheConfiguration;
 
import net.sf.ehcache.config.PersistenceConfiguration;
 
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
 
 
/**
 
* Sample filter implementation to scrutiny CORS "Origin" HTTP header.<br/>
 
*
 
* This implementation has a dependency on EHCache API because<br/>
 
* it use Caching for reverse DNS resolving result in order to enhance performance.
 
*
 
* Assume here that all CORS resources are grouped in context path "/cors/".
 
*
 
*/
 
@WebFilter("/cors/*")
 
public class CORSOriginHeaderScrutiny implements Filter {
 
 
// Filter configuration
 
@SuppressWarnings("unused")
 
private FilterConfig filterConfig = null;
 
 
// Cache used to cache Domain's resolved IP address
 
private Cache domainsIPCache = null;
 
 
/**
 
* {@inheritDoc}
 
*
 
* @see Filter#init(FilterConfig)
 
*/
 
@Override
 
public void init(FilterConfig fConfig) throws ServletException {
 
// Get filter configuration
 
this.filterConfig = fConfig;
 
// Initialize Domain IP address dedicated cache with a cache of 15 minutes expiration delay for each item
 
PersistenceConfiguration cachePersistence = new PersistenceConfiguration();
 
cachePersistence.strategy(PersistenceConfiguration.Strategy.NONE);
 
CacheConfiguration cacheConfig = new CacheConfiguration().memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.FIFO).eternal(false).timeToLiveSeconds(900).statistics(false)
 
.diskExpiryThreadIntervalSeconds(450).persistence(cachePersistence).maxEntriesLocalHeap(10000).logging(false);
 
cacheConfig.setName("DomainsCacheConfig");
 
this.domainsIPCache = new Cache(cacheConfig);
 
this.domainsIPCache.setName("DomainsCache");
 
CacheManager.getInstance().addCache(this.domainsIPCache);
 
}
 
 
/**
 
* {@inheritDoc}
 
*
 
* @see Filter#destroy()
 
*/
 
@Override
 
public void destroy() {
 
// Remove Cache
 
CacheManager.getInstance().removeCache("DomainsCache");
 
}
 
 
/**
 
* {@inheritDoc}
 
*
 
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
 
*/
 
@Override
 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 
HttpServletRequest httpRequest = ((HttpServletRequest) request);
 
HttpServletResponse httpResponse = ((HttpServletResponse) response);
 
Enumeration<String> headerNames = httpRequest.getHeaderNames();
 
boolean isValid = false;
 
String origin = null;
 
String headerName = null;
 
String domainIP = null;
 
 
/* Step 1 : Retrieve the value of the "Origin" HTTP request header */
 
while (headerNames.hasMoreElements()) {
 
headerName = headerNames.nextElement();
 
if ("origin".equalsIgnoreCase(headerName)) {
 
origin = httpRequest.getHeader(headerName);
 
break;
 
}
 
}
 
 
/* Step 2 : Perform analysis */
 
// Origin header is required
 
if ((origin != null) && !"".equals(origin.trim())) {
 
try {
 
// Remove HTTP / HTTPS protocols
 
origin = origin.toLowerCase();
 
origin = origin.replaceFirst("http://", "");
 
origin = origin.replaceFirst("https://", "");
 
 
// Get IP address of the specified domain
 
if (this.domainsIPCache.get(origin) != null) {
 
// First using Cache
 
domainIP = (String) this.domainsIPCache.get(origin).getValue();
 
} else {
 
// Second using reverse DNS and update Cache
 
InetAddress clientAddress = InetAddress.getByName(origin);
 
if (clientAddress != null) {
 
domainIP = clientAddress.getHostAddress();
 
Element cacheElement = new Element(origin, domainIP);
 
this.domainsIPCache.put(cacheElement);
 
}
 
}
 
 
// Compare IP addresses : Specified domain IP address against HTTP request sender IP address
 
if ((domainIP != null) && domainIP.equals(httpRequest.getRemoteAddr())) {
 
isValid = true;
 
}
 
}
 
catch (UnknownHostException uhe) {
 
// We print stack trace here for sample but in real app. a notification must be sent
 
// to monitoring system in order to log malicious request...
 
uhe.printStackTrace();
 
}
 
}
 
 
// Finalize request next step
 
if (isValid) {
 
// Analysis OK then pass the request along the filter chain
 
chain.doFilter(request, response);
 
} else {
 
// Return HTTP Error without any information about cause of the request reject !
 
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
 
}
 
}
 
 
}
 
</pre>
 
 
'''Note:'''
 
[[Automated Audit using W3AF|W3AF]] audit tools (http://w3af.org) contains plugins to automatically audit web
 
application to check if they implements this type of countermeasure.
 
 
<pre style="color:#088A08">
 
It's very useful to include this type of tools into a web application development process in order to
 
perform a regular automatic first level check (do not replace an manual audit and manual audit must be also conducted regularly).
 
</pre>
 
 
== Informations links ==
 
 
* W3C Specification : http://www.w3.org/TR/cors/
 
* W3C Specification : http://www.w3.org/TR/cors/
 
* Mozilla Wiki : https://developer.mozilla.org/en-US/docs/HTTP_access_control
 
* Mozilla Wiki : https://developer.mozilla.org/en-US/docs/HTTP_access_control
 
* Wikipedia : http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
 
* Wikipedia : http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
 +
* CORS Abuse : http://blog.secureideas.com/2013/02/grab-cors-light.html
  
[[Category:OWASP Java Project]]
+
[[Category:Java]]
[[Category: Injection]]
 
 
[[Category:Attack]]
 
[[Category:Attack]]
 
[[Category:Injection Attack]]
 
[[Category:Injection Attack]]

Latest revision as of 20:05, 25 February 2019

This Page has been flagged for review. Please help OWASP and review this Page to FixME.

Introduction

CORS stands for Cross-Origin Resource Sharing.

Is a feature offering the possibility for:

  • A web application to expose resources to all or restricted domain,
  • A web client to make AJAX request for resource on other domain than is source domain.

This article will focus on the role of the Origin header in the exchange between web client and web application.

The basic process is composed of the steps below (sample HTTP request/response has been taken from Mozilla Wiki):

  • Step 1 : Web client sends a request to get a resource from a different domain.
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example

[Request Body]

The web client tells the server its source domain using the HTTP request header "Origin".

  • Step 2 : Web application response to request.
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61 
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
Access-Control-Allow-Origin: *

[Response Body]

The web application informs the web client of the allowed domains using the HTTP response header Access-Control-Allow-Origin. The header can contains either a '*' to indicate that all domains are allowed OR a specified domain to indicate the specified allowed domain.

  • Step 3 : Web client process web application response.

According to the CORS W3C specification, it's up to the web client (usually a browser) to determine, using the web application response HTTP header Access-Control-Allow-Origin, if the web client is allowed to access response data.

Risk

A reminder : This article will focus on the web application side because it's the only part in which we have the maximum of control.

The risk here is that a web client can put any value into the Origin request HTTP header in order to force web application to provide it the target resource content. In the case of a Browser web client, the header value is managed by the browser but another "web client" can be used (like Curl/Wget/Burp suite/...) to change/override the "Origin" header value. For this reason it is not recommended to use the Origin header to authenticate requests as coming from your site.

Countermeasure

Enable authentication on the resources accessed and require that the user/application credentials be passed with the CORS requests.

It is not possible to be 100% certain that any request comes from an expected client application, since all information of a HTTP request can be faked.

Informations links