User Tools

Site Tools


hack:cors_origin_header_scrutiny

Introduction

CORS représente la Cross-Origin Resource Sharing.

C'est une caractéristique offrant la possibilité de : Une application web pour exposer les ressources à tout le domaine ou restreint,

Un client web pour faire une requête AJAX pour la ressource sur d'autre domaine qu'est le domaine source.

Cet article se concentrera sur le rôle de l'en-tête d'Origine dans l'échange entre le client web et l'application web.

Le processus basique est composé des étapes ci-dessous (un exemple de resquête/réponse HTTP a été pris de Mozilla Wiki):

  • Etape 1 : le client Web envoie une requête pour recevoir la ressource d'un domaine différent. 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: <yamdwenowiki>0</yamdwenowiki> Origin: <yamdwenowiki>1</yamdwenowiki>
    [Request Body]

Le client web informe qu'il est le domaine source en utilisant l'en-tête de requête HTTP d<yamdwenowiki>2</yamdwenowiki>Origine. * Etape 2 : l'application Web réspond à la requête. 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] L'application web informe le client web du domaine autorisé utilisant l'en-tête de réponse HTTP Access-Control-Allow-Origin. L'en-tête peut contenir un '*' pour indiquer que tout le domaine est autorisé OU un domaine spécifié pour indiquer le domaine autorisé spécifié. * Etape 3 :le client Web traite la réponse de l'application web.
Conformément à la spécification CORS W3C, il appartient au client web (généralement un navigateur) pour déterminer, à l'aide de l'en-tête de la réponse HTTP de l'application web Access-Control-Allow-Origin, que contrôle d'approche “d'en-tête de HTTP permet l'origine”, si le client web est autorisé à accéder aux données de réponse…. # Risque # Un mémento : Dans cet article nous nous concentrons sur le côté application web parce que c'est la seule partie dans laquelle nous avons le contrôle total. Le risque ici est qu'un client web peut mettre n'importe quelle valeur dans l'Origine de l'en-tête de réponse HTTP pour forcer l'application web à lui fournir le contenu de ressource cible prévue. Dans le cas d'un client web de Navigateur la valeur d'en-tête est dirigée par le navigateur mais l'ordinateur peut contenir un malware ou un autre client web peut être utilisé comme Curl,OWASP Zap Proxy,…. # Contre-mesure # Nous devons l'examiner la valeur d'Origine côté serveur. Pour y parvenir nous allons utiliser JEE Web Filter qui garantira que le domaine reçu est en accord avec la requête HTTP de domaine source. implémentation: Filter class <code>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. * * This implementation has a dependency on EHCache API because * 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 = 1)

1)
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);
             }
     }
} </code> : les outils d'audit de W3AF (http://w3af.sourceforge.net) contiennent des plugins pour automatiquement auditer l'application web pour vérifier s'ils exécutent ce type de contre-mesure. Il est très utile d'inclure ce type d'outils dans un processus de développement d'application web afin de procéder pèriodiquement à une vérification regulière de premier niveau (ne pas remplacer un audit manuel et un audit manuel doit également être effectué régulièrement). # Réferences # W3C Specification : http://www.w3.org/TR/cors/ Mozilla Wiki : https://developer.mozilla.org/en-US/docs/HTTP_access_control Wikipedia : http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
hack/cors_origin_header_scrutiny.txt · Last modified: 2019/02/13 13:10 by 127.0.0.1