OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 "config.h" | 5 #include "config.h" |
6 #include "core/frame/SubresourceIntegrity.h" | 6 #include "core/frame/SubresourceIntegrity.h" |
7 | 7 |
8 #include "core/HTMLNames.h" | 8 #include "core/HTMLNames.h" |
9 #include "core/dom/Document.h" | 9 #include "core/dom/Document.h" |
10 #include "core/dom/Element.h" | 10 #include "core/dom/Element.h" |
(...skipping 10 matching lines...) Expand all Loading... | |
21 #include "wtf/ASCIICType.h" | 21 #include "wtf/ASCIICType.h" |
22 #include "wtf/text/Base64.h" | 22 #include "wtf/text/Base64.h" |
23 #include "wtf/text/StringUTF8Adaptor.h" | 23 #include "wtf/text/StringUTF8Adaptor.h" |
24 #include "wtf/text/WTFString.h" | 24 #include "wtf/text/WTFString.h" |
25 | 25 |
26 namespace blink { | 26 namespace blink { |
27 | 27 |
28 // FIXME: This should probably use common functions with ContentSecurityPolicy. | 28 // FIXME: This should probably use common functions with ContentSecurityPolicy. |
29 static bool isIntegrityCharacter(UChar c) | 29 static bool isIntegrityCharacter(UChar c) |
30 { | 30 { |
31 // FIXME: This should be checking base64url encoding, not base64 encoding. | |
32 | |
31 // Check if it's a base64 encoded value. | 33 // Check if it's a base64 encoded value. |
32 return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '='; | 34 return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '='; |
33 } | 35 } |
34 | 36 |
35 static void logErrorToConsole(const String& message, Document& document) | 37 static void logErrorToConsole(const String& message, Document& document) |
36 { | 38 { |
37 document.addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, Err orMessageLevel, message)); | 39 document.addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, Err orMessageLevel, message)); |
38 } | 40 } |
39 | 41 |
40 static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2) | 42 static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2) |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
105 } | 107 } |
106 if (!resourceSecurityOrigin->canAccessFeatureRequiringSecureOrigin(insecureO riginMsg)) { | 108 if (!resourceSecurityOrigin->canAccessFeatureRequiringSecureOrigin(insecureO riginMsg)) { |
107 UseCounter::count(document, UseCounter::SRIElementWithIntegrityAttribute AndInsecureResource); | 109 UseCounter::count(document, UseCounter::SRIElementWithIntegrityAttribute AndInsecureResource); |
108 logErrorToConsole("The 'integrity' attribute may only be used with resou rces on secure origins.", document); | 110 logErrorToConsole("The 'integrity' attribute may only be used with resou rces on secure origins.", document); |
109 return false; | 111 return false; |
110 } | 112 } |
111 | 113 |
112 String integrity; | 114 String integrity; |
113 HashAlgorithm algorithm; | 115 HashAlgorithm algorithm; |
114 String attribute = element.fastGetAttribute(HTMLNames::integrityAttr); | 116 String attribute = element.fastGetAttribute(HTMLNames::integrityAttr); |
115 if (!parseIntegrityAttribute(attribute, integrity, algorithm)) { | 117 if (!parseIntegrityAttribute(attribute, integrity, algorithm, document)) { |
118 // An error is logged to the console during parsing; we don't need to lo g one here. | |
116 UseCounter::count(document, UseCounter::SRIElementWithUnparsableIntegrit yAttribute); | 119 UseCounter::count(document, UseCounter::SRIElementWithUnparsableIntegrit yAttribute); |
117 logErrorToConsole("The 'integrity' attribute's value '" + attribute + " ' is not valid integrity metadata.", document); | |
118 return false; | 120 return false; |
119 } | 121 } |
120 | 122 |
121 Vector<char> hashVector; | 123 Vector<char> hashVector; |
122 base64Decode(integrity, hashVector); | 124 base64Decode(integrity, hashVector); |
123 | 125 |
124 StringUTF8Adaptor normalizedSource(source, StringUTF8Adaptor::Normalize, WTF ::EntitiesForUnencodables); | 126 StringUTF8Adaptor normalizedSource(source, StringUTF8Adaptor::Normalize, WTF ::EntitiesForUnencodables); |
125 | 127 |
126 DigestValue digest; | 128 DigestValue digest; |
127 bool digestSuccess = computeDigest(algorithm, normalizedSource.data(), norma lizedSource.length(), digest); | 129 bool digestSuccess = computeDigest(algorithm, normalizedSource.data(), norma lizedSource.length(), digest); |
(...skipping 13 matching lines...) Expand all Loading... | |
141 logErrorToConsole("The computed " + algorithmToString(algorithm) + " integrity '" + digestToString(digest) + "' does not match the 'integrity' attri bute '" + integrity + "' for resource '" + resourceUrl.elidedString() + "'.", do cument); | 143 logErrorToConsole("The computed " + algorithmToString(algorithm) + " integrity '" + digestToString(digest) + "' does not match the 'integrity' attri bute '" + integrity + "' for resource '" + resourceUrl.elidedString() + "'.", do cument); |
142 } | 144 } |
143 } else { | 145 } else { |
144 logErrorToConsole("There was an error computing an 'integrity' value for resource '" + resourceUrl.elidedString() + "'.", document); | 146 logErrorToConsole("There was an error computing an 'integrity' value for resource '" + resourceUrl.elidedString() + "'.", document); |
145 } | 147 } |
146 | 148 |
147 UseCounter::count(document, UseCounter::SRIElementWithNonMatchingIntegrityAt tribute); | 149 UseCounter::count(document, UseCounter::SRIElementWithNonMatchingIntegrityAt tribute); |
148 return false; | 150 return false; |
149 } | 151 } |
150 | 152 |
151 bool SubresourceIntegrity::parseIntegrityAttribute(const String& attribute, Stri ng& integrity, HashAlgorithm& algorithm) | 153 // Before: |
154 // | |
155 // ni:///[algorithm];[hash] | |
156 // ^ ^ | |
157 // position end | |
158 // | |
159 // After: | |
160 // | |
161 // ni:///[algorithm];[hash] | |
162 // ^ ^ | |
163 // position end | |
164 bool SubresourceIntegrity::parseAlgorithm(const UChar*& position, const UChar* e nd, HashAlgorithm& algorithm) | |
152 { | 165 { |
153 DEFINE_STATIC_LOCAL(const String, integrityPrefix, ("ni://")); | |
154 // Any additions or subtractions from this struct should also modify the | 166 // Any additions or subtractions from this struct should also modify the |
155 // respective entries in the kAlgorithmMap array in checkDigest() as well | 167 // respective entries in the kAlgorithmMap array in checkDigest() as well |
156 // as the array in algorithmToString(). | 168 // as the array in algorithmToString(). |
157 static const struct { | 169 static const struct { |
158 const char* prefix; | 170 const char* prefix; |
159 HashAlgorithm algorithm; | 171 HashAlgorithm algorithm; |
160 } kSupportedPrefixes[] = { | 172 } kSupportedPrefixes[] = { |
161 { "sha256", HashAlgorithmSha256 }, | 173 { "sha256", HashAlgorithmSha256 }, |
162 { "sha384", HashAlgorithmSha384 }, | 174 { "sha384", HashAlgorithmSha384 }, |
163 { "sha512", HashAlgorithmSha512 } | 175 { "sha512", HashAlgorithmSha512 } |
164 }; | 176 }; |
165 Vector<UChar> characters; | |
166 attribute.stripWhiteSpace().appendTo(characters); | |
167 UChar* begin = characters.data(); | |
168 UChar* end = characters.end(); | |
169 | 177 |
170 if (characters.size() < 1) | 178 for (auto& prefix : kSupportedPrefixes) { |
171 return false; | 179 if (skipToken<UChar>(position, end, prefix.prefix)) { |
172 | 180 algorithm = prefix.algorithm; |
173 if (!equalIgnoringCase(integrityPrefix.characters8(), begin, integrityPrefix .length())) | 181 return true; |
174 return false; | |
175 | |
176 const UChar* algorithmStart = begin + integrityPrefix.length(); | |
177 const UChar* algorithmEnd = algorithmStart; | |
178 | |
179 skipUntil<UChar>(algorithmEnd, end, ';'); | |
180 | |
181 // Instead of this sizeof() calculation to get the length of this array, | |
182 // it would be preferable to use WTF_ARRAY_LENGTH for simplicity and to | |
183 // guarantee a compile time calculation. Unfortunately, on some | |
184 // compliers, the call to WTF_ARRAY_LENGTH fails on arrays of anonymous | |
185 // stucts, so, for now, it is necessary to resort to this sizeof | |
186 // calculation. | |
187 size_t i = 0; | |
188 size_t kSupportedPrefixesLength = sizeof(kSupportedPrefixes) / sizeof(kSuppo rtedPrefixes[0]); | |
189 for (; i < kSupportedPrefixesLength; i++) { | |
190 if (equalIgnoringCase(kSupportedPrefixes[i].prefix, algorithmStart, strl en(kSupportedPrefixes[i].prefix))) { | |
191 algorithm = kSupportedPrefixes[i].algorithm; | |
192 break; | |
193 } | 182 } |
194 } | 183 } |
195 | 184 |
196 if (i == kSupportedPrefixesLength) | 185 return false; |
186 } | |
187 | |
188 // Before: | |
189 // | |
190 // ni:///[algorithm];[hash] OR ni:///[algorithm];[hash]?[params] | |
jww
2014/10/15 23:26:04
Shouldn't the carrets here be pointing at the star
Mike West
2014/10/16 06:17:21
Yup. Typo.
| |
191 // ^ ^ ^ ^ | |
192 // position end position end | |
193 // | |
194 // After: | |
195 // | |
196 // ni:///[algorithm];[hash] OR ni:///[algorithm];[hash]?[params] | |
197 // ^ ^ ^ | |
198 // position/end position end | |
199 bool SubresourceIntegrity::parseDigest(const UChar*& position, const UChar* end, String& digest) | |
200 { | |
201 const UChar* begin = position; | |
202 skipWhile<UChar, isIntegrityCharacter>(position, end); | |
203 | |
204 if (position == begin || (position != end && *position != '?')) { | |
205 digest = emptyString(); | |
197 return false; | 206 return false; |
207 } | |
198 | 208 |
199 const UChar* integrityStart = algorithmEnd; | 209 digest = String(begin, position - begin); |
200 if (!skipExactly<UChar>(integrityStart, end, ';')) | |
201 return false; | |
202 | |
203 const UChar* integrityEnd = integrityStart; | |
204 skipWhile<UChar, isIntegrityCharacter>(integrityEnd, end); | |
205 if (integrityEnd != end) | |
206 return false; | |
207 | |
208 integrity = String(integrityStart, integrityEnd - integrityStart); | |
209 return true; | 210 return true; |
210 } | 211 } |
211 | 212 |
213 bool SubresourceIntegrity::parseIntegrityAttribute(const String& attribute, Stri ng& digest, HashAlgorithm& algorithm, Document& document) | |
214 { | |
215 Vector<UChar> characters; | |
216 attribute.stripWhiteSpace().appendTo(characters); | |
217 const UChar* position = characters.data(); | |
218 const UChar* end = characters.end(); | |
219 | |
220 if (!skipToken<UChar>(position, end, "ni:///")) { | |
221 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + "'). The value must position with 'ni:///'.", document); | |
jww
2014/10/15 23:26:03
s/position/begin
Mike West
2014/10/16 06:17:21
Amusing s/begin/position/g error. :)
| |
222 return false; | |
223 } | |
224 | |
225 if (!parseAlgorithm(position, end, algorithm)) { | |
226 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + "'). The specified hash algorithm must be one of 'sha256', 'sha384', or 'sha512 '.", document); | |
227 return false; | |
228 } | |
229 | |
230 if (!skipExactly<UChar>(position, end, ';')) { | |
231 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + "'). The hash algorithm must be followed by a ';' character.", document); | |
232 return false; | |
233 } | |
234 | |
235 if (!parseDigest(position, end, digest)) { | |
236 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + "'). The digest must be a valid, base64-encoded value.", document); | |
237 return false; | |
238 } | |
239 | |
240 // FIXME: Parse params in order to get content type (e.g. "?ct=application/j avascript") | |
241 | |
242 return true; | |
243 } | |
244 | |
212 } // namespace blink | 245 } // namespace blink |
OLD | NEW |