| 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 |