2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "CrossOriginAccessControl.h"
30 #include "HTTPParsers.h"
31 #include "ResourceResponse.h"
32 #include "SecurityOrigin.h"
33 #include <wtf/HashSet.h>
34 #include <wtf/Threading.h>
35 #include <wtf/text/AtomicString.h>
36 #include <wtf/text/StringBuilder.h>
40 bool isOnAccessControlSimpleRequestMethodWhitelist(const String& method)
42 return method == "GET" || method == "HEAD" || method == "POST";
45 bool isOnAccessControlSimpleRequestHeaderWhitelist(const String& name, const String& value)
47 if (equalIgnoringCase(name, "accept")
48 || equalIgnoringCase(name, "accept-language")
49 || equalIgnoringCase(name, "content-language")
50 || equalIgnoringCase(name, "origin")
51 || equalIgnoringCase(name, "referer"))
54 // Preflight is required for MIME types that can not be sent via form submission.
55 if (equalIgnoringCase(name, "content-type")) {
56 String mimeType = extractMIMETypeFromMediaType(value);
57 return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded")
58 || equalIgnoringCase(mimeType, "multipart/form-data")
59 || equalIgnoringCase(mimeType, "text/plain");
65 bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap& headerMap)
67 if (!isOnAccessControlSimpleRequestMethodWhitelist(method))
70 HTTPHeaderMap::const_iterator end = headerMap.end();
71 for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) {
72 if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->first, it->second))
79 typedef HashSet<String, CaseFoldingHash> HTTPHeaderSet;
80 static PassOwnPtr<HTTPHeaderSet> createAllowedCrossOriginResponseHeadersSet()
82 OwnPtr<HTTPHeaderSet> headerSet = adoptPtr(new HashSet<String, CaseFoldingHash>);
84 headerSet->add("cache-control");
85 headerSet->add("content-language");
86 headerSet->add("content-type");
87 headerSet->add("expires");
88 headerSet->add("last-modified");
89 headerSet->add("pragma");
91 return headerSet.release();
94 bool isOnAccessControlResponseHeaderWhitelist(const String& name)
96 AtomicallyInitializedStatic(HTTPHeaderSet*, allowedCrossOriginResponseHeaders = createAllowedCrossOriginResponseHeadersSet().leakPtr());
98 return allowedCrossOriginResponseHeaders->contains(name);
101 void updateRequestForAccessControl(ResourceRequest& request, SecurityOrigin* securityOrigin, StoredCredentials allowCredentials)
103 request.removeCredentials();
104 request.setAllowCookies(allowCredentials == AllowStoredCredentials);
105 request.setHTTPOrigin(securityOrigin->toString());
108 ResourceRequest createAccessControlPreflightRequest(const ResourceRequest& request, SecurityOrigin* securityOrigin, StoredCredentials allowCredentials)
110 ResourceRequest preflightRequest(request.url());
111 updateRequestForAccessControl(preflightRequest, securityOrigin, allowCredentials);
112 preflightRequest.setHTTPMethod("OPTIONS");
113 preflightRequest.setHTTPHeaderField("Access-Control-Request-Method", request.httpMethod());
114 preflightRequest.setPriority(request.priority());
116 const HTTPHeaderMap& requestHeaderFields = request.httpHeaderFields();
118 if (requestHeaderFields.size() > 0) {
119 StringBuilder headerBuffer;
120 HTTPHeaderMap::const_iterator it = requestHeaderFields.begin();
121 headerBuffer.append(it->first);
124 HTTPHeaderMap::const_iterator end = requestHeaderFields.end();
125 for (; it != end; ++it) {
126 headerBuffer.append(',');
127 headerBuffer.append(' ');
128 headerBuffer.append(it->first);
131 preflightRequest.setHTTPHeaderField("Access-Control-Request-Headers", headerBuffer.toString());
134 return preflightRequest;
137 bool passesAccessControlCheck(const ResourceResponse& response, StoredCredentials includeCredentials, SecurityOrigin* securityOrigin, String& errorDescription)
139 // A wildcard Access-Control-Allow-Origin can not be used if credentials are to be sent,
140 // even with Access-Control-Allow-Credentials set to true.
141 const String& accessControlOriginString = response.httpHeaderField("Access-Control-Allow-Origin");
142 if (accessControlOriginString == "*" && includeCredentials == DoNotAllowStoredCredentials)
145 if (securityOrigin->isUnique()) {
146 errorDescription = "Cannot make any requests from " + securityOrigin->toString() + ".";
150 // FIXME: Access-Control-Allow-Origin can contain a list of origins.
151 RefPtr<SecurityOrigin> accessControlOrigin = SecurityOrigin::createFromString(accessControlOriginString);
152 if (!accessControlOrigin->isSameSchemeHostPort(securityOrigin)) {
153 if (accessControlOriginString == "*")
154 errorDescription = "Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.";
156 errorDescription = "Origin " + securityOrigin->toString() + " is not allowed by Access-Control-Allow-Origin.";
160 if (includeCredentials == AllowStoredCredentials) {
161 const String& accessControlCredentialsString = response.httpHeaderField("Access-Control-Allow-Credentials");
162 if (accessControlCredentialsString != "true") {
163 errorDescription = "Credentials flag is true, but Access-Control-Allow-Credentials is not \"true\".";
171 } // namespace WebCore