File: http_security_headers.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (141 lines) | stat: -rw-r--r-- 4,723 bytes parent folder | download | duplicates (6)
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