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

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

Issue 2614813004: Fix a crash of SRI when used with web workers (Closed)
Patch Set: fix Created 3 years, 11 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 "core/frame/SubresourceIntegrity.h" 5 #include "core/frame/SubresourceIntegrity.h"
6 6
7 #include "core/HTMLNames.h" 7 #include "core/HTMLNames.h"
8 #include "core/dom/Document.h" 8 #include "core/dom/Document.h"
9 #include "core/dom/Element.h" 9 #include "core/dom/Element.h"
10 #include "core/dom/ExecutionContext.h"
10 #include "core/fetch/Resource.h" 11 #include "core/fetch/Resource.h"
11 #include "core/frame/UseCounter.h" 12 #include "core/frame/UseCounter.h"
12 #include "core/inspector/ConsoleMessage.h" 13 #include "core/inspector/ConsoleMessage.h"
13 #include "platform/Crypto.h" 14 #include "platform/Crypto.h"
14 #include "platform/weborigin/KURL.h" 15 #include "platform/weborigin/KURL.h"
15 #include "platform/weborigin/SecurityOrigin.h" 16 #include "platform/weborigin/SecurityOrigin.h"
16 #include "public/platform/WebCrypto.h" 17 #include "public/platform/WebCrypto.h"
17 #include "public/platform/WebCryptoAlgorithm.h" 18 #include "public/platform/WebCryptoAlgorithm.h"
18 #include "wtf/ASCIICType.h" 19 #include "wtf/ASCIICType.h"
19 #include "wtf/Vector.h" 20 #include "wtf/Vector.h"
(...skipping 11 matching lines...) Expand all
31 // not much risk in it, and it'll make it simpler for developers. 32 // not much risk in it, and it'll make it simpler for developers.
32 return isASCIIAlphanumeric(c) || c == '_' || c == '-' || c == '+' || 33 return isASCIIAlphanumeric(c) || c == '_' || c == '-' || c == '+' ||
33 c == '/' || c == '='; 34 c == '/' || c == '=';
34 } 35 }
35 36
36 static bool isValueCharacter(UChar c) { 37 static bool isValueCharacter(UChar c) {
37 // VCHAR per https://tools.ietf.org/html/rfc5234#appendix-B.1 38 // VCHAR per https://tools.ietf.org/html/rfc5234#appendix-B.1
38 return c >= 0x21 && c <= 0x7e; 39 return c >= 0x21 && c <= 0x7e;
39 } 40 }
40 41
41 static void logErrorToConsole(const String& message, Document& document) { 42 static void logErrorToConsole(const String& message,
42 document.addConsoleMessage(ConsoleMessage::create( 43 ExecutionContext& executionContext) {
44 executionContext.addConsoleMessage(ConsoleMessage::create(
43 SecurityMessageSource, ErrorMessageLevel, message)); 45 SecurityMessageSource, ErrorMessageLevel, message));
44 } 46 }
45 47
46 static bool DigestsEqual(const DigestValue& digest1, 48 static bool DigestsEqual(const DigestValue& digest1,
47 const DigestValue& digest2) { 49 const DigestValue& digest2) {
48 if (digest1.size() != digest2.size()) 50 if (digest1.size() != digest2.size())
49 return false; 51 return false;
50 52
51 for (size_t i = 0; i < digest1.size(); i++) { 53 for (size_t i = 0; i < digest1.size(); i++) {
52 if (digest1[i] != digest2[i]) 54 if (digest1[i] != digest2[i])
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
149 if (!result) 151 if (!result)
150 logErrorToConsole(errorMessage, document); 152 logErrorToConsole(errorMessage, document);
151 return result; 153 return result;
152 } 154 }
153 155
154 bool SubresourceIntegrity::CheckSubresourceIntegrity( 156 bool SubresourceIntegrity::CheckSubresourceIntegrity(
155 const String& integrityMetadata, 157 const String& integrityMetadata,
156 const char* content, 158 const char* content,
157 size_t size, 159 size_t size,
158 const KURL& resourceUrl, 160 const KURL& resourceUrl,
159 Document& document, 161 ExecutionContext& executionContext,
160 String& errorMessage) { 162 String& errorMessage) {
161 IntegrityMetadataSet metadataSet; 163 IntegrityMetadataSet metadataSet;
162 IntegrityParseResult integrityParseResult = 164 IntegrityParseResult integrityParseResult = parseIntegrityAttribute(
163 parseIntegrityAttribute(integrityMetadata, metadataSet, &document); 165 integrityMetadata, metadataSet, &executionContext);
164 // On failed parsing, there's no need to log an error here, as 166 // On failed parsing, there's no need to log an error here, as
165 // parseIntegrityAttribute() will output an appropriate console message. 167 // parseIntegrityAttribute() will output an appropriate console message.
166 if (integrityParseResult != IntegrityParseValidResult) 168 if (integrityParseResult != IntegrityParseValidResult)
167 return true; 169 return true;
168 170
169 return CheckSubresourceIntegrity(metadataSet, content, size, resourceUrl, 171 return CheckSubresourceIntegrity(metadataSet, content, size, resourceUrl,
170 document, errorMessage); 172 executionContext, errorMessage);
171 } 173 }
172 174
173 bool SubresourceIntegrity::CheckSubresourceIntegrity( 175 bool SubresourceIntegrity::CheckSubresourceIntegrity(
174 const IntegrityMetadataSet& metadataSet, 176 const IntegrityMetadataSet& metadataSet,
175 const char* content, 177 const char* content,
176 size_t size, 178 size_t size,
177 const KURL& resourceUrl, 179 const KURL& resourceUrl,
178 Document& document, 180 ExecutionContext& executionContext,
179 String& errorMessage) { 181 String& errorMessage) {
180 if (!metadataSet.size()) 182 if (!metadataSet.size())
181 return true; 183 return true;
182 184
183 HashAlgorithm strongestAlgorithm = HashAlgorithmSha256; 185 HashAlgorithm strongestAlgorithm = HashAlgorithmSha256;
184 for (const IntegrityMetadata& metadata : metadataSet) 186 for (const IntegrityMetadata& metadata : metadataSet)
185 strongestAlgorithm = 187 strongestAlgorithm =
186 getPrioritizedHashFunction(metadata.algorithm(), strongestAlgorithm); 188 getPrioritizedHashFunction(metadata.algorithm(), strongestAlgorithm);
187 189
188 DigestValue digest; 190 DigestValue digest;
189 for (const IntegrityMetadata& metadata : metadataSet) { 191 for (const IntegrityMetadata& metadata : metadataSet) {
190 if (metadata.algorithm() != strongestAlgorithm) 192 if (metadata.algorithm() != strongestAlgorithm)
191 continue; 193 continue;
192 194
193 digest.clear(); 195 digest.clear();
194 bool digestSuccess = 196 bool digestSuccess =
195 computeDigest(metadata.algorithm(), content, size, digest); 197 computeDigest(metadata.algorithm(), content, size, digest);
196 198
197 if (digestSuccess) { 199 if (digestSuccess) {
198 Vector<char> hashVector; 200 Vector<char> hashVector;
199 base64Decode(metadata.digest(), hashVector); 201 base64Decode(metadata.digest(), hashVector);
200 DigestValue convertedHashVector; 202 DigestValue convertedHashVector;
201 convertedHashVector.append(reinterpret_cast<uint8_t*>(hashVector.data()), 203 convertedHashVector.append(reinterpret_cast<uint8_t*>(hashVector.data()),
202 hashVector.size()); 204 hashVector.size());
203 205
204 if (DigestsEqual(digest, convertedHashVector)) { 206 if (DigestsEqual(digest, convertedHashVector)) {
205 UseCounter::count(document, 207 UseCounter::count(&executionContext,
206 UseCounter::SRIElementWithMatchingIntegrityAttribute); 208 UseCounter::SRIElementWithMatchingIntegrityAttribute);
207 return true; 209 return true;
208 } 210 }
209 } 211 }
210 } 212 }
211 213
212 digest.clear(); 214 digest.clear();
213 if (computeDigest(HashAlgorithmSha256, content, size, digest)) { 215 if (computeDigest(HashAlgorithmSha256, content, size, digest)) {
214 // This message exposes the digest of the resource to the console. 216 // This message exposes the digest of the resource to the console.
215 // Because this is only to the console, that's okay for now, but we 217 // Because this is only to the console, that's okay for now, but we
216 // need to be very careful not to expose this in exceptions or 218 // need to be very careful not to expose this in exceptions or
217 // JavaScript, otherwise it risks exposing information about the 219 // JavaScript, otherwise it risks exposing information about the
218 // resource cross-origin. 220 // resource cross-origin.
219 errorMessage = 221 errorMessage =
220 "Failed to find a valid digest in the 'integrity' attribute for " 222 "Failed to find a valid digest in the 'integrity' attribute for "
221 "resource '" + 223 "resource '" +
222 resourceUrl.elidedString() + "' with computed SHA-256 integrity '" + 224 resourceUrl.elidedString() + "' with computed SHA-256 integrity '" +
223 digestToString(digest) + "'. The resource has been blocked."; 225 digestToString(digest) + "'. The resource has been blocked.";
224 } else { 226 } else {
225 errorMessage = 227 errorMessage =
226 "There was an error computing an integrity value for resource '" + 228 "There was an error computing an integrity value for resource '" +
227 resourceUrl.elidedString() + "'. The resource has been blocked."; 229 resourceUrl.elidedString() + "'. The resource has been blocked.";
228 } 230 }
229 UseCounter::count(document, 231 UseCounter::count(&executionContext,
230 UseCounter::SRIElementWithNonMatchingIntegrityAttribute); 232 UseCounter::SRIElementWithNonMatchingIntegrityAttribute);
231 return false; 233 return false;
232 } 234 }
233 235
234 // Before: 236 // Before:
235 // 237 //
236 // [algorithm]-[hash] 238 // [algorithm]-[hash]
237 // ^ ^ 239 // ^ ^
238 // position end 240 // position end
239 // 241 //
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 } 312 }
311 313
312 SubresourceIntegrity::IntegrityParseResult 314 SubresourceIntegrity::IntegrityParseResult
313 SubresourceIntegrity::parseIntegrityAttribute( 315 SubresourceIntegrity::parseIntegrityAttribute(
314 const WTF::String& attribute, 316 const WTF::String& attribute,
315 IntegrityMetadataSet& metadataSet) { 317 IntegrityMetadataSet& metadataSet) {
316 return parseIntegrityAttribute(attribute, metadataSet, nullptr); 318 return parseIntegrityAttribute(attribute, metadataSet, nullptr);
317 } 319 }
318 320
319 SubresourceIntegrity::IntegrityParseResult 321 SubresourceIntegrity::IntegrityParseResult
320 SubresourceIntegrity::parseIntegrityAttribute(const WTF::String& attribute, 322 SubresourceIntegrity::parseIntegrityAttribute(
321 IntegrityMetadataSet& metadataSet, 323 const WTF::String& attribute,
322 Document* document) { 324 IntegrityMetadataSet& metadataSet,
325 ExecutionContext* executionContext) {
323 Vector<UChar> characters; 326 Vector<UChar> characters;
324 attribute.stripWhiteSpace().appendTo(characters); 327 attribute.stripWhiteSpace().appendTo(characters);
325 const UChar* position = characters.data(); 328 const UChar* position = characters.data();
326 const UChar* end = characters.end(); 329 const UChar* end = characters.end();
327 const UChar* currentIntegrityEnd; 330 const UChar* currentIntegrityEnd;
328 331
329 metadataSet.clear(); 332 metadataSet.clear();
330 bool error = false; 333 bool error = false;
331 334
332 // The integrity attribute takes the form: 335 // The integrity attribute takes the form:
(...skipping 11 matching lines...) Expand all
344 // Algorithm parsing errors are non-fatal (the subresource should 347 // Algorithm parsing errors are non-fatal (the subresource should
345 // still be loaded) because strong hash algorithms should be used 348 // still be loaded) because strong hash algorithms should be used
346 // without fear of breaking older user agents that don't support 349 // without fear of breaking older user agents that don't support
347 // them. 350 // them.
348 AlgorithmParseResult parseResult = 351 AlgorithmParseResult parseResult =
349 parseAlgorithm(position, currentIntegrityEnd, algorithm); 352 parseAlgorithm(position, currentIntegrityEnd, algorithm);
350 if (parseResult == AlgorithmUnknown) { 353 if (parseResult == AlgorithmUnknown) {
351 // Unknown hash algorithms are treated as if they're not present, 354 // Unknown hash algorithms are treated as if they're not present,
352 // and thus are not marked as an error, they're just skipped. 355 // and thus are not marked as an error, they're just skipped.
353 skipUntil<UChar, isASCIISpace>(position, end); 356 skipUntil<UChar, isASCIISpace>(position, end);
354 if (document) { 357 if (executionContext) {
355 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + 358 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute +
356 "'). The specified hash algorithm must be one of " 359 "'). The specified hash algorithm must be one of "
357 "'sha256', 'sha384', or 'sha512'.", 360 "'sha256', 'sha384', or 'sha512'.",
358 *document); 361 *executionContext);
359 UseCounter::count( 362 UseCounter::count(
360 *document, UseCounter::SRIElementWithUnparsableIntegrityAttribute); 363 executionContext,
364 UseCounter::SRIElementWithUnparsableIntegrityAttribute);
361 } 365 }
362 continue; 366 continue;
363 } 367 }
364 368
365 if (parseResult == AlgorithmUnparsable) { 369 if (parseResult == AlgorithmUnparsable) {
366 error = true; 370 error = true;
367 skipUntil<UChar, isASCIISpace>(position, end); 371 skipUntil<UChar, isASCIISpace>(position, end);
368 if (document) { 372 if (executionContext) {
369 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + 373 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute +
370 "'). The hash algorithm must be one of 'sha256', " 374 "'). The hash algorithm must be one of 'sha256', "
371 "'sha384', or 'sha512', followed by a '-' " 375 "'sha384', or 'sha512', followed by a '-' "
372 "character.", 376 "character.",
373 *document); 377 *executionContext);
374 UseCounter::count( 378 UseCounter::count(
375 *document, UseCounter::SRIElementWithUnparsableIntegrityAttribute); 379 executionContext,
380 UseCounter::SRIElementWithUnparsableIntegrityAttribute);
376 } 381 }
377 continue; 382 continue;
378 } 383 }
379 384
380 ASSERT(parseResult == AlgorithmValid); 385 ASSERT(parseResult == AlgorithmValid);
381 386
382 if (!parseDigest(position, currentIntegrityEnd, digest)) { 387 if (!parseDigest(position, currentIntegrityEnd, digest)) {
383 error = true; 388 error = true;
384 skipUntil<UChar, isASCIISpace>(position, end); 389 skipUntil<UChar, isASCIISpace>(position, end);
385 if (document) { 390 if (executionContext) {
386 logErrorToConsole( 391 logErrorToConsole(
387 "Error parsing 'integrity' attribute ('" + attribute + 392 "Error parsing 'integrity' attribute ('" + attribute +
388 "'). The digest must be a valid, base64-encoded value.", 393 "'). The digest must be a valid, base64-encoded value.",
389 *document); 394 *executionContext);
390 UseCounter::count( 395 UseCounter::count(
391 *document, UseCounter::SRIElementWithUnparsableIntegrityAttribute); 396 executionContext,
397 UseCounter::SRIElementWithUnparsableIntegrityAttribute);
392 } 398 }
393 continue; 399 continue;
394 } 400 }
395 401
396 // The spec defines a space in the syntax for options, separated by a 402 // The spec defines a space in the syntax for options, separated by a
397 // '?' character followed by unbounded VCHARs, but no actual options 403 // '?' character followed by unbounded VCHARs, but no actual options
398 // have been defined yet. Thus, for forward compatibility, ignore any 404 // have been defined yet. Thus, for forward compatibility, ignore any
399 // options specified. 405 // options specified.
400 if (skipExactly<UChar>(position, end, '?')) { 406 if (skipExactly<UChar>(position, end, '?')) {
401 const UChar* begin = position; 407 const UChar* begin = position;
402 skipWhile<UChar, isValueCharacter>(position, end); 408 skipWhile<UChar, isValueCharacter>(position, end);
403 if (begin != position && document) 409 if (begin != position && executionContext) {
404 logErrorToConsole( 410 logErrorToConsole(
405 "Ignoring unrecogized 'integrity' attribute option '" + 411 "Ignoring unrecogized 'integrity' attribute option '" +
406 String(begin, position - begin) + "'.", 412 String(begin, position - begin) + "'.",
407 *document); 413 *executionContext);
414 }
408 } 415 }
409 416
410 IntegrityMetadata integrityMetadata(digest, algorithm); 417 IntegrityMetadata integrityMetadata(digest, algorithm);
411 metadataSet.add(integrityMetadata.toPair()); 418 metadataSet.add(integrityMetadata.toPair());
412 } 419 }
413 420
414 if (metadataSet.size() == 0 && error) 421 if (metadataSet.size() == 0 && error)
415 return IntegrityParseNoValidResult; 422 return IntegrityParseNoValidResult;
416 423
417 return IntegrityParseValidResult; 424 return IntegrityParseValidResult;
418 } 425 }
419 426
420 } // namespace blink 427 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/frame/SubresourceIntegrity.h ('k') | third_party/WebKit/Source/modules/fetch/FetchManager.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698