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

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

Issue 596043003: Basic console error messages for subresource integrity. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Updated some of the FIXME comments Created 6 years, 3 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
« no previous file with comments | « Source/core/dom/ScriptLoader.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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"
11 #include "core/frame/ConsoleTypes.h"
11 #include "core/frame/UseCounter.h" 12 #include "core/frame/UseCounter.h"
13 #include "core/inspector/ConsoleMessage.h"
12 #include "platform/Crypto.h" 14 #include "platform/Crypto.h"
13 #include "platform/ParsingUtilities.h" 15 #include "platform/ParsingUtilities.h"
14 #include "platform/RuntimeEnabledFeatures.h" 16 #include "platform/RuntimeEnabledFeatures.h"
15 #include "platform/weborigin/KURL.h" 17 #include "platform/weborigin/KURL.h"
16 #include "platform/weborigin/SecurityOrigin.h" 18 #include "platform/weborigin/SecurityOrigin.h"
17 #include "public/platform/WebCrypto.h" 19 #include "public/platform/WebCrypto.h"
18 #include "public/platform/WebCryptoAlgorithm.h" 20 #include "public/platform/WebCryptoAlgorithm.h"
19 #include "wtf/ASCIICType.h" 21 #include "wtf/ASCIICType.h"
20 #include "wtf/text/Base64.h" 22 #include "wtf/text/Base64.h"
21 #include "wtf/text/StringUTF8Adaptor.h" 23 #include "wtf/text/StringUTF8Adaptor.h"
22 #include "wtf/text/WTFString.h" 24 #include "wtf/text/WTFString.h"
23 25
24 namespace blink { 26 namespace blink {
25 27
26 // FIXME: This should probably use common functions with ContentSecurityPolicy. 28 // FIXME: This should probably use common functions with ContentSecurityPolicy.
27 static bool isIntegrityCharacter(UChar c) 29 static bool isIntegrityCharacter(UChar c)
28 { 30 {
29 // Check if it's a base64 encoded value. 31 // Check if it's a base64 encoded value.
30 return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '='; 32 return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '=';
31 } 33 }
32 34
35 static void logErrorToConsole(const String& message, Document& document)
36 {
37 document.addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, Err orMessageLevel, message));
38 }
39
33 static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2) 40 static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2)
34 { 41 {
35 if (digest1.size() != digest2.size()) 42 if (digest1.size() != digest2.size())
36 return false; 43 return false;
37 44
38 for (size_t i = 0; i < digest1.size(); i++) { 45 for (size_t i = 0; i < digest1.size(); i++) {
39 if (digest1[i] != digest2[i]) 46 if (digest1[i] != digest2[i])
40 return false; 47 return false;
41 } 48 }
42 49
43 return true; 50 return true;
44 } 51 }
45 52
46 // FIXME: If CheckSubresourceIntegrity fails, Blink should create a console 53 static String algorithmToString(HashAlgorithm algorithm)
47 // message to alert the developer of the failure. 54 {
55 static const struct {
56 HashAlgorithm algorithm;
57 const char* name;
58 } kAlgorithmToString[] = {
59 { HashAlgorithmSha256, "SHA-256" },
60 { HashAlgorithmSha384, "SHA-384" },
61 { HashAlgorithmSha512, "SHA-512" }
62 };
63
64 // See comment in parseIntegrityAttribute about why sizeof() is used
65 // instead of WTF_ARRAY_LENGTH.
66 size_t i = 0;
67 size_t kSupportedAlgorithmsLength = sizeof(kAlgorithmToString) / sizeof(kAlg orithmToString[0]);
68 for (; i < kSupportedAlgorithmsLength; i++) {
69 if (kAlgorithmToString[i].algorithm == algorithm)
70 return kAlgorithmToString[i].name;
71 }
72
73 ASSERT_NOT_REACHED();
74 return String();
75 }
76
77 static String digestToString(const DigestValue& digest)
78 {
79 return base64Encode(reinterpret_cast<const char*>(digest.data()), digest.siz e(), Base64DoNotInsertLFs);
80 }
81
48 bool SubresourceIntegrity::CheckSubresourceIntegrity(const Element& element, con st String& source, const KURL& resourceUrl) 82 bool SubresourceIntegrity::CheckSubresourceIntegrity(const Element& element, con st String& source, const KURL& resourceUrl)
49 { 83 {
50 if (!RuntimeEnabledFeatures::subresourceIntegrityEnabled()) 84 if (!RuntimeEnabledFeatures::subresourceIntegrityEnabled())
51 return true; 85 return true;
52 86
53 if (!element.fastHasAttribute(HTMLNames::integrityAttr)) 87 if (!element.fastHasAttribute(HTMLNames::integrityAttr))
54 return true; 88 return true;
55 89
56 // FIXME: If insecureOriginMsg is not empty after the check, Blink 90 Document& document = element.document();
57 // should send a console message. 91
58 //
59 // Instead of just checking SecurityOrigin::isSecure on resourceUrl, this 92 // Instead of just checking SecurityOrigin::isSecure on resourceUrl, this
60 // checks canAccessFeatureRequiringSecureOrigin so that file:// protocols 93 // checks canAccessFeatureRequiringSecureOrigin so that file:// protocols
61 // and localhost resources can be allowed. These may be useful for testing 94 // and localhost resources can be allowed. These may be useful for testing
62 // and are allowed for features requiring authenticated origins, so Chrome 95 // and are allowed for features requiring authenticated origins, so Chrome
63 // allows them here. 96 // allows them here.
64 String insecureOriginMsg = ""; 97 String insecureOriginMsg = "";
65 RefPtr<SecurityOrigin> resourceSecurityOrigin = SecurityOrigin::create(resou rceUrl); 98 RefPtr<SecurityOrigin> resourceSecurityOrigin = SecurityOrigin::create(resou rceUrl);
66 if (!element.document().securityOrigin()->canAccessFeatureRequiringSecureOri gin(insecureOriginMsg)) { 99 if (!document.securityOrigin()->canAccessFeatureRequiringSecureOrigin(insecu reOriginMsg)) {
67 UseCounter::count(element.document(), UseCounter::SRIElementWithIntegrit yAttributeAndInsecureOrigin); 100 UseCounter::count(document, UseCounter::SRIElementWithIntegrityAttribute AndInsecureOrigin);
101 // FIXME: This console message should probably utilize
102 // inesecureOriginMsg to give a more helpful message to the user.
103 logErrorToConsole("The 'integrity' attribute may only be used in documen ts in secure origins.", document);
68 return false; 104 return false;
69 } 105 }
70 if (!resourceSecurityOrigin->canAccessFeatureRequiringSecureOrigin(insecureO riginMsg)) { 106 if (!resourceSecurityOrigin->canAccessFeatureRequiringSecureOrigin(insecureO riginMsg)) {
71 UseCounter::count(element.document(), UseCounter::SRIElementWithIntegrit yAttributeAndInsecureResource); 107 UseCounter::count(document, UseCounter::SRIElementWithIntegrityAttribute AndInsecureResource);
108 logErrorToConsole("The 'integrity' attribute may only be used with resou rces on secure origins.", document);
72 return false; 109 return false;
73 } 110 }
74 111
75 String integrity; 112 String integrity;
76 HashAlgorithm algorithm; 113 HashAlgorithm algorithm;
77 if (!parseIntegrityAttribute(element.fastGetAttribute(HTMLNames::integrityAt tr), integrity, algorithm)) { 114 String attribute = element.fastGetAttribute(HTMLNames::integrityAttr);
78 UseCounter::count(element.document(), UseCounter::SRIElementWithUnparsab leIntegrityAttribute); 115 if (!parseIntegrityAttribute(attribute, integrity, algorithm)) {
116 UseCounter::count(document, UseCounter::SRIElementWithUnparsableIntegrit yAttribute);
117 logErrorToConsole("The 'integrity' attribute '" + attribute + "' does no t parse as a valid integrity.", document);
Mike West 2014/09/24 08:40:27 How about "The 'integrity' attribute's value '" +
jww 2014/09/30 01:38:50 Yeah, first part done, and I figure we can do more
79 return false; 118 return false;
80 } 119 }
81 120
82 Vector<char> hashVector; 121 Vector<char> hashVector;
83 base64Decode(integrity, hashVector); 122 base64Decode(integrity, hashVector);
84 123
85 StringUTF8Adaptor normalizedSource(source, StringUTF8Adaptor::Normalize, WTF ::EntitiesForUnencodables); 124 StringUTF8Adaptor normalizedSource(source, StringUTF8Adaptor::Normalize, WTF ::EntitiesForUnencodables);
86 125
87 DigestValue digest; 126 DigestValue digest;
88 bool digestSuccess = computeDigest(algorithm, normalizedSource.data(), norma lizedSource.length(), digest); 127 bool digestSuccess = computeDigest(algorithm, normalizedSource.data(), norma lizedSource.length(), digest);
89 128
90 if (digestSuccess) { 129 if (digestSuccess) {
91 DigestValue convertedHashVector; 130 DigestValue convertedHashVector;
92 convertedHashVector.append(reinterpret_cast<uint8_t*>(hashVector.data()) , hashVector.size()); 131 convertedHashVector.append(reinterpret_cast<uint8_t*>(hashVector.data()) , hashVector.size());
93 if (DigestsEqual(digest, convertedHashVector)) { 132 if (DigestsEqual(digest, convertedHashVector)) {
94 UseCounter::count(element.document(), UseCounter::SRIElementWithMatc hingIntegrityAttribute); 133 UseCounter::count(document, UseCounter::SRIElementWithMatchingIntegr ityAttribute);
95 return true; 134 return true;
135 } else {
136 logErrorToConsole("The computed " + algorithmToString(algorithm) + " integrity '" + digestToString(digest) + "' does not match the 'integrity' attri bute '" + integrity + "'.", document);
Mike West 2014/09/24 08:40:28 This message exposes the digest of the resource. A
jww 2014/09/30 01:38:50 Done.
96 } 137 }
138 } else {
139 logErrorToConsole("There was an error computing an 'integrity' value for a resource.", document);
Mike West 2014/09/24 08:40:27 Perhaps you could pass a KURL into this method to
jww 2014/09/30 01:38:49 Done.
97 } 140 }
98 141
99 UseCounter::count(element.document(), UseCounter::SRIElementWithNonMatchingI ntegrityAttribute); 142 UseCounter::count(document, UseCounter::SRIElementWithNonMatchingIntegrityAt tribute);
100 return false; 143 return false;
101 } 144 }
102 145
103 bool SubresourceIntegrity::parseIntegrityAttribute(const String& attribute, Stri ng& integrity, HashAlgorithm& algorithm) 146 bool SubresourceIntegrity::parseIntegrityAttribute(const String& attribute, Stri ng& integrity, HashAlgorithm& algorithm)
104 { 147 {
105 DEFINE_STATIC_LOCAL(const String, integrityPrefix, ("ni://")); 148 DEFINE_STATIC_LOCAL(const String, integrityPrefix, ("ni://"));
106 // Any additions or subtractions from this struct should also modify the 149 // Any additions or subtractions from this struct should also modify the
107 // respective entries in the kAlgorithmMap array in checkDigest(). 150 // respective entries in the kAlgorithmMap array in checkDigest().
Mike West 2014/09/24 08:40:28 // as well as the array in algorithmToString().
jww 2014/09/30 01:38:50 Done.
108 static const struct { 151 static const struct {
109 const char* prefix; 152 const char* prefix;
110 HashAlgorithm algorithm; 153 HashAlgorithm algorithm;
111 } kSupportedPrefixes[] = { 154 } kSupportedPrefixes[] = {
112 { "sha256", HashAlgorithmSha256 }, 155 { "sha256", HashAlgorithmSha256 },
113 { "sha384", HashAlgorithmSha384 }, 156 { "sha384", HashAlgorithmSha384 },
114 { "sha512", HashAlgorithmSha512 } 157 { "sha512", HashAlgorithmSha512 }
115 }; 158 };
116 Vector<UChar> characters; 159 Vector<UChar> characters;
117 attribute.stripWhiteSpace().appendTo(characters); 160 attribute.stripWhiteSpace().appendTo(characters);
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 const UChar* integrityEnd = integrityStart; 197 const UChar* integrityEnd = integrityStart;
155 skipWhile<UChar, isIntegrityCharacter>(integrityEnd, end); 198 skipWhile<UChar, isIntegrityCharacter>(integrityEnd, end);
156 if (integrityEnd != end) 199 if (integrityEnd != end)
157 return false; 200 return false;
158 201
159 integrity = String(integrityStart, integrityEnd - integrityStart); 202 integrity = String(integrityStart, integrityEnd - integrityStart);
160 return true; 203 return true;
161 } 204 }
162 205
163 } // namespace blink 206 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/dom/ScriptLoader.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698