OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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 "config.h" | |
6 #include "core/frame/SubresourceIntegrity.h" | |
7 | |
8 #include "core/HTMLNames.h" | |
9 #include "core/dom/Document.h" | |
10 #include "core/dom/Element.h" | |
11 #include "core/frame/UseCounter.h" | |
12 #include "platform/Crypto.h" | |
13 #include "platform/ParsingUtilities.h" | |
14 #include "platform/RuntimeEnabledFeatures.h" | |
15 #include "platform/weborigin/KURL.h" | |
16 #include "platform/weborigin/SecurityOrigin.h" | |
17 #include "public/platform/WebCrypto.h" | |
18 #include "public/platform/WebCryptoAlgorithm.h" | |
19 #include "wtf/ASCIICType.h" | |
20 #include "wtf/text/Base64.h" | |
21 #include "wtf/text/StringUTF8Adaptor.h" | |
22 #include "wtf/text/WTFString.h" | |
23 | |
24 namespace blink { | |
25 | |
26 static bool isIntegrityCharacter(UChar c) | |
27 { | |
28 // Check if it's a base64 encoded value. | |
29 return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '='; | |
Mike West
2014/09/16 06:45:04
Nit: It's probably worth sharing the base64 check
jww
2014/09/16 22:34:49
Acknowledged.
| |
30 } | |
31 | |
32 static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2) | |
33 { | |
34 if (digest1.size() != digest2.size()) | |
35 return false; | |
36 | |
37 for (size_t i = 0; i < digest1.size(); i++) { | |
38 if (digest1[i] != digest2[i]) | |
39 return false; | |
40 } | |
41 | |
42 return true; | |
43 } | |
44 | |
45 // TODO(jww) If CheckSubresourceIntegrity fails, Blink should create a console | |
46 // message to alert the developer of the failure. | |
47 bool SubresourceIntegrity::CheckSubresourceIntegrity(const Element& element, con st String& source, const KURL& resourceUrl) | |
48 { | |
49 if (!RuntimeEnabledFeatures::subresourceIntegrityEnabled()) | |
50 return true; | |
51 | |
52 if (!element.fastHasAttribute(HTMLNames::integrityAttr)) | |
53 return true; | |
54 | |
55 // TODO(jww): If insecureOriginMsg is not empty after the check, Blink | |
56 // should send a console message. | |
57 // | |
58 // Instead of just checking SecurityOrigin::isSecure on resourceUrl, this | |
59 // checks canAccessFeatureRequiringSecureOrigin so that file:// protocols | |
60 // and localhost resources can be allowed. These may be useful for testing | |
61 // and are allowed for features requiring authenticated origins, so Chrome | |
62 // allows them here. | |
Mike West
2014/09/16 06:45:04
I don't like that we've ended up with two levels o
jww
2014/09/16 22:34:49
Okay, I will do that, although there *is* a subtle
| |
63 String insecureOriginMsg = ""; | |
64 RefPtr<SecurityOrigin> resourceSecurityOrigin = SecurityOrigin::create(resou rceUrl); | |
65 if (!element.document().securityOrigin()->canAccessFeatureRequiringSecureOri gin(insecureOriginMsg) || !resourceSecurityOrigin->canAccessFeatureRequiringSecu reOrigin(insecureOriginMsg)) { | |
66 UseCounter::count(element.document(), UseCounter::SRIElementWithIntegrit yAttributeAndInsecureResource); | |
Mike West
2014/09/16 06:45:04
This will also trigger if the document itself is i
jww
2014/09/16 22:34:49
Done.
| |
67 return false; | |
68 } | |
69 | |
70 String integrity; | |
71 HashAlgorithm algorithm; | |
72 if (!parseIntegrityAttribute(element.fastGetAttribute(HTMLNames::integrityAt tr), integrity, algorithm)) { | |
73 UseCounter::count(element.document(), UseCounter::SRIElementWithUnparsab leIntegrityAttribute); | |
74 return false; | |
75 } | |
76 | |
77 Vector<char> hashVector; | |
78 base64Decode(integrity, hashVector); | |
79 | |
80 StringUTF8Adaptor normalizedSource(source, StringUTF8Adaptor::Normalize, WTF ::EntitiesForUnencodables); | |
81 | |
82 DigestValue digest; | |
83 bool digestSuccess = computeDigest(algorithm, normalizedSource.data(), norma lizedSource.length(), digest); | |
84 | |
85 if (digestSuccess) { | |
86 DigestValue convertedHashVector; | |
87 convertedHashVector.append(reinterpret_cast<uint8_t*>(hashVector.data()) , hashVector.size()); | |
88 if (DigestsEqual(digest, convertedHashVector)) { | |
89 UseCounter::count(element.document(), UseCounter::SRIElementWithMatc hingIntegrityAttribute); | |
90 return true; | |
91 } | |
92 } | |
93 | |
94 UseCounter::count(element.document(), UseCounter::SRIElementWithNonMatchingI ntegrityAttribute); | |
95 return false; | |
96 } | |
97 | |
98 bool SubresourceIntegrity::parseIntegrityAttribute(const String& attribute, Stri ng& integrity, HashAlgorithm& algorithm) | |
Mike West
2014/09/16 06:45:03
This parsing algorithm looks solid, but I'd really
jww
2014/09/16 22:34:49
Done.
| |
99 { | |
100 DEFINE_STATIC_LOCAL(const String, integrityPrefix, ("ni://")); | |
101 // Any additions or subtractions from this struct should also modify the | |
102 // respective entries in the kAlgorithmMap array in checkDigest(). | |
103 static const struct { | |
104 const char* prefix; | |
105 HashAlgorithm algorithm; | |
106 } kSupportedPrefixes[] = { | |
107 { "sha256", HashAlgorithmSha256 }, | |
108 { "sha384", HashAlgorithmSha384 }, | |
109 { "sha512", HashAlgorithmSha512 } | |
110 }; | |
111 Vector<UChar> characters; | |
112 attribute.appendTo(characters); | |
113 UChar* begin = characters.data(); | |
114 UChar* end = characters.end(); | |
115 | |
116 if (!equalIgnoringCase(integrityPrefix.characters8(), begin, integrityPrefix .length())) | |
Mike West
2014/09/16 06:45:03
I don't recall if 'fastGetAttribute' strips whites
jww
2014/09/16 22:34:49
Done.
| |
117 return false; | |
118 | |
119 const UChar* algorithmStart = begin + integrityPrefix.length(); | |
120 const UChar* algorithmEnd = algorithmStart; | |
121 | |
122 skipUntil<UChar>(algorithmEnd, end, ';'); | |
123 | |
124 // Instead of this sizeof() calculation to get the length of this array, | |
125 // it would be preferable to use WTF_ARRAY_LENGTH for simplicity and to | |
126 // guarantee a compile time calculation. Unfortunately, on some | |
127 // compliers, the call to WTF_ARRAY_LENGTH fails on arrays of anonymous | |
128 // stucts, so, for now, it is necessary to resort to this sizeof | |
129 // calculation. | |
130 size_t i = 0; | |
131 size_t kSupportedPrefixesLength = sizeof(kSupportedPrefixes) / sizeof(kSuppo rtedPrefixes[0]); | |
132 for (; i < kSupportedPrefixesLength; i++) { | |
133 if (equalIgnoringCase(kSupportedPrefixes[i].prefix, algorithmStart, strl en(kSupportedPrefixes[i].prefix))) { | |
134 algorithm = kSupportedPrefixes[i].algorithm; | |
135 break; | |
136 } | |
137 } | |
138 | |
139 if (i == kSupportedPrefixesLength) | |
140 return false; | |
141 | |
142 const UChar* integrityStart = algorithmEnd; | |
143 if (!skipExactly<UChar>(integrityStart, end, ';')) | |
144 return false; | |
145 | |
146 const UChar* integrityEnd = integrityStart; | |
147 skipWhile<UChar, isIntegrityCharacter>(integrityEnd, end); | |
148 if (integrityEnd != end) | |
149 return false; | |
150 | |
151 integrity = String(integrityStart, integrityEnd - integrityStart); | |
152 return true; | |
153 } | |
154 | |
155 } // namespace blink | |
OLD | NEW |