Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: net/http/http_security_headers.cc

Issue 1733973004: Limit Public-Key-Pins max-age to 60 days (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update tests Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/http/http_security_headers.h ('k') | net/http/http_security_headers_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <limits> 5 #include <limits>
6 6
7 #include "base/base64.h" 7 #include "base/base64.h"
8 #include "base/strings/string_number_conversions.h" 8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_piece.h" 9 #include "base/strings/string_piece.h"
10 #include "base/strings/string_tokenizer.h" 10 #include "base/strings/string_tokenizer.h"
11 #include "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
12 #include "net/http/http_security_headers.h" 12 #include "net/http/http_security_headers.h"
13 #include "net/http/http_util.h" 13 #include "net/http/http_util.h"
14 #include "url/gurl.h" 14 #include "url/gurl.h"
15 15
16 namespace net { 16 namespace net {
17 17
18 namespace { 18 namespace {
19 19
20 enum MaxAgeParsing { REQUIRE_MAX_AGE, DO_NOT_REQUIRE_MAX_AGE }; 20 enum MaxAgeParsing { REQUIRE_MAX_AGE, DO_NOT_REQUIRE_MAX_AGE };
21 21
22 static_assert(kMaxHSTSAgeSecs <= UINT32_MAX, "kMaxHSTSAgeSecs too large"); 22 static_assert(kMaxHSTSAgeSecs <= UINT32_MAX, "kMaxHSTSAgeSecs too large");
23 static_assert(kMaxHPKPAgeSecs <= UINT32_MAX, "kMaxHPKPAgeSecs too large");
23 24
24 // MaxAgeToInt converts a string representation of a "whole number" of 25 // MaxAgeToLimitedInt converts a string representation of a "whole number" of
25 // seconds into a uint32_t. The string may contain an arbitrarily large number, 26 // seconds into a uint32_t. The string may contain an arbitrarily large number,
26 // which will be clipped to kMaxHSTSAgeSecs and which is guaranteed to fit 27 // which will be clipped to a supplied limit and which is guaranteed to fit
27 // within a 32-bit unsigned integer. False is returned on any parse error. 28 // within a 32-bit unsigned integer. False is returned on any parse error.
28 bool MaxAgeToInt(std::string::const_iterator begin, 29 bool MaxAgeToLimitedInt(std::string::const_iterator begin,
29 std::string::const_iterator end, 30 std::string::const_iterator end,
30 uint32_t* result) { 31 uint32_t limit,
32 uint32_t* result) {
31 const base::StringPiece s(begin, end); 33 const base::StringPiece s(begin, end);
32 if (s.empty()) 34 if (s.empty())
33 return false; 35 return false;
34 36
35 int64_t i = 0; 37 int64_t i = 0;
36 38
37 // Return false on any StringToInt64 parse errors *except* for int64_t 39 // Return false on any StringToInt64 parse errors *except* for int64_t
38 // overflow. StringToInt64 is used, rather than StringToUint64, in order to 40 // overflow. StringToInt64 is used, rather than StringToUint64, in order to
39 // properly handle and reject negative numbers (StringToUint64 does not return 41 // properly handle and reject negative numbers (StringToUint64 does not return
40 // false on negative numbers). For values too large to be stored in an 42 // false on negative numbers). For values too large to be stored in an
41 // int64_t, StringToInt64 will return false with i set to 43 // int64_t, StringToInt64 will return false with i set to
42 // std::numeric_limits<int64_t>::max(), so this case is detected by the 44 // std::numeric_limits<int64_t>::max(), so this case is allowed to fall
43 // immediately following if-statement and allowed to fall through so that i 45 // through so that i gets clipped to limit.
44 // gets clipped to kMaxHSTSAgeSecs.
45 if (!base::StringToInt64(s, &i) && i != std::numeric_limits<int64_t>::max()) 46 if (!base::StringToInt64(s, &i) && i != std::numeric_limits<int64_t>::max())
46 return false; 47 return false;
47 if (i < 0) 48 if (i < 0)
48 return false; 49 return false;
49 if (i > kMaxHSTSAgeSecs) 50 if (i > limit)
50 i = kMaxHSTSAgeSecs; 51 i = limit;
51 *result = (uint32_t)i; 52 *result = (uint32_t)i;
52 return true; 53 return true;
53 } 54 }
54 55
55 // Returns true iff there is an item in |pins| which is not present in 56 // Returns true iff there is an item in |pins| which is not present in
56 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". 57 // |from_cert_chain|. Such an SPKI hash is called a "backup pin".
57 bool IsBackupPinPresent(const HashValueVector& pins, 58 bool IsBackupPinPresent(const HashValueVector& pins,
58 const HashValueVector& from_cert_chain) { 59 const HashValueVector& from_cert_chain) {
59 for (HashValueVector::const_iterator i = pins.begin(); i != pins.end(); 60 for (HashValueVector::const_iterator i = pins.begin(); i != pins.end();
60 ++i) { 61 ++i) {
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
136 137
137 HttpUtil::NameValuePairsIterator name_value_pairs( 138 HttpUtil::NameValuePairsIterator name_value_pairs(
138 value.begin(), value.end(), ';', 139 value.begin(), value.end(), ';',
139 HttpUtil::NameValuePairsIterator::VALUES_OPTIONAL); 140 HttpUtil::NameValuePairsIterator::VALUES_OPTIONAL);
140 141
141 while (name_value_pairs.GetNext()) { 142 while (name_value_pairs.GetNext()) {
142 if (base::LowerCaseEqualsASCII( 143 if (base::LowerCaseEqualsASCII(
143 base::StringPiece(name_value_pairs.name_begin(), 144 base::StringPiece(name_value_pairs.name_begin(),
144 name_value_pairs.name_end()), 145 name_value_pairs.name_end()),
145 "max-age")) { 146 "max-age")) {
146 if (!MaxAgeToInt(name_value_pairs.value_begin(), 147 if (!MaxAgeToLimitedInt(name_value_pairs.value_begin(),
147 name_value_pairs.value_end(), &max_age_candidate)) { 148 name_value_pairs.value_end(), kMaxHPKPAgeSecs,
149 &max_age_candidate)) {
148 return false; 150 return false;
149 } 151 }
150 parsed_max_age = true; 152 parsed_max_age = true;
151 } else if (base::LowerCaseEqualsASCII( 153 } else if (base::LowerCaseEqualsASCII(
152 base::StringPiece(name_value_pairs.name_begin(), 154 base::StringPiece(name_value_pairs.name_begin(),
153 name_value_pairs.name_end()), 155 name_value_pairs.name_end()),
154 "pin-sha256")) { 156 "pin-sha256")) {
155 // Pins are always quoted. 157 // Pins are always quoted.
156 if (!name_value_pairs.value_is_quoted() || 158 if (!name_value_pairs.value_is_quoted() ||
157 !ParseAndAppendPin(name_value_pairs.value_begin(), 159 !ParseAndAppendPin(name_value_pairs.value_begin(),
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
273 if (*tokenizer.token_begin() != '=') 275 if (*tokenizer.token_begin() != '=')
274 return false; 276 return false;
275 DCHECK_EQ(tokenizer.token().length(), 1U); 277 DCHECK_EQ(tokenizer.token().length(), 1U);
276 state = AFTER_MAX_AGE_EQUALS; 278 state = AFTER_MAX_AGE_EQUALS;
277 break; 279 break;
278 280
279 case AFTER_MAX_AGE_EQUALS: 281 case AFTER_MAX_AGE_EQUALS:
280 if (base::IsAsciiWhitespace(*tokenizer.token_begin())) 282 if (base::IsAsciiWhitespace(*tokenizer.token_begin()))
281 continue; 283 continue;
282 unquoted = HttpUtil::Unquote(tokenizer.token()); 284 unquoted = HttpUtil::Unquote(tokenizer.token());
283 if (!MaxAgeToInt(unquoted.begin(), unquoted.end(), &max_age_candidate)) 285 if (!MaxAgeToLimitedInt(unquoted.begin(), unquoted.end(),
286 kMaxHSTSAgeSecs, &max_age_candidate))
284 return false; 287 return false;
285 state = AFTER_MAX_AGE; 288 state = AFTER_MAX_AGE;
286 break; 289 break;
287 290
288 case AFTER_MAX_AGE: 291 case AFTER_MAX_AGE:
289 case AFTER_INCLUDE_SUBDOMAINS: 292 case AFTER_INCLUDE_SUBDOMAINS:
290 if (base::IsAsciiWhitespace(*tokenizer.token_begin())) 293 if (base::IsAsciiWhitespace(*tokenizer.token_begin()))
291 continue; 294 continue;
292 else if (*tokenizer.token_begin() == ';') 295 else if (*tokenizer.token_begin() == ';')
293 state = DIRECTIVE_END; 296 state = DIRECTIVE_END;
294 else 297 else
295 return false; 298 return false;
296 break; 299 break;
297 300
298 case AFTER_UNKNOWN_LABEL: 301 case AFTER_UNKNOWN_LABEL:
299 // Consume and ignore the post-label contents (if any). 302 // Consume and ignore the post-label contents (if any).
300 if (*tokenizer.token_begin() != ';') 303 if (*tokenizer.token_begin() != ';')
301 continue; 304 continue;
302 state = DIRECTIVE_END; 305 state = DIRECTIVE_END;
303 break; 306 break;
304 } 307 }
305 } 308 }
306 309
307 // We've consumed all the input. Let's see what state we ended up in. 310 // We've consumed all the input. Let's see what state we ended up in.
308 if (max_age_observed != 1 || 311 if (max_age_observed != 1 ||
309 (include_subdomains_observed != 0 && include_subdomains_observed != 1)) { 312 (include_subdomains_observed != 0 && include_subdomains_observed != 1)) {
310 return false; 313 return false;
311 } 314 }
312 315
313 switch (state) { 316 switch (state) {
314 case DIRECTIVE_END: 317 case DIRECTIVE_END:
315 case AFTER_MAX_AGE: 318 case AFTER_MAX_AGE:
316 case AFTER_INCLUDE_SUBDOMAINS: 319 case AFTER_INCLUDE_SUBDOMAINS:
317 case AFTER_UNKNOWN_LABEL: 320 case AFTER_UNKNOWN_LABEL:
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
369 bool* include_subdomains, 372 bool* include_subdomains,
370 HashValueVector* hashes, 373 HashValueVector* hashes,
371 GURL* report_uri) { 374 GURL* report_uri) {
372 // max-age is irrelevant for Report-Only headers. 375 // max-age is irrelevant for Report-Only headers.
373 base::TimeDelta unused_max_age; 376 base::TimeDelta unused_max_age;
374 return ParseHPKPHeaderImpl(value, DO_NOT_REQUIRE_MAX_AGE, &unused_max_age, 377 return ParseHPKPHeaderImpl(value, DO_NOT_REQUIRE_MAX_AGE, &unused_max_age,
375 include_subdomains, hashes, report_uri); 378 include_subdomains, hashes, report_uri);
376 } 379 }
377 380
378 } // namespace net 381 } // namespace net
OLDNEW
« no previous file with comments | « net/http/http_security_headers.h ('k') | net/http/http_security_headers_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698