OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. | 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 "cache-control", | 49 "cache-control", |
50 "content-language", | 50 "content-language", |
51 "content-type", | 51 "content-type", |
52 "expires", | 52 "expires", |
53 "last-modified", | 53 "last-modified", |
54 "pragma", | 54 "pragma", |
55 }))); | 55 }))); |
56 return allowedCrossOriginResponseHeaders.contains(name); | 56 return allowedCrossOriginResponseHeaders.contains(name); |
57 } | 57 } |
58 | 58 |
59 void updateRequestForAccessControl(ResourceRequest& request, SecurityOrigin* sec
urityOrigin, StoredCredentials allowCredentials) | 59 void updateRequestForAccessControl(ResourceRequest& request, const SecurityOrigi
n* securityOrigin, StoredCredentials allowCredentials) |
60 { | 60 { |
61 request.removeCredentials(); | 61 request.removeCredentials(); |
62 request.setAllowStoredCredentials(allowCredentials == AllowStoredCredentials
); | 62 request.setAllowStoredCredentials(allowCredentials == AllowStoredCredentials
); |
63 | 63 |
64 if (securityOrigin) | 64 if (securityOrigin) |
65 request.setHTTPOrigin(securityOrigin); | 65 request.setHTTPOrigin(securityOrigin); |
66 } | 66 } |
67 | 67 |
68 ResourceRequest createAccessControlPreflightRequest(const ResourceRequest& reque
st, SecurityOrigin* securityOrigin) | 68 ResourceRequest createAccessControlPreflightRequest(const ResourceRequest& reque
st, const SecurityOrigin* securityOrigin) |
69 { | 69 { |
70 ResourceRequest preflightRequest(request.url()); | 70 ResourceRequest preflightRequest(request.url()); |
71 updateRequestForAccessControl(preflightRequest, securityOrigin, DoNotAllowSt
oredCredentials); | 71 updateRequestForAccessControl(preflightRequest, securityOrigin, DoNotAllowSt
oredCredentials); |
72 preflightRequest.setHTTPMethod(HTTPNames::OPTIONS); | 72 preflightRequest.setHTTPMethod(HTTPNames::OPTIONS); |
73 preflightRequest.setHTTPHeaderField(HTTPNames::Access_Control_Request_Method
, AtomicString(request.httpMethod())); | 73 preflightRequest.setHTTPHeaderField(HTTPNames::Access_Control_Request_Method
, AtomicString(request.httpMethod())); |
74 preflightRequest.setPriority(request.priority()); | 74 preflightRequest.setPriority(request.priority()); |
75 preflightRequest.setRequestContext(request.requestContext()); | 75 preflightRequest.setRequestContext(request.requestContext()); |
76 preflightRequest.setSkipServiceWorker(WebURLRequest::SkipServiceWorker::All)
; | 76 preflightRequest.setSkipServiceWorker(WebURLRequest::SkipServiceWorker::All)
; |
77 | 77 |
78 if (request.isExternalRequest()) | 78 if (request.isExternalRequest()) |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
117 } | 117 } |
118 | 118 |
119 static bool isInterestingStatusCode(int statusCode) | 119 static bool isInterestingStatusCode(int statusCode) |
120 { | 120 { |
121 // Predicate that gates what status codes should be included in | 121 // Predicate that gates what status codes should be included in |
122 // console error messages for responses containing no access | 122 // console error messages for responses containing no access |
123 // control headers. | 123 // control headers. |
124 return statusCode >= 400; | 124 return statusCode >= 400; |
125 } | 125 } |
126 | 126 |
127 static String buildAccessControlFailureMessage(const String& detail, SecurityOri
gin* securityOrigin) | 127 static String buildAccessControlFailureMessage(const String& detail, const Secur
ityOrigin* securityOrigin) |
128 { | 128 { |
129 return detail + " Origin '" + securityOrigin->toString() + "' is therefore n
ot allowed access."; | 129 return detail + " Origin '" + securityOrigin->toString() + "' is therefore n
ot allowed access."; |
130 } | 130 } |
131 | 131 |
132 bool passesAccessControlCheck(const ResourceResponse& response, StoredCredential
s includeCredentials, SecurityOrigin* securityOrigin, String& errorDescription,
WebURLRequest::RequestContext context) | 132 bool passesAccessControlCheck(const ResourceResponse& response, StoredCredential
s includeCredentials, const SecurityOrigin* securityOrigin, String& errorDescrip
tion, WebURLRequest::RequestContext context) |
133 { | 133 { |
134 DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, allowOriginHeaderName, (new At
omicString("access-control-allow-origin"))); | 134 DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, allowOriginHeaderName, (new At
omicString("access-control-allow-origin"))); |
135 DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, allowCredentialsHeaderName, (n
ew AtomicString("access-control-allow-credentials"))); | 135 DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, allowCredentialsHeaderName, (n
ew AtomicString("access-control-allow-credentials"))); |
136 DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, allowSuboriginHeaderName, (new
AtomicString("access-control-allow-suborigin"))); | 136 DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, allowSuboriginHeaderName, (new
AtomicString("access-control-allow-suborigin"))); |
137 | 137 |
138 // TODO(esprehn): This code is using String::append extremely inefficiently | 138 // TODO(esprehn): This code is using String::append extremely inefficiently |
139 // causing tons of copies. It should pass around a StringBuilder instead. | 139 // causing tons of copies. It should pass around a StringBuilder instead. |
140 | 140 |
141 int statusCode = response.httpStatusCode(); | 141 int statusCode = response.httpStatusCode(); |
142 | 142 |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
265 if (response.wasFetchedViaServiceWorker()) { | 265 if (response.wasFetchedViaServiceWorker()) { |
266 for (const auto& header : response.corsExposedHeaderNames()) | 266 for (const auto& header : response.corsExposedHeaderNames()) |
267 headerSet.add(header); | 267 headerSet.add(header); |
268 return; | 268 return; |
269 } | 269 } |
270 parseAccessControlExposeHeadersAllowList(response.httpHeaderField(HTTPNames:
:Access_Control_Expose_Headers), headerSet); | 270 parseAccessControlExposeHeadersAllowList(response.httpHeaderField(HTTPNames:
:Access_Control_Expose_Headers), headerSet); |
271 } | 271 } |
272 | 272 |
273 bool CrossOriginAccessControl::isLegalRedirectLocation(const KURL& requestURL, S
tring& errorDescription) | 273 bool CrossOriginAccessControl::isLegalRedirectLocation(const KURL& requestURL, S
tring& errorDescription) |
274 { | 274 { |
275 // CORS restrictions imposed on Location: URL -- http://www.w3.org/TR/cors/#
redirect-steps (steps 2 + 3.) | 275 // Block non HTTP(S) schemes as specified in the step 4 in |
| 276 // https://fetch.spec.whatwg.org/#http-redirect-fetch. Chromium also allows |
| 277 // the data scheme. |
| 278 // |
| 279 // TODO(tyoshino): This check should be performed regardless of the CORS |
| 280 // flag and request's mode. |
276 if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(requestURL.protocol()
)) { | 281 if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(requestURL.protocol()
)) { |
277 errorDescription = "Redirect location '" + requestURL.getString() + "' h
as a disallowed scheme for cross-origin requests."; | 282 errorDescription = "Redirect location '" + requestURL.getString() + "' h
as a disallowed scheme for cross-origin requests."; |
278 return false; | 283 return false; |
279 } | 284 } |
280 | 285 |
| 286 // Block URLs including credentials as specified in the step 9 in |
| 287 // https://fetch.spec.whatwg.org/#http-redirect-fetch. |
| 288 // |
| 289 // TODO(tyoshino): This check should be performed also when request's |
| 290 // origin is not same origin with the redirect destination's origin. |
281 if (!(requestURL.user().isEmpty() && requestURL.pass().isEmpty())) { | 291 if (!(requestURL.user().isEmpty() && requestURL.pass().isEmpty())) { |
282 errorDescription = "Redirect location '" + requestURL.getString() + "' c
ontains userinfo, which is disallowed for cross-origin requests."; | 292 errorDescription = "Redirect location '" + requestURL.getString() + "' c
ontains userinfo, which is disallowed for cross-origin requests."; |
283 return false; | 293 return false; |
284 } | 294 } |
285 | 295 |
286 return true; | 296 return true; |
287 } | 297 } |
288 | 298 |
289 bool CrossOriginAccessControl::handleRedirect(SecurityOrigin* securityOrigin, Re
sourceRequest& newRequest, const ResourceResponse& redirectResponse, StoredCrede
ntials withCredentials, ResourceLoaderOptions& options, String& errorMessage) | 299 bool CrossOriginAccessControl::handleRedirect(const SecurityOrigin* securityOrig
in, ResourceRequest& newRequest, const ResourceResponse& redirectResponse, Store
dCredentials withCredentials, ResourceLoaderOptions& options, String& errorMessa
ge) |
290 { | 300 { |
291 // http://www.w3.org/TR/cors/#redirect-steps terminology: | 301 // http://www.w3.org/TR/cors/#redirect-steps terminology: |
292 const KURL& originalURL = redirectResponse.url(); | 302 const KURL& lastURL = redirectResponse.url(); |
293 const KURL& newURL = newRequest.url(); | 303 const KURL& newURL = newRequest.url(); |
294 | 304 |
295 bool redirectCrossOrigin = !securityOrigin->canRequest(newURL); | 305 const SecurityOrigin* securityOriginForHeader = securityOrigin; |
296 | 306 |
297 // Same-origin request URLs that redirect are allowed without checking acces
s. | 307 // TODO(tyoshino): This should be fixed to check not only the last one but |
298 if (!securityOrigin->canRequest(originalURL)) { | 308 // all redirect responses. |
| 309 if (!securityOrigin->canRequest(lastURL)) { |
299 // Follow http://www.w3.org/TR/cors/#redirect-steps | 310 // Follow http://www.w3.org/TR/cors/#redirect-steps |
300 String errorDescription; | 311 String errorDescription; |
301 | 312 |
302 // Steps 3 & 4 - check if scheme and other URL restrictions hold. | |
303 if (!isLegalRedirectLocation(newURL, errorDescription)) { | 313 if (!isLegalRedirectLocation(newURL, errorDescription)) { |
304 errorMessage = "Redirect from '" + originalURL.getString() + "' has
been blocked by CORS policy: " + errorDescription; | 314 errorMessage = "Redirect from '" + lastURL.getString() + "' has been
blocked by CORS policy: " + errorDescription; |
305 return false; | 315 return false; |
306 } | 316 } |
307 | 317 |
308 // Step 5: perform resource sharing access check. | 318 // Step 5: perform resource sharing access check. |
309 if (!passesAccessControlCheck(redirectResponse, withCredentials, securit
yOrigin, errorDescription, newRequest.requestContext())) { | 319 if (!passesAccessControlCheck(redirectResponse, withCredentials, securit
yOrigin, errorDescription, newRequest.requestContext())) { |
310 errorMessage = "Redirect from '" + originalURL.getString() + "' has
been blocked by CORS policy: " + errorDescription; | 320 errorMessage = "Redirect from '" + lastURL.getString() + "' has been
blocked by CORS policy: " + errorDescription; |
311 return false; | 321 return false; |
312 } | 322 } |
313 | 323 |
314 RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::create(originalU
RL); | 324 RefPtr<SecurityOrigin> lastOrigin = SecurityOrigin::create(lastURL); |
315 // Step 6: if the request URL origin is not same origin as the original
URL's, | 325 // Set request's origin to a globally unique identifier as specified in |
316 // set the source origin to a globally unique identifier. | 326 // the step 10 in https://fetch.spec.whatwg.org/#http-redirect-fetch. |
317 if (!originalOrigin->canRequest(newURL)) { | 327 if (!lastOrigin->canRequest(newURL)) { |
318 options.securityOrigin = SecurityOrigin::createUnique(); | 328 options.securityOrigin = SecurityOrigin::createUnique(); |
319 securityOrigin = options.securityOrigin.get(); | 329 securityOriginForHeader = options.securityOrigin.get(); |
320 } | 330 } |
321 } | 331 } |
322 if (redirectCrossOrigin) { | 332 |
323 // If now to a different origin, update/set Origin:. | 333 if (!securityOrigin->canRequest(newURL)) { |
324 newRequest.clearHTTPOrigin(); | 334 newRequest.clearHTTPOrigin(); |
325 newRequest.setHTTPOrigin(securityOrigin); | 335 newRequest.setHTTPOrigin(securityOriginForHeader); |
326 // If the user didn't request credentials in the first place, update our | 336 |
327 // state so we neither request them nor expect they must be allowed. | 337 // Unset credentials flag if request's credentials mode is |
| 338 // "same-origin" as request's response tainting becomes "cors". |
| 339 // |
| 340 // This is equivalent to the step 2 in |
| 341 // https://fetch.spec.whatwg.org/#http-network-or-cache-fetch |
328 if (options.credentialsRequested == ClientDidNotRequestCredentials) | 342 if (options.credentialsRequested == ClientDidNotRequestCredentials) |
329 options.allowCredentials = DoNotAllowStoredCredentials; | 343 options.allowCredentials = DoNotAllowStoredCredentials; |
330 } | 344 } |
331 return true; | 345 return true; |
332 } | 346 } |
333 | 347 |
334 } // namespace blink | 348 } // namespace blink |
OLD | NEW |