1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits>
#include <string_view>
#include "base/base64.h"
#include "base/check_op.h"
#include "base/notreached.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "net/base/parse_number.h"
#include "net/http/http_security_headers.h"
#include "net/http/http_util.h"
#include "url/gurl.h"
namespace net {
namespace {
enum MaxAgeParsing { REQUIRE_MAX_AGE, DO_NOT_REQUIRE_MAX_AGE };
// MaxAgeToLimitedInt converts a string representation of a "whole number" of
// seconds into a uint32_t. The string may contain an arbitrarily large number,
// which will be clipped to a supplied limit and which is guaranteed to fit
// within a 32-bit unsigned integer. False is returned on any parse error.
bool MaxAgeToLimitedInt(std::string_view s, uint32_t limit, uint32_t* result) {
ParseIntError error;
if (!ParseUint32(s, ParseIntFormat::NON_NEGATIVE, result, &error)) {
if (error == ParseIntError::FAILED_OVERFLOW) {
*result = limit;
} else {
return false;
}
}
if (*result > limit)
*result = limit;
return true;
}
} // namespace
// Parse the Strict-Transport-Security header, as currently defined in
// http://tools.ietf.org/html/draft-ietf-websec-strict-transport-sec-14:
//
// Strict-Transport-Security = "Strict-Transport-Security" ":"
// [ directive ] *( ";" [ directive ] )
//
// directive = directive-name [ "=" directive-value ]
// directive-name = token
// directive-value = token | quoted-string
//
// 1. The order of appearance of directives is not significant.
//
// 2. All directives MUST appear only once in an STS header field.
// Directives are either optional or required, as stipulated in
// their definitions.
//
// 3. Directive names are case-insensitive.
//
// 4. UAs MUST ignore any STS header fields containing directives, or
// other header field value data, that does not conform to the
// syntax defined in this specification.
//
// 5. If an STS header field contains directive(s) not recognized by
// the UA, the UA MUST ignore the unrecognized directives and if the
// STS header field otherwise satisfies the above requirements (1
// through 4), the UA MUST process the recognized directives.
bool ParseHSTSHeader(std::string_view value,
base::TimeDelta* max_age,
bool* include_subdomains) {
uint32_t max_age_value = 0;
bool max_age_seen = false;
bool include_subdomains_value = false;
HttpUtil::NameValuePairsIterator hsts_iterator(
value, ';', HttpUtil::NameValuePairsIterator::Values::NOT_REQUIRED,
HttpUtil::NameValuePairsIterator::Quotes::STRICT_QUOTES);
while (hsts_iterator.GetNext()) {
// Process `max-age`:
if (base::EqualsCaseInsensitiveASCII(hsts_iterator.name(), "max-age")) {
// Reject the header if `max-age` is specified more than once.
if (max_age_seen) {
return false;
}
max_age_seen = true;
// Reject the header if `max-age`'s value is invalid. Otherwise, store it
// in `max_age_value`.
if (!MaxAgeToLimitedInt(hsts_iterator.value(), kMaxHSTSAgeSecs,
&max_age_value)) {
return false;
}
// Process `includeSubDomains`:
} else if (base::EqualsCaseInsensitiveASCII(hsts_iterator.name(),
"includeSubDomains")) {
// Reject the header if `includeSubDomains` is specified more than once.
if (include_subdomains_value) {
return false;
}
// Reject the header if `includeSubDomains` has a value specified:
if (!hsts_iterator.value().empty() || hsts_iterator.value_is_quoted()) {
return false;
}
include_subdomains_value = true;
// Process unknown directives.
} else {
// Reject the header if a directive's name or unquoted value doesn't match
// the `token` grammar.
if (!HttpUtil::IsToken(hsts_iterator.name()) ||
hsts_iterator.name().empty()) {
return false;
}
if (!hsts_iterator.value().empty() && !hsts_iterator.value_is_quoted() &&
!HttpUtil::IsToken(hsts_iterator.value())) {
return false;
}
}
}
if (!hsts_iterator.valid()) {
return false;
}
// Reject the header if no `max-age` was set.
if (!max_age_seen) {
return false;
}
*max_age = base::Seconds(max_age_value);
*include_subdomains = include_subdomains_value;
return true;
}
} // namespace net
|