• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/url_request/redirect_util.h"
6 
7 #include "base/check.h"
8 #include "base/memory/scoped_refptr.h"
9 #include "base/strings/stringprintf.h"
10 #include "net/http/http_request_headers.h"
11 #include "net/http/http_response_headers.h"
12 #include "net/http/http_util.h"
13 #include "net/url_request/redirect_info.h"
14 #include "url/gurl.h"
15 #include "url/origin.h"
16 
17 namespace net {
18 
19 // static
UpdateHttpRequest(const GURL & original_url,const std::string & original_method,const RedirectInfo & redirect_info,const absl::optional<std::vector<std::string>> & removed_headers,const absl::optional<net::HttpRequestHeaders> & modified_headers,HttpRequestHeaders * request_headers,bool * should_clear_upload)20 void RedirectUtil::UpdateHttpRequest(
21     const GURL& original_url,
22     const std::string& original_method,
23     const RedirectInfo& redirect_info,
24     const absl::optional<std::vector<std::string>>& removed_headers,
25     const absl::optional<net::HttpRequestHeaders>& modified_headers,
26     HttpRequestHeaders* request_headers,
27     bool* should_clear_upload) {
28   DCHECK(request_headers);
29   DCHECK(should_clear_upload);
30 
31   *should_clear_upload = false;
32 
33   if (removed_headers) {
34     for (const std::string& key : removed_headers.value())
35       request_headers->RemoveHeader(key);
36   }
37 
38   if (redirect_info.new_method != original_method) {
39     // TODO(davidben): This logic still needs to be replicated at the consumers.
40     //
41     // The Origin header is sent on anything that is not a GET or HEAD, which
42     // suggests all redirects that change methods (since they always change to
43     // GET) should drop the Origin header.
44     // See https://fetch.spec.whatwg.org/#origin-header
45     // TODO(jww): This is Origin header removal is probably layering violation
46     // and should be refactored into //content. See https://crbug.com/471397.
47     // See also: https://crbug.com/760487
48     request_headers->RemoveHeader(HttpRequestHeaders::kOrigin);
49 
50     // This header should only be present further down the stack, but remove it
51     // here just in case.
52     request_headers->RemoveHeader(HttpRequestHeaders::kContentLength);
53 
54     // These are "request-body-headers" and should be removed on redirects that
55     // change the method, per the fetch spec.
56     // https://fetch.spec.whatwg.org/
57     request_headers->RemoveHeader(HttpRequestHeaders::kContentType);
58     request_headers->RemoveHeader("Content-Encoding");
59     request_headers->RemoveHeader("Content-Language");
60     request_headers->RemoveHeader("Content-Location");
61 
62     *should_clear_upload = true;
63   }
64 
65   // Cross-origin redirects should not result in an Origin header value that is
66   // equal to the original request's Origin header. This is necessary to prevent
67   // a reflection of POST requests to bypass CSRF protections. If the header was
68   // not set to "null", a POST request from origin A to a malicious origin M
69   // could be redirected by M back to A.
70   //
71   // This behavior is specified in step 10 of the HTTP-redirect fetch
72   // algorithm[1] (which supercedes the behavior outlined in RFC 6454[2].
73   //
74   // [1]: https://fetch.spec.whatwg.org/#http-redirect-fetch
75   // [2]: https://tools.ietf.org/html/rfc6454#section-7
76   //
77   // TODO(jww): This is a layering violation and should be refactored somewhere
78   // up into //net's embedder. https://crbug.com/471397
79   if (!url::IsSameOriginWith(redirect_info.new_url, original_url) &&
80       request_headers->HasHeader(HttpRequestHeaders::kOrigin)) {
81     request_headers->SetHeader(HttpRequestHeaders::kOrigin,
82                                url::Origin().Serialize());
83   }
84 
85   if (modified_headers)
86     request_headers->MergeFrom(modified_headers.value());
87 }
88 
89 // static
GetReferrerPolicyHeader(const HttpResponseHeaders * response_headers)90 absl::optional<std::string> RedirectUtil::GetReferrerPolicyHeader(
91     const HttpResponseHeaders* response_headers) {
92   if (!response_headers)
93     return absl::nullopt;
94   std::string referrer_policy_header;
95   if (!response_headers->GetNormalizedHeader("Referrer-Policy",
96                                              &referrer_policy_header)) {
97     return absl::nullopt;
98   }
99   return referrer_policy_header;
100 }
101 
102 // static
SynthesizeRedirectHeaders(const GURL & redirect_destination,ResponseCode response_code,const std::string & redirect_reason,const HttpRequestHeaders & request_headers)103 scoped_refptr<HttpResponseHeaders> RedirectUtil::SynthesizeRedirectHeaders(
104     const GURL& redirect_destination,
105     ResponseCode response_code,
106     const std::string& redirect_reason,
107     const HttpRequestHeaders& request_headers) {
108   std::string header_string = base::StringPrintf(
109       "HTTP/1.1 %i Internal Redirect\n"
110       "Location: %s\n"
111       "Cross-Origin-Resource-Policy: Cross-Origin\n"
112       "Non-Authoritative-Reason: %s",
113       response_code, redirect_destination.spec().c_str(),
114       redirect_reason.c_str());
115 
116   std::string http_origin;
117   if (request_headers.GetHeader("Origin", &http_origin)) {
118     // If this redirect is used in a cross-origin request, add CORS headers to
119     // make sure that the redirect gets through. Note that the destination URL
120     // is still subject to the usual CORS policy, i.e. the resource will only
121     // be available to web pages if the server serves the response with the
122     // required CORS response headers.
123     header_string += base::StringPrintf(
124         "\n"
125         "Access-Control-Allow-Origin: %s\n"
126         "Access-Control-Allow-Credentials: true",
127         http_origin.c_str());
128   }
129 
130   auto fake_headers = base::MakeRefCounted<HttpResponseHeaders>(
131       HttpUtil::AssembleRawHeaders(header_string));
132   DCHECK(fake_headers->IsRedirect(nullptr));
133 
134   return fake_headers;
135 }
136 
137 }  // namespace net
138