1 // Copyright 2019 The Chromium Authors. All rights reserved.
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 "services/network/public/cpp/header_util.h"
6 
7 #include "base/strings/string_util.h"
8 #include "net/http/http_request_headers.h"
9 
10 namespace network {
11 
12 namespace {
13 
14 // Headers that consumers are not trusted to set. All "Proxy-" prefixed messages
15 // are blocked inline. The"Authorization" auth header is deliberately not
16 // included, since OAuth requires websites be able to set it directly. These are
17 // a subset of headers forbidden by the fetch spec.
18 //
19 // This list has some values in common with
20 // https://fetch.spec.whatwg.org/#forbidden-header-name, but excludes some
21 // values that are still set by the caller in Chrome.
22 const char* kUnsafeHeaders[] = {
23     // This is determined by the upload body and set by net/. A consumer
24     // overriding that could allow for Bad Things.
25     net::HttpRequestHeaders::kContentLength,
26 
27     // Disallow setting the Host header because it can conflict with specified
28     // URL and logic related to isolating sites.
29     net::HttpRequestHeaders::kHost,
30 
31     // Trailers are not supported.
32     "Trailer", "Te",
33 
34     // Websockets use a different API.
35     "Upgrade",
36 
37     // Obsolete header, and network stack manages headers itself.
38     "Cookie2",
39 
40     // Not supported by net/.
41     "Keep-Alive",
42 
43     // Forbidden by the fetch spec.
44     net::HttpRequestHeaders::kTransferEncoding,
45 
46     // TODO(mmenke): Figure out what to do about the remaining headers:
47     // Connection, Cookie, Date, Expect, Referer, Via.
48 };
49 
50 // Headers that consumers are currently allowed to set, with the exception of
51 // certain values could cause problems.
52 // TODO(mmenke): Gather stats on these, and see if these headers can be banned
53 // outright instead.
54 const struct {
55   const char* name;
56   const char* value;
57 } kUnsafeHeaderValues[] = {
58     // Websockets use a different API.
59     {net::HttpRequestHeaders::kConnection, "Upgrade"},
60 };
61 
62 }  // namespace
63 
IsRequestHeaderSafe(const base::StringPiece & key,const base::StringPiece & value)64 bool IsRequestHeaderSafe(const base::StringPiece& key,
65                          const base::StringPiece& value) {
66   for (const auto* header : kUnsafeHeaders) {
67     if (base::EqualsCaseInsensitiveASCII(header, key))
68       return false;
69   }
70 
71   for (const auto& header : kUnsafeHeaderValues) {
72     if (base::EqualsCaseInsensitiveASCII(header.name, key) &&
73         base::EqualsCaseInsensitiveASCII(header.value, value)) {
74       return false;
75     }
76   }
77 
78   // Proxy headers are destined for the proxy, so shouldn't be set by callers.
79   if (base::StartsWith(key, "Proxy-", base::CompareCase::INSENSITIVE_ASCII))
80     return false;
81 
82   return true;
83 }
84 
AreRequestHeadersSafe(const net::HttpRequestHeaders & request_headers)85 bool AreRequestHeadersSafe(const net::HttpRequestHeaders& request_headers) {
86   net::HttpRequestHeaders::Iterator it(request_headers);
87 
88   while (it.GetNext()) {
89     if (!IsRequestHeaderSafe(it.name(), it.value()))
90       return false;
91   }
92 
93   return true;
94 }
95 
96 }  // namespace network
97