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

Side by Side Diff: Source/core/frame/SubresourceIntegrity.cpp

Issue 656063002: Subresource Integrity: Improve parsing. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Feedback. Created 6 years, 2 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
OLDNEW
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
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
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
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]
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 begin with 'ni:///'.", document);
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
OLDNEW
« no previous file with comments | « Source/core/frame/SubresourceIntegrity.h ('k') | Source/core/frame/SubresourceIntegrityTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698