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: <yamdwe
nowiki>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)