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 // FIXME: This should probably use common functions with ContentSecurityPolicy. 27 static bool isIntegrityCharacter(UChar c) 28 { 29 // Check if it's a base64 encoded value. 30 return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '='; 31 } 32 33 static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2) 34 { 35 if (digest1.size() != digest2.size()) 36 return false; 37 38 for (size_t i = 0; i < digest1.size(); i++) { 39 if (digest1[i] != digest2[i]) 40 return false; 41 } 42 43 return true; 44 } 45 46 // FIXME: If CheckSubresourceIntegrity fails, Blink should create a console 47 // message to alert the developer of the failure. 48 bool SubresourceIntegrity::CheckSubresourceIntegrity(const Element& element, const String& source, const KURL& resourceUrl) 49 { 50 if (!RuntimeEnabledFeatures::subresourceIntegrityEnabled()) 51 return true; 52 53 if (!element.fastHasAttribute(HTMLNames::integrityAttr)) 54 return true; 55 56 // FIXME: If insecureOriginMsg is not empty after the check, Blink 57 // should send a console message. 58 // 59 // Instead of just checking SecurityOrigin::isSecure on resourceUrl, this 60 // checks canAccessFeatureRequiringSecureOrigin so that file:// protocols 61 // and localhost resources can be allowed. These may be useful for testing 62 // and are allowed for features requiring authenticated origins, so Chrome 63 // allows them here. 64 String insecureOriginMsg = ""; 65 RefPtr<SecurityOrigin> resourceSecurityOrigin = SecurityOrigin::create(resourceUrl); 66 if (!element.document().securityOrigin()->canAccessFeatureRequiringSecureOrigin(insecureOriginMsg)) { 67 UseCounter::count(element.document(), UseCounter::SRIElementWithIntegrityAttributeAndInsecureOrigin); 68 return false; 69 } 70 if (!resourceSecurityOrigin->canAccessFeatureRequiringSecureOrigin(insecureOriginMsg)) { 71 UseCounter::count(element.document(), UseCounter::SRIElementWithIntegrityAttributeAndInsecureResource); 72 return false; 73 } 74 75 String integrity; 76 HashAlgorithm algorithm; 77 if (!parseIntegrityAttribute(element.fastGetAttribute(HTMLNames::integrityAttr), integrity, algorithm)) { 78 UseCounter::count(element.document(), UseCounter::SRIElementWithUnparsableIntegrityAttribute); 79 return false; 80 } 81 82 Vector<char> hashVector; 83 base64Decode(integrity, hashVector); 84 85 StringUTF8Adaptor normalizedSource(source, StringUTF8Adaptor::Normalize, WTF::EntitiesForUnencodables); 86 87 DigestValue digest; 88 bool digestSuccess = computeDigest(algorithm, normalizedSource.data(), normalizedSource.length(), digest); 89 90 if (digestSuccess) { 91 DigestValue convertedHashVector; 92 convertedHashVector.append(reinterpret_cast<uint8_t*>(hashVector.data()), hashVector.size()); 93 if (DigestsEqual(digest, convertedHashVector)) { 94 UseCounter::count(element.document(), UseCounter::SRIElementWithMatchingIntegrityAttribute); 95 return true; 96 } 97 } 98 99 UseCounter::count(element.document(), UseCounter::SRIElementWithNonMatchingIntegrityAttribute); 100 return false; 101 } 102 103 bool SubresourceIntegrity::parseIntegrityAttribute(const String& attribute, String& integrity, HashAlgorithm& algorithm) 104 { 105 DEFINE_STATIC_LOCAL(const String, integrityPrefix, ("ni://")); 106 // Any additions or subtractions from this struct should also modify the 107 // respective entries in the kAlgorithmMap array in checkDigest(). 108 static const struct { 109 const char* prefix; 110 HashAlgorithm algorithm; 111 } kSupportedPrefixes[] = { 112 { "sha256", HashAlgorithmSha256 }, 113 { "sha384", HashAlgorithmSha384 }, 114 { "sha512", HashAlgorithmSha512 } 115 }; 116 Vector<UChar> characters; 117 attribute.stripWhiteSpace().appendTo(characters); 118 UChar* begin = characters.data(); 119 UChar* end = characters.end(); 120 121 if (characters.size() < 1) 122 return false; 123 124 if (!equalIgnoringCase(integrityPrefix.characters8(), begin, integrityPrefix.length())) 125 return false; 126 127 const UChar* algorithmStart = begin + integrityPrefix.length(); 128 const UChar* algorithmEnd = algorithmStart; 129 130 skipUntil<UChar>(algorithmEnd, end, ';'); 131 132 // Instead of this sizeof() calculation to get the length of this array, 133 // it would be preferable to use WTF_ARRAY_LENGTH for simplicity and to 134 // guarantee a compile time calculation. Unfortunately, on some 135 // compliers, the call to WTF_ARRAY_LENGTH fails on arrays of anonymous 136 // stucts, so, for now, it is necessary to resort to this sizeof 137 // calculation. 138 size_t i = 0; 139 size_t kSupportedPrefixesLength = sizeof(kSupportedPrefixes) / sizeof(kSupportedPrefixes[0]); 140 for (; i < kSupportedPrefixesLength; i++) { 141 if (equalIgnoringCase(kSupportedPrefixes[i].prefix, algorithmStart, strlen(kSupportedPrefixes[i].prefix))) { 142 algorithm = kSupportedPrefixes[i].algorithm; 143 break; 144 } 145 } 146 147 if (i == kSupportedPrefixesLength) 148 return false; 149 150 const UChar* integrityStart = algorithmEnd; 151 if (!skipExactly<UChar>(integrityStart, end, ';')) 152 return false; 153 154 const UChar* integrityEnd = integrityStart; 155 skipWhile<UChar, isIntegrityCharacter>(integrityEnd, end); 156 if (integrityEnd != end) 157 return false; 158 159 integrity = String(integrityStart, integrityEnd - integrityStart); 160 return true; 161 } 162 163 } // namespace blink 164