Fix cookie exclusion for fetch CORS pre-flight requests (fixes #3596)

Cookies (and other credentials) will be excluded when appropriate by
downgrading |credentials_mode| from kSameOrigin to kOmit.

Improve logic for Origin header inclusion, including a fix for
Referrer/Origin calculation in URLRequestJob::ComputeReferrerForPolicy
when used with custom standard schemes.

Specify correct CookiePartitionKeyCollection when loading cookies.

To test:
- Run tests from https://browseraudit.com/ with and without
  `--disable-request-handling-for-testing`. Results are the same.
- Run `ceftests --gtest_filter=CorsTest.*`.
This commit is contained in:
Marshall Greenblatt
2023-11-16 18:19:27 -05:00
parent a9f1ce090a
commit cf934a20a7
10 changed files with 166 additions and 32 deletions

View File

@@ -22,6 +22,7 @@
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/referrer.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/http/http_status_code.h"
@@ -30,6 +31,7 @@
#include "services/network/public/cpp/cors/cors.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/early_hints.mojom.h"
#include "third_party/blink/public/common/loader/referrer_utils.h"
namespace net_service {
@@ -65,6 +67,21 @@ bool DisableRequestHandlingForTesting() {
return disabled;
}
// Match logic in devtools_url_loader_interceptor.cc
// InterceptionJob::CalculateResponseTainting.
network::mojom::FetchResponseType CalculateResponseTainting(
bool should_check_cors,
network::mojom::RequestMode mode,
bool tainted_origin) {
if (should_check_cors) {
return network::mojom::FetchResponseType::kCors;
}
if (mode == network::mojom::RequestMode::kNoCors && tainted_origin) {
return network::mojom::FetchResponseType::kOpaque;
}
return network::mojom::FetchResponseType::kBasic;
}
} // namespace
// Owns all of the ProxyURLLoaderFactorys for a given BrowserContext. Since
@@ -451,25 +468,71 @@ void InterceptedRequest::Restart() {
current_request_uses_header_client_ =
factory_->url_loader_header_client_receiver_.is_bound();
if (request_.request_initiator &&
network::cors::ShouldCheckCors(request_.url, request_.request_initiator,
request_.mode)) {
if (scheme::IsCorsEnabledScheme(request_.url.scheme())) {
// Add the Origin header for CORS-enabled scheme requests.
request_.headers.SetHeaderIfMissing(
net::HttpRequestHeaders::kOrigin,
request_.request_initiator->Serialize());
} else if (!HasCrossOriginWhitelistEntry(
*request_.request_initiator,
url::Origin::Create(request_.url))) {
// Fail requests if a CORS check is required and the scheme is not CORS
// enabled. This matches the error condition that would be generated by
// CorsURLLoader::StartRequest in the network process.
SendErrorStatusAndCompleteImmediately(
network::URLLoaderCompletionStatus(network::CorsErrorStatus(
network::mojom::CorsError::kCorsDisabledScheme)));
return;
const bool is_cross_origin =
request_.request_initiator &&
!request_.request_initiator->IsSameOriginWith(request_.url);
const bool is_cors_enabled_scheme =
scheme::IsCorsEnabledScheme(request_.url.scheme());
// Match logic in network::cors::ShouldCheckCors.
bool should_check_cors =
is_cross_origin &&
request_.mode != network::mojom::RequestMode::kNavigate &&
request_.mode != network::mojom::RequestMode::kNoCors;
if (should_check_cors && !is_cors_enabled_scheme &&
!HasCrossOriginWhitelistEntry(*request_.request_initiator,
url::Origin::Create(request_.url))) {
// Fail requests if a CORS check is required and the scheme is not CORS
// enabled. This matches the error condition that would be generated by
// CorsURLLoader::StartRequest in the network process.
SendErrorStatusAndCompleteImmediately(
network::URLLoaderCompletionStatus(network::CorsErrorStatus(
network::mojom::CorsError::kCorsDisabledScheme)));
return;
}
// Maybe update |credentials_mode| for fetch requests.
if (request_.credentials_mode ==
network::mojom::CredentialsMode::kSameOrigin) {
// Match logic in devtools_url_loader_interceptor.cc
// InterceptionJob::FollowRedirect.
bool tainted_origin = false;
if (redirect_in_progress_ && request_.request_initiator &&
!url::IsSameOriginWith(request_.url, original_url_) &&
!request_.request_initiator->IsSameOriginWith(original_url_)) {
tainted_origin = true;
}
// Match logic in CorsURLLoader::StartNetworkRequest.
const auto response_tainting = CalculateResponseTainting(
should_check_cors, request_.mode, tainted_origin);
request_.credentials_mode =
network::cors::CalculateCredentialsFlag(request_.credentials_mode,
response_tainting)
? network::mojom::CredentialsMode::kInclude
: network::mojom::CredentialsMode::kOmit;
}
const bool should_add_origin_header =
// Cross-origin requests that are not kNavigate nor kNoCors.
should_check_cors ||
// Same-origin requests except for GET and HEAD.
(!is_cross_origin &&
request_.method != net::HttpRequestHeaders::kGetMethod &&
request_.method != net::HttpRequestHeaders::kHeadMethod);
if (should_add_origin_header) {
// Match logic in navigation_request.cc AddAdditionalRequestHeaders.
url::Origin origin_header_value =
request_.request_initiator.value_or(url::Origin());
origin_header_value = content::Referrer::SanitizeOriginForRequest(
request_.url, origin_header_value,
blink::ReferrerUtils::NetToMojoReferrerPolicy(
request_.referrer_policy));
request_.headers.SetHeaderIfMissing(net::HttpRequestHeaders::kOrigin,
origin_header_value.Serialize());
}
const GURL original_url = request_.url;