void DocumentThreadableLoader::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
{
    ASSERT(m_client);
    ASSERT_UNUSED(loader, loader == m_loader);

    if (m_actualRequest) {
        if (!passesAccessControlCheck(response, m_options.allowCredentials, m_document->securityOrigin())) {
            preflightFailure();
            return;
        }

        OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult(new CrossOriginPreflightResultCacheItem(m_options.allowCredentials));
        if (!preflightResult->parse(response)
                || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod())
                || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields())) {
            preflightFailure();
            return;
        }

        CrossOriginPreflightResultCache::shared().appendEntry(m_document->securityOrigin()->toString(), m_actualRequest->url(), preflightResult.release());
    } else {
        if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) {
            if (!passesAccessControlCheck(response, m_options.allowCredentials, m_document->securityOrigin())) {
                m_client->didFail(ResourceError());
                return;
            }
        }

        m_client->didReceiveResponse(response);
    }
}
void DocumentThreadableLoader::handlePreflightResponse(const ResourceResponse& response)
{
    String accessControlErrorDescription;

    if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription, m_requestContext)) {
        handlePreflightFailure(response.url().string(), "Response to preflight request doesn't pass access control check: " + accessControlErrorDescription);
        // |this| may be dead here in async mode.
        return;
    }

    if (!passesPreflightStatusCheck(response, accessControlErrorDescription)) {
        handlePreflightFailure(response.url().string(), accessControlErrorDescription);
        // |this| may be dead here in async mode.
        return;
    }

    OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult = adoptPtr(new CrossOriginPreflightResultCacheItem(effectiveAllowCredentials()));
    if (!preflightResult->parse(response, accessControlErrorDescription)
        || !preflightResult->allowsCrossOriginMethod(m_actualRequest.httpMethod(), accessControlErrorDescription)
        || !preflightResult->allowsCrossOriginHeaders(m_actualRequest.httpHeaderFields(), accessControlErrorDescription)) {
        handlePreflightFailure(response.url().string(), accessControlErrorDescription);
        // |this| may be dead here in async mode.
        return;
    }

    CrossOriginPreflightResultCache::shared().appendEntry(securityOrigin()->toString(), m_actualRequest.url(), preflightResult.release());
}
示例#3
0
bool SubresourceLoader::checkRedirectionCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse& redirectResponse, ResourceRequest& newRequest)
{
    ASSERT(options().mode != FetchOptions::Mode::SameOrigin);

    bool shouldCheckCrossOrigin = options().mode == FetchOptions::Mode::Cors && m_resource->isCrossOrigin();

    if (!(m_origin && m_origin->canRequest(newRequest.url())))
        m_resource->setCrossOrigin();

    if (!shouldCheckCrossOrigin)
        return true;

    ASSERT(m_origin);
    String errorDescription;
    bool responsePassesCORS = m_origin->canRequest(previousRequest.url())
        || passesAccessControlCheck(redirectResponse, options().allowCredentials(), *m_origin, 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, options().allowCredentials());

    return true;
}
void DocumentThreadableLoader::handleResponse(unsigned long identifier, const ResourceResponse& response, PassOwnPtr<WebDataConsumerHandle> handle)
{
    ASSERT(m_client);

    if (!m_actualRequest.isNull()) {
        reportResponseReceived(identifier, response);
        handlePreflightResponse(response);
        // |this| may be dead here in async mode.
        return;
    }

    if (response.wasFetchedViaServiceWorker()) {
        // It's still possible to reach here with null m_fallbackRequestForServiceWorker
        // if the request was for main resource loading (i.e. for SharedWorker), for which
        // we create DocumentLoader before the controller ServiceWorker is set.
        ASSERT(!m_fallbackRequestForServiceWorker.isNull() || m_requestContext == WebURLRequest::RequestContextSharedWorker);
        if (response.wasFallbackRequiredByServiceWorker()) {
            // At this point we must have m_fallbackRequestForServiceWorker.
            // (For SharedWorker the request won't be CORS or CORS-with-preflight,
            // therefore fallback-to-network is handled in the browser process
            // when the ServiceWorker does not call respondWith().)
            ASSERT(!m_fallbackRequestForServiceWorker.isNull());
            reportResponseReceived(identifier, response);
            loadFallbackRequestForServiceWorker();
            // |this| may be dead here in async mode.
            return;
        }
        m_fallbackRequestForServiceWorker = ResourceRequest();
        m_client->didReceiveResponse(identifier, response, handle);
        return;
    }

    // Even if the request met the conditions to get handled by a Service Worker
    // in the constructor of this class (and therefore
    // |m_fallbackRequestForServiceWorker| is set), the Service Worker may skip
    // processing the request. Only if the request is same origin, the skipped
    // response may come here (wasFetchedViaServiceWorker() returns false) since
    // such a request doesn't have to go through the CORS algorithm by calling
    // loadFallbackRequestForServiceWorker().
    // FIXME: We should use |m_sameOriginRequest| when we will support
    // Suborigins (crbug.com/336894) for Service Worker.
    ASSERT(m_fallbackRequestForServiceWorker.isNull() || securityOrigin()->canRequest(m_fallbackRequestForServiceWorker.url()));
    m_fallbackRequestForServiceWorker = ResourceRequest();

    if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) {
        String accessControlErrorDescription;
        if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription, m_requestContext)) {
            reportResponseReceived(identifier, response);

            ThreadableLoaderClient* client = m_client;
            clear();
            client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, response.url().string(), accessControlErrorDescription));
            // |this| may be dead here.
            return;
        }
    }

    m_client->didReceiveResponse(identifier, response, handle);
}
示例#5
0
bool SubresourceLoader::checkResponseCrossOriginAccessControl(const ResourceResponse& response, String& errorDescription)
{
    if (!m_resource->isCrossOrigin() || options().mode != FetchOptions::Mode::Cors)
        return true;

    ASSERT(m_origin);
    return passesAccessControlCheck(response, options().allowCredentials, *m_origin, errorDescription);
}
示例#6
0
bool CachedImage::isOriginClean(SecurityOrigin* securityOrigin)
{
    if (!image()->hasSingleSecurityOrigin())
        return false;
    if (passesAccessControlCheck(*securityOrigin))
        return true;
    return !securityOrigin->taintsCanvas(responseForSameOriginPolicyChecks().url());
}
示例#7
0
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();
}
示例#8
0
bool ImageResource::isAccessAllowed(SecurityOrigin* securityOrigin)
{
    if (response().wasFetchedViaServiceWorker())
        return response().serviceWorkerResponseType() != WebServiceWorkerResponseTypeOpaque;
    if (!image()->currentFrameHasSingleSecurityOrigin())
        return false;
    if (passesAccessControlCheck(securityOrigin))
        return true;
    return !securityOrigin->taintsCanvas(response().url());
}
void DocumentThreadableLoader::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
{
    ASSERT(m_client);

    String accessControlErrorDescription;
    if (m_actualRequest) {
        // Notifying the inspector here is necessary because a call to preflightFailure() might synchronously
        // cause the underlying ResourceLoader to be cancelled before it tells the inspector about the response.
        // In that case, if we don't tell the inspector about the response now, the resource type in the inspector
        // will default to "other" instead of something more descriptive.
        DocumentLoader* loader = m_document->frame()->loader().documentLoader();
        InspectorInstrumentation::didReceiveResourceResponse(m_document->frame(), identifier, loader, response, resource() ? resource()->loader() : 0);

        if (!passesAccessControlCheck(response, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription)) {
            preflightFailure(response.url().string(), accessControlErrorDescription);
            return;
        }

        if (!passesPreflightStatusCheck(response, accessControlErrorDescription)) {
            preflightFailure(response.url().string(), accessControlErrorDescription);
            return;
        }

        OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult = adoptPtr(new CrossOriginPreflightResultCacheItem(m_options.allowCredentials));
        if (!preflightResult->parse(response, accessControlErrorDescription)
            || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription)
            || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) {
            preflightFailure(response.url().string(), accessControlErrorDescription);
            return;
        }

        CrossOriginPreflightResultCache::shared().appendEntry(securityOrigin()->toString(), m_actualRequest->url(), preflightResult.release());
    } else {
        if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) {
            if (!passesAccessControlCheck(response, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription)) {
                m_client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, response.url().string(), accessControlErrorDescription));
                return;
            }
        }

        m_client->didReceiveResponse(identifier, response);
    }
}
示例#10
0
void DocumentThreadableLoader::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
{
    ASSERT(m_client);

#if ENABLE(INSPECTOR)
    if (m_preflightRequestIdentifier) {
        DocumentLoader* loader = m_document->frame()->loader()->documentLoader();
        InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(m_document->frame(), m_preflightRequestIdentifier, response);
        InspectorInstrumentation::didReceiveResourceResponse(cookie, m_preflightRequestIdentifier, loader, response, 0);
    }
#endif

    String accessControlErrorDescription;
    if (m_actualRequest) {
        if (!passesAccessControlCheck(response, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription)) {
            preflightFailure(response.url(), accessControlErrorDescription);
            return;
        }

        OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult = adoptPtr(new CrossOriginPreflightResultCacheItem(m_options.allowCredentials));
        if (!preflightResult->parse(response, accessControlErrorDescription)
            || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription)
            || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) {
            preflightFailure(response.url(), accessControlErrorDescription);
            return;
        }

        CrossOriginPreflightResultCache::shared().appendEntry(securityOrigin()->toString(), m_actualRequest->url(), preflightResult.release());
    } else {
        if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) {
            if (!passesAccessControlCheck(response, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription)) {
                m_client->didFail(ResourceError(errorDomainWebKitInternal, 0, response.url().string(), accessControlErrorDescription));
                return;
            }
        }

        m_client->didReceiveResponse(identifier, response);
    }
}
示例#11
0
void DocumentThreadableLoader::redirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
{
    ASSERT(m_client);
    ASSERT_UNUSED(resource, resource == m_resource);

    RefPtr<DocumentThreadableLoader> protect(this);
    // Allow same origin requests to continue after allowing clients to audit the redirect.
    if (isAllowedRedirect(request.url())) {
        if (m_client->isDocumentThreadableLoaderClient())
            static_cast<DocumentThreadableLoaderClient*>(m_client)->willSendRequest(request, redirectResponse);
        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 (m_options.crossOriginRequestPolicy == UseAccessControl) {
        bool allowRedirect = false;
        if (m_simpleRequest) {
            String accessControlErrorDescription;
            allowRedirect = SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(request.url().protocol())
                            && request.url().user().isEmpty()
                            && request.url().pass().isEmpty()
                            && 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 request URL origin is not same origin with the original URL origin, set source origin to a globally unique identifier.
            if (!originalOrigin->isSameSchemeHostPort(requestOrigin.get()))
                m_options.securityOrigin = SecurityOrigin::createUnique();
            // Force any subsequent requests to use these checks.
            m_sameOriginRequest = false;

            // Remove any headers that may have been added by the network layer that cause access control to fail.
            request.clearHTTPContentType();
            request.clearHTTPReferrer();
            request.clearHTTPOrigin();
            request.clearHTTPUserAgent();
            request.clearHTTPAccept();
            makeCrossOriginAccessRequest(request);
            return;
        }
    }

    m_client->didFailRedirectCheck();
    request = ResourceRequest();
}
示例#12
0
void DocumentThreadableLoader::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
{
    ASSERT(m_client);

    String accessControlErrorDescription;
    if (m_actualRequest) {
        DocumentLoader* loader = m_document.frame()->loader().documentLoader();
        InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(m_document.frame());
        InspectorInstrumentation::didReceiveResourceResponse(cookie, identifier, loader, response, 0);

        if (!passesAccessControlCheck(response, m_options.allowCredentials(), securityOrigin(), accessControlErrorDescription)) {
            preflightFailure(identifier, response.url(), accessControlErrorDescription);
            return;
        }

        StoredCredentials allowCredentials = m_options.allowCredentials();
        auto preflightResult = std::make_unique<CrossOriginPreflightResultCacheItem>(allowCredentials);
        if (!preflightResult->parse(response, accessControlErrorDescription)
            || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription)
            || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) {
            preflightFailure(identifier, response.url(), accessControlErrorDescription);
            return;
        }

        CrossOriginPreflightResultCache::singleton().appendEntry(securityOrigin()->toString(), m_actualRequest->url(), WTFMove(preflightResult));
    } else {
        if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) {
            if (!passesAccessControlCheck(response, m_options.allowCredentials(), securityOrigin(), accessControlErrorDescription)) {
                m_client->didFailAccessControlCheck(ResourceError(errorDomainWebKitInternal, 0, response.url(), accessControlErrorDescription));
                return;
            }
        }

        m_client->didReceiveResponse(identifier, response);
    }
}
bool CrossOriginAccessControl::handleRedirect(Resource* resource, SecurityOrigin* securityOrigin, ResourceRequest& request, const ResourceResponse& redirectResponse, ResourceLoaderOptions& options, String& errorMessage)
{
    // http://www.w3.org/TR/cors/#redirect-steps terminology:
    const KURL& originalURL = redirectResponse.url();
    const KURL& requestURL = request.url();

    bool redirectCrossOrigin = !securityOrigin->canRequest(requestURL);

    // Same-origin request URLs that redirect are allowed without checking access.
    if (!securityOrigin->canRequest(originalURL)) {
        // Follow http://www.w3.org/TR/cors/#redirect-steps
        String errorDescription;

        // Steps 3 & 4 - check if scheme and other URL restrictions hold.
        bool allowRedirect = isLegalRedirectLocation(requestURL, errorDescription);
        if (allowRedirect) {
            // Step 5: perform resource sharing access check.
            StoredCredentials withCredentials = resource->resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials;
            allowRedirect = passesAccessControlCheck(redirectResponse, withCredentials, securityOrigin, errorDescription);
            if (allowRedirect) {
                RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::create(originalURL);
                // Step 6: if the request URL origin is not same origin as the original URL's,
                // set the source origin to a globally unique identifier.
                if (!originalOrigin->canRequest(requestURL)) {
                    options.securityOrigin = SecurityOrigin::createUnique();
                    securityOrigin = options.securityOrigin.get();
                }
            }
        }
        if (!allowRedirect) {
            const String& originalOrigin = SecurityOrigin::create(originalURL)->toString();
            errorMessage = "Redirect at origin '" + originalOrigin + "' has been blocked from loading by Cross-Origin Resource Sharing policy: " + errorDescription;
            return false;
        }
    }
    if (redirectCrossOrigin) {
        // If now to a different origin, update/set Origin:.
        request.clearHTTPOrigin();
        request.setHTTPOrigin(securityOrigin->toAtomicString());
        // 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 (options.credentialsRequested == ClientDidNotRequestCredentials)
            options.allowCredentials = DoNotAllowStoredCredentials;
    }
    return true;
}
示例#14
0
void DocumentThreadableLoader::handlePreflightResponse(
    const ResourceResponse& response) {
  String accessControlErrorDescription;

  if (!passesAccessControlCheck(
          response, effectiveAllowCredentials(), getSecurityOrigin(),
          accessControlErrorDescription, m_requestContext)) {
    handlePreflightFailure(
        response.url().getString(),
        "Response to preflight request doesn't pass access control check: " +
            accessControlErrorDescription);
    return;
  }

  if (!passesPreflightStatusCheck(response, accessControlErrorDescription)) {
    handlePreflightFailure(response.url().getString(),
                           accessControlErrorDescription);
    return;
  }

  if (m_actualRequest.isExternalRequest() &&
      !passesExternalPreflightCheck(response, accessControlErrorDescription)) {
    handlePreflightFailure(response.url().getString(),
                           accessControlErrorDescription);
    return;
  }

  std::unique_ptr<CrossOriginPreflightResultCacheItem> preflightResult =
      WTF::wrapUnique(
          new CrossOriginPreflightResultCacheItem(effectiveAllowCredentials()));
  if (!preflightResult->parse(response, accessControlErrorDescription) ||
      !preflightResult->allowsCrossOriginMethod(
          m_actualRequest.httpMethod(), accessControlErrorDescription) ||
      !preflightResult->allowsCrossOriginHeaders(
          m_actualRequest.httpHeaderFields(), accessControlErrorDescription)) {
    handlePreflightFailure(response.url().getString(),
                           accessControlErrorDescription);
    return;
  }

  CrossOriginPreflightResultCache::shared().appendEntry(
      getSecurityOrigin()->toString(), m_actualRequest.url(),
      std::move(preflightResult));
}
示例#15
0
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;
}
示例#16
0
// In this method, we can clear |request| to tell content::WebURLLoaderImpl of
// Chromium not to follow the redirect. This works only when this method is
// called by RawResource::willSendRequest(). If called by
// RawResource::didAddClient(), clearing |request| won't be propagated to
// content::WebURLLoaderImpl. So, this loader must also get detached from the
// resource by calling clearResource().
bool DocumentThreadableLoader::redirectReceived(
    Resource* resource,
    const ResourceRequest& request,
    const ResourceResponse& redirectResponse) {
  DCHECK(m_client);
  DCHECK_EQ(resource, this->resource());
  DCHECK(m_async);

  m_checker.redirectReceived();

  if (!m_actualRequest.isNull()) {
    reportResponseReceived(resource->identifier(), redirectResponse);

    handlePreflightFailure(redirectResponse.url().getString(),
                           "Response for preflight is invalid (redirect)");

    return false;
  }

  if (m_redirectMode == WebURLRequest::FetchRedirectModeManual) {
    // We use |m_redirectMode| to check the original redirect mode. |request| is
    // a new request for redirect. So we don't set the redirect mode of it in
    // WebURLLoaderImpl::Context::OnReceivedRedirect().
    DCHECK(request.useStreamOnResponse());
    // There is no need to read the body of redirect response because there is
    // no way to read the body of opaque-redirect filtered response's internal
    // response.
    // TODO(horo): If we support any API which expose the internal body, we will
    // have to read the body. And also HTTPCache changes will be needed because
    // it doesn't store the body of redirect responses.
    responseReceived(resource, redirectResponse,
                     WTF::makeUnique<EmptyDataHandle>());

    if (m_client) {
      DCHECK(m_actualRequest.isNull());
      notifyFinished(resource);
    }

    return false;
  }

  if (m_redirectMode == WebURLRequest::FetchRedirectModeError) {
    ThreadableLoaderClient* client = m_client;
    clear();
    client->didFailRedirectCheck();

    return false;
  }

  // Allow same origin requests to continue after allowing clients to audit the
  // redirect.
  if (isAllowedRedirect(request.url())) {
    m_client->didReceiveRedirectTo(request.url());
    if (m_client->isDocumentThreadableLoaderClient()) {
      return static_cast<DocumentThreadableLoaderClient*>(m_client)
          ->willFollowRedirect(request, redirectResponse);
    }
    return true;
  }

  if (m_corsRedirectLimit <= 0) {
    ThreadableLoaderClient* client = m_client;
    clear();
    client->didFailRedirectCheck();
    return false;
  }

  --m_corsRedirectLimit;

  InspectorInstrumentation::didReceiveCORSRedirectResponse(
      document().frame(), resource->identifier(),
      document().frame()->loader().documentLoader(), redirectResponse,
      resource);

  bool allowRedirect = false;
  String accessControlErrorDescription;

  if (!CrossOriginAccessControl::isLegalRedirectLocation(
          request.url(), accessControlErrorDescription)) {
    accessControlErrorDescription =
        "Redirect from '" + redirectResponse.url().getString() +
        "' has been blocked by CORS policy: " + accessControlErrorDescription;
  } else if (!m_sameOriginRequest &&
             !passesAccessControlCheck(
                 redirectResponse, effectiveAllowCredentials(),
                 getSecurityOrigin(), accessControlErrorDescription,
                 m_requestContext)) {
    // The redirect response must pass the access control check if the original
    // request was not same-origin.
    accessControlErrorDescription =
        "Redirect from '" + redirectResponse.url().getString() + "' to '" +
        request.url().getString() + "' has been blocked by CORS policy: " +
        accessControlErrorDescription;
  } else {
    allowRedirect = true;
  }

  if (!allowRedirect) {
    ThreadableLoaderClient* client = m_client;
    clear();
    client->didFailAccessControlCheck(ResourceError(
        errorDomainBlinkInternal, 0, redirectResponse.url().getString(),
        accessControlErrorDescription));
    return false;
  }

  m_client->didReceiveRedirectTo(request.url());

  // FIXME: consider combining this with CORS redirect handling performed by
  // CrossOriginAccessControl::handleRedirect().
  clearResource();

  // 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) {
    RefPtr<SecurityOrigin> originalOrigin =
        SecurityOrigin::create(redirectResponse.url());
    RefPtr<SecurityOrigin> requestOrigin =
        SecurityOrigin::create(request.url());
    if (!originalOrigin->isSameSchemeHostPort(requestOrigin.get()))
      m_securityOrigin = SecurityOrigin::createUnique();
  }
  // Force any subsequent requests 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_resourceLoaderOptions.credentialsRequested ==
      ClientDidNotRequestCredentials)
    m_forceDoNotAllowStoredCredentials = true;

  // Save the referrer to use when following the redirect.
  m_overrideReferrer = true;
  m_referrerAfterRedirect =
      Referrer(request.httpReferrer(), request.getReferrerPolicy());

  ResourceRequest crossOriginRequest(request);

  // Remove any headers that may have been added by the network layer that cause
  // access control to fail.
  crossOriginRequest.clearHTTPReferrer();
  crossOriginRequest.clearHTTPOrigin();
  crossOriginRequest.clearHTTPUserAgent();
  // Add any request headers which we previously saved from the
  // original request.
  for (const auto& header : m_requestHeaders)
    crossOriginRequest.setHTTPHeaderField(header.key, header.value);
  makeCrossOriginAccessRequest(crossOriginRequest);

  return false;
}
bool Resource::isEligibleForIntegrityCheck(SecurityOrigin* securityOrigin) const
{
    String ignoredErrorDescription;
    return securityOrigin->canRequest(resourceRequest().url()) || passesAccessControlCheck(securityOrigin, ignoredErrorDescription);
}
bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin) const
{
    String ignoredErrorDescription;
    return passesAccessControlCheck(securityOrigin, ignoredErrorDescription);
}
// In this method, we can clear |request| to tell content::WebURLLoaderImpl of
// Chromium not to follow the redirect. This works only when this method is
// called by RawResource::willSendRequest(). If called by
// RawResource::didAddClient(), clearing |request| won't be propagated
// to content::WebURLLoaderImpl. So, this loader must also get detached from
// the resource by calling clearResource().
void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
{
    ASSERT(m_client);
    ASSERT_UNUSED(resource, resource == this->resource());
    ASSERT(m_async);

    if (!m_actualRequest.isNull()) {
        reportResponseReceived(resource->identifier(), redirectResponse);

        handlePreflightFailure(redirectResponse.url().string(), "Response for preflight is invalid (redirect)");
        // |this| may be dead here.

        request = ResourceRequest();

        return;
    }

    if (m_redirectMode == WebURLRequest::FetchRedirectModeManual) {
        // Keep |this| alive even if the client release a reference in
        // responseReceived().
        RefPtr<DocumentThreadableLoader> protect(this);

        // We use |m_redirectMode| to check the original redirect mode.
        // |request| is a new request for redirect. So we don't set the redirect
        // mode of it in WebURLLoaderImpl::Context::OnReceivedRedirect().
        ASSERT(request.useStreamOnResponse());
        // There is no need to read the body of redirect response because there
        // is no way to read the body of opaque-redirect filtered response's
        // internal response.
        // TODO(horo): If we support any API which expose the internal body, we
        // will have to read the body. And also HTTPCache changes will be needed
        // because it doesn't store the body of redirect responses.
        responseReceived(resource, redirectResponse, adoptPtr(new EmptyDataHandle()));

        if (m_client) {
            ASSERT(m_actualRequest.isNull());
            notifyFinished(resource);
        }

        request = ResourceRequest();

        return;
    }

    if (m_redirectMode == WebURLRequest::FetchRedirectModeError || !isAllowedByContentSecurityPolicy(request.url(), ContentSecurityPolicy::DidRedirect)) {
        ThreadableLoaderClient* client = m_client;
        clear();
        client->didFailRedirectCheck();
        // |this| may be dead here.

        request = ResourceRequest();

        return;
    }

    // Allow same origin requests to continue after allowing clients to audit the redirect.
    if (isAllowedRedirect(request.url())) {
        if (m_client->isDocumentThreadableLoaderClient())
            static_cast<DocumentThreadableLoaderClient*>(m_client)->willFollowRedirect(request, redirectResponse);
        return;
    }

    if (m_corsRedirectLimit <= 0) {
        ThreadableLoaderClient* client = m_client;
        clear();
        client->didFailRedirectCheck();
        // |this| may be dead here.
    } else if (m_options.crossOriginRequestPolicy == UseAccessControl) {
        --m_corsRedirectLimit;

        InspectorInstrumentation::didReceiveCORSRedirectResponse(document().frame(), resource->identifier(), document().frame()->loader().documentLoader(), redirectResponse, 0);

        bool allowRedirect = false;
        String accessControlErrorDescription;

        // Non-simple cross origin requests (both preflight and actual one) are
        // not allowed to follow redirect.
        if (m_crossOriginNonSimpleRequest) {
            accessControlErrorDescription = "The request was redirected to '"+ request.url().string() + "', which is disallowed for cross-origin requests that require preflight.";
        } else {
            // The redirect response must pass the access control check if the
            // original request was not same-origin.
            allowRedirect = CrossOriginAccessControl::isLegalRedirectLocation(request.url(), accessControlErrorDescription)
                && (m_sameOriginRequest || passesAccessControlCheck(redirectResponse, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription, m_requestContext));
        }

        if (allowRedirect) {
            // FIXME: consider combining this with CORS redirect handling performed by
            // CrossOriginAccessControl::handleRedirect().
            clearResource();

            RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::create(redirectResponse.url());
            RefPtr<SecurityOrigin> requestOrigin = SecurityOrigin::create(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_securityOrigin = SecurityOrigin::createUnique();
            // Force any subsequent requests 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_resourceLoaderOptions.credentialsRequested == ClientDidNotRequestCredentials)
                m_forceDoNotAllowStoredCredentials = true;

            // Remove any headers that may have been added by the network layer that cause access control to fail.
            request.clearHTTPReferrer();
            request.clearHTTPOrigin();
            request.clearHTTPUserAgent();
            // Add any CORS simple request headers which we previously saved from the original request.
            for (const auto& header : m_simpleRequestHeaders)
                request.setHTTPHeaderField(header.key, header.value);
            makeCrossOriginAccessRequest(request);
            // |this| may be dead here.
            return;
        }

        ThreadableLoaderClient* client = m_client;
        clear();
        client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, redirectResponse.url().string(), accessControlErrorDescription));
        // |this| may be dead here.
    } else {
        ThreadableLoaderClient* client = m_client;
        clear();
        client->didFailRedirectCheck();
        // |this| may be dead here.
    }

    request = ResourceRequest();
}
示例#20
0
void DocumentThreadableLoader::handleResponse(
    unsigned long identifier,
    const ResourceResponse& response,
    std::unique_ptr<WebDataConsumerHandle> handle) {
  DCHECK(m_client);

  if (!m_actualRequest.isNull()) {
    reportResponseReceived(identifier, response);
    handlePreflightResponse(response);
    return;
  }

  if (response.wasFetchedViaServiceWorker()) {
    if (response.wasFetchedViaForeignFetch())
      UseCounter::count(m_document, UseCounter::ForeignFetchInterception);
    if (response.wasFallbackRequiredByServiceWorker()) {
      // At this point we must have m_fallbackRequestForServiceWorker. (For
      // SharedWorker the request won't be CORS or CORS-with-preflight,
      // therefore fallback-to-network is handled in the browser process when
      // the ServiceWorker does not call respondWith().)
      DCHECK(!m_fallbackRequestForServiceWorker.isNull());
      reportResponseReceived(identifier, response);
      loadFallbackRequestForServiceWorker();
      return;
    }
    m_fallbackRequestForServiceWorker = ResourceRequest();
    m_client->didReceiveResponse(identifier, response, std::move(handle));
    return;
  }

  // Even if the request met the conditions to get handled by a Service Worker
  // in the constructor of this class (and therefore
  // |m_fallbackRequestForServiceWorker| is set), the Service Worker may skip
  // processing the request. Only if the request is same origin, the skipped
  // response may come here (wasFetchedViaServiceWorker() returns false) since
  // such a request doesn't have to go through the CORS algorithm by calling
  // loadFallbackRequestForServiceWorker().
  // FIXME: We should use |m_sameOriginRequest| when we will support Suborigins
  // (crbug.com/336894) for Service Worker.
  DCHECK(
      m_fallbackRequestForServiceWorker.isNull() ||
      getSecurityOrigin()->canRequest(m_fallbackRequestForServiceWorker.url()));
  m_fallbackRequestForServiceWorker = ResourceRequest();

  if (!m_sameOriginRequest &&
      m_options.crossOriginRequestPolicy == UseAccessControl) {
    String accessControlErrorDescription;
    if (!passesAccessControlCheck(
            response, effectiveAllowCredentials(), getSecurityOrigin(),
            accessControlErrorDescription, m_requestContext)) {
      reportResponseReceived(identifier, response);

      ThreadableLoaderClient* client = m_client;
      clear();
      client->didFailAccessControlCheck(
          ResourceError(errorDomainBlinkInternal, 0, response.url().getString(),
                        accessControlErrorDescription));
      return;
    }
  }

  m_client->didReceiveResponse(identifier, response, std::move(handle));
}
void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
{
    ASSERT(m_client);
    ASSERT_UNUSED(resource, resource == this->resource());

    RefPtr<DocumentThreadableLoader> protect(this);
    if (!isAllowedByPolicy(request.url())) {
        m_client->didFailRedirectCheck();
        request = ResourceRequest();
        return;
    }

    // Allow same origin requests to continue after allowing clients to audit the redirect.
    if (isAllowedRedirect(request.url())) {
        if (m_client->isDocumentThreadableLoaderClient())
            static_cast<DocumentThreadableLoaderClient*>(m_client)->willSendRequest(request, redirectResponse);
        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) {

        InspectorInstrumentation::didReceiveCORSRedirectResponse(m_document->frame(), resource->identifier(), m_document->frame()->loader().documentLoader(), redirectResponse, 0);

        bool allowRedirect = false;
        String accessControlErrorDescription;

        if (m_simpleRequest) {
            allowRedirect = CrossOriginAccessControl::isLegalRedirectLocation(request.url(), accessControlErrorDescription)
                            && (m_sameOriginRequest || passesAccessControlCheck(redirectResponse, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription));
        } else {
            accessControlErrorDescription = "The request was redirected to '"+ request.url().string() + "', which is disallowed for cross-origin requests that require preflight.";
        }

        if (allowRedirect) {
            // FIXME: consider combining this with CORS redirect handling performed by
            // CrossOriginAccessControl::handleRedirect().
            clearResource();

            RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::create(redirectResponse.url());
            RefPtr<SecurityOrigin> requestOrigin = SecurityOrigin::create(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 requests 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.credentialsRequested == ClientDidNotRequestCredentials)
                m_options.allowCredentials = DoNotAllowStoredCredentials;

            // Remove any headers that may have been added by the network layer that cause access control to fail.
            request.clearHTTPContentType();
            request.clearHTTPReferrer();
            request.clearHTTPOrigin();
            request.clearHTTPUserAgent();
            request.clearHTTPAccept();
            makeCrossOriginAccessRequest(request);
            return;
        }

        ResourceError error(errorDomainBlinkInternal, 0, redirectResponse.url().string(), accessControlErrorDescription);
        m_client->didFailAccessControlCheck(error);
    } else {
        m_client->didFailRedirectCheck();
    }
    request = ResourceRequest();
}