void DocumentThreadableLoader::redirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse) { ASSERT(m_client); ASSERT_UNUSED(resource, resource == m_resource); Ref<DocumentThreadableLoader> protectedThis(*this); if (!isAllowedByContentSecurityPolicy(request.url(), !redirectResponse.isNull())) { m_client->didFailRedirectCheck(); request = ResourceRequest(); return; } // Allow same origin requests to continue after allowing clients to audit the redirect. if (isAllowedRedirect(request.url())) return; // When using access control, only simple cross origin requests are allowed to redirect. The new request URL must have a supported // scheme and not contain the userinfo production. In addition, the redirect response must pass the access control check if the // original request was not same-origin. if (m_options.crossOriginRequestPolicy == UseAccessControl) { bool allowRedirect = false; if (m_simpleRequest) { String accessControlErrorDescription; allowRedirect = isValidCrossOriginRedirectionURL(request.url()) && (m_sameOriginRequest || passesAccessControlCheck(redirectResponse, m_options.allowCredentials(), securityOrigin(), accessControlErrorDescription)); } if (allowRedirect) { if (m_resource) clearResource(); RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::createFromString(redirectResponse.url()); RefPtr<SecurityOrigin> requestOrigin = SecurityOrigin::createFromString(request.url()); // If the original request wasn't same-origin, then if the request URL origin is not same origin with the original URL origin, // set the source origin to a globally unique identifier. (If the original request was same-origin, the origin of the new request // should be the original URL origin.) if (!m_sameOriginRequest && !originalOrigin->isSameSchemeHostPort(requestOrigin.get())) m_options.securityOrigin = SecurityOrigin::createUnique(); // Force any subsequent request to use these checks. m_sameOriginRequest = false; // Since the request is no longer same-origin, if the user didn't request credentials in // the first place, update our state so we neither request them nor expect they must be allowed. if (m_options.credentialRequest() == ClientDidNotRequestCredentials) m_options.setAllowCredentials(DoNotAllowStoredCredentials); cleanRedirectedRequestForAccessControl(request); makeCrossOriginAccessRequest(request); return; } } m_client->didFailRedirectCheck(); request = ResourceRequest(); }
bool SubresourceLoader::checkRedirectionCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse& redirectResponse, ResourceRequest& newRequest, String& errorMessage) { bool crossOriginFlag = m_resource->isCrossOrigin(); bool isNextRequestCrossOrigin = m_origin && !m_origin->canRequest(newRequest.url()); if (isNextRequestCrossOrigin) m_resource->setCrossOrigin(); ASSERT(options().mode != FetchOptions::Mode::SameOrigin || !m_resource->isCrossOrigin()); if (options().mode != FetchOptions::Mode::Cors) return true; // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 8 & 9. if (m_resource->isCrossOrigin() && !isValidCrossOriginRedirectionURL(newRequest.url())) { errorMessage = ASCIILiteral("URL is either a non-HTTP URL or contains credentials."); return false; } ASSERT(m_origin); if (crossOriginFlag && !passesAccessControlCheck(redirectResponse, options().allowCredentials, *m_origin, errorMessage)) return false; bool redirectingToNewOrigin = false; if (m_resource->isCrossOrigin()) { if (!crossOriginFlag && isNextRequestCrossOrigin) redirectingToNewOrigin = true; else redirectingToNewOrigin = !SecurityOrigin::create(previousRequest.url())->canRequest(newRequest.url()); } // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 10. if (crossOriginFlag && redirectingToNewOrigin) m_origin = SecurityOrigin::createUnique(); if (redirectingToNewOrigin) { cleanRedirectedRequestForAccessControl(newRequest); updateRequestForAccessControl(newRequest, *m_origin, options().allowCredentials); } return true; }
bool SubresourceLoader::checkCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse& redirectResponse, ResourceRequest& newRequest) { if (m_origin->canRequest(newRequest.url())) return true; String errorDescription; bool responsePassesCORS = m_origin->canRequest(previousRequest.url()) || passesAccessControlCheck(redirectResponse, options().allowCredentials(), m_origin.get(), errorDescription); if (!responsePassesCORS || !isValidCrossOriginRedirectionURL(newRequest.url())) { if (m_frame && m_frame->document()) { String errorMessage = "Cross-origin redirection denied by Cross-Origin Resource Sharing policy: " + (!responsePassesCORS ? errorDescription : "Redirected to either a non-HTTP URL or a URL that contains credentials."); m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, errorMessage); } return false; } // If the request URL origin is not the same as the original origin, the request origin should be set to a globally unique identifier. m_origin = SecurityOrigin::createUnique(); cleanRedirectedRequestForAccessControl(newRequest); updateRequestForAccessControl(newRequest, m_origin.get(), options().allowCredentials()); return true; }