| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2011 Google, Inc. All rights reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions | |
| 6 * are met: | |
| 7 * 1. Redistributions of source code must retain the above copyright | |
| 8 * notice, this list of conditions and the following disclaimer. | |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | |
| 10 * notice, this list of conditions and the following disclaimer in the | |
| 11 * documentation and/or other materials provided with the distribution. | |
| 12 * | |
| 13 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY | |
| 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
| 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 24 */ | |
| 25 | |
| 26 #include "config.h" | |
| 27 #include "core/frame/ContentSecurityPolicy.h" | |
| 28 | |
| 29 #include "RuntimeEnabledFeatures.h" | |
| 30 #include "bindings/v8/ScriptCallStackFactory.h" | |
| 31 #include "bindings/v8/ScriptController.h" | |
| 32 #include "core/dom/DOMStringList.h" | |
| 33 #include "core/dom/Document.h" | |
| 34 #include "core/events/SecurityPolicyViolationEvent.h" | |
| 35 #include "core/frame/DOMWindow.h" | |
| 36 #include "core/frame/LocalFrame.h" | |
| 37 #include "core/frame/UseCounter.h" | |
| 38 #include "core/frame/csp/CSPDirectiveList.h" | |
| 39 #include "core/frame/csp/CSPSource.h" | |
| 40 #include "core/frame/csp/CSPSourceList.h" | |
| 41 #include "core/frame/csp/MediaListDirective.h" | |
| 42 #include "core/frame/csp/SourceListDirective.h" | |
| 43 #include "core/inspector/InspectorInstrumentation.h" | |
| 44 #include "core/inspector/ScriptCallStack.h" | |
| 45 #include "core/loader/DocumentLoader.h" | |
| 46 #include "core/loader/PingLoader.h" | |
| 47 #include "platform/JSONValues.h" | |
| 48 #include "platform/NotImplemented.h" | |
| 49 #include "platform/ParsingUtilities.h" | |
| 50 #include "platform/network/ContentSecurityPolicyParsers.h" | |
| 51 #include "platform/network/ContentSecurityPolicyResponseHeaders.h" | |
| 52 #include "platform/network/FormData.h" | |
| 53 #include "platform/network/ResourceResponse.h" | |
| 54 #include "platform/weborigin/KURL.h" | |
| 55 #include "platform/weborigin/KnownPorts.h" | |
| 56 #include "platform/weborigin/SchemeRegistry.h" | |
| 57 #include "platform/weborigin/SecurityOrigin.h" | |
| 58 #include "public/platform/Platform.h" | |
| 59 #include "public/platform/WebArrayBuffer.h" | |
| 60 #include "public/platform/WebCrypto.h" | |
| 61 #include "public/platform/WebCryptoAlgorithm.h" | |
| 62 #include "wtf/HashMap.h" | |
| 63 #include "wtf/StringHasher.h" | |
| 64 #include "wtf/text/StringBuilder.h" | |
| 65 | |
| 66 namespace WebCore { | |
| 67 | |
| 68 // CSP 1.0 Directives | |
| 69 const char ContentSecurityPolicy::ConnectSrc[] = "connect-src"; | |
| 70 const char ContentSecurityPolicy::DefaultSrc[] = "default-src"; | |
| 71 const char ContentSecurityPolicy::FontSrc[] = "font-src"; | |
| 72 const char ContentSecurityPolicy::FrameSrc[] = "frame-src"; | |
| 73 const char ContentSecurityPolicy::ImgSrc[] = "img-src"; | |
| 74 const char ContentSecurityPolicy::MediaSrc[] = "media-src"; | |
| 75 const char ContentSecurityPolicy::ObjectSrc[] = "object-src"; | |
| 76 const char ContentSecurityPolicy::ReportURI[] = "report-uri"; | |
| 77 const char ContentSecurityPolicy::Sandbox[] = "sandbox"; | |
| 78 const char ContentSecurityPolicy::ScriptSrc[] = "script-src"; | |
| 79 const char ContentSecurityPolicy::StyleSrc[] = "style-src"; | |
| 80 | |
| 81 // CSP 1.1 Directives | |
| 82 const char ContentSecurityPolicy::BaseURI[] = "base-uri"; | |
| 83 const char ContentSecurityPolicy::ChildSrc[] = "child-src"; | |
| 84 const char ContentSecurityPolicy::FormAction[] = "form-action"; | |
| 85 const char ContentSecurityPolicy::FrameAncestors[] = "frame-ancestors"; | |
| 86 const char ContentSecurityPolicy::PluginTypes[] = "plugin-types"; | |
| 87 const char ContentSecurityPolicy::ReflectedXSS[] = "reflected-xss"; | |
| 88 const char ContentSecurityPolicy::Referrer[] = "referrer"; | |
| 89 | |
| 90 bool ContentSecurityPolicy::isDirectiveName(const String& name) | |
| 91 { | |
| 92 return (equalIgnoringCase(name, ConnectSrc) | |
| 93 || equalIgnoringCase(name, DefaultSrc) | |
| 94 || equalIgnoringCase(name, FontSrc) | |
| 95 || equalIgnoringCase(name, FrameSrc) | |
| 96 || equalIgnoringCase(name, ImgSrc) | |
| 97 || equalIgnoringCase(name, MediaSrc) | |
| 98 || equalIgnoringCase(name, ObjectSrc) | |
| 99 || equalIgnoringCase(name, ReportURI) | |
| 100 || equalIgnoringCase(name, Sandbox) | |
| 101 || equalIgnoringCase(name, ScriptSrc) | |
| 102 || equalIgnoringCase(name, StyleSrc) | |
| 103 || equalIgnoringCase(name, BaseURI) | |
| 104 || equalIgnoringCase(name, ChildSrc) | |
| 105 || equalIgnoringCase(name, FormAction) | |
| 106 || equalIgnoringCase(name, FrameAncestors) | |
| 107 || equalIgnoringCase(name, PluginTypes) | |
| 108 || equalIgnoringCase(name, ReflectedXSS) | |
| 109 || equalIgnoringCase(name, Referrer) | |
| 110 ); | |
| 111 } | |
| 112 | |
| 113 static UseCounter::Feature getUseCounterType(ContentSecurityPolicyHeaderType typ
e) | |
| 114 { | |
| 115 switch (type) { | |
| 116 case ContentSecurityPolicyHeaderTypeEnforce: | |
| 117 return UseCounter::ContentSecurityPolicy; | |
| 118 case ContentSecurityPolicyHeaderTypeReport: | |
| 119 return UseCounter::ContentSecurityPolicyReportOnly; | |
| 120 } | |
| 121 ASSERT_NOT_REACHED(); | |
| 122 return UseCounter::NumberOfFeatures; | |
| 123 } | |
| 124 | |
| 125 static ReferrerPolicy mergeReferrerPolicies(ReferrerPolicy a, ReferrerPolicy b) | |
| 126 { | |
| 127 if (a != b) | |
| 128 return ReferrerPolicyNever; | |
| 129 return a; | |
| 130 } | |
| 131 | |
| 132 ContentSecurityPolicy::ContentSecurityPolicy(ExecutionContextClient* client) | |
| 133 : m_client(client) | |
| 134 , m_overrideInlineStyleAllowed(false) | |
| 135 , m_scriptHashAlgorithmsUsed(ContentSecurityPolicyHashAlgorithmNone) | |
| 136 , m_styleHashAlgorithmsUsed(ContentSecurityPolicyHashAlgorithmNone) | |
| 137 { | |
| 138 } | |
| 139 | |
| 140 ContentSecurityPolicy::~ContentSecurityPolicy() | |
| 141 { | |
| 142 } | |
| 143 | |
| 144 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) | |
| 145 { | |
| 146 ASSERT(m_policies.isEmpty()); | |
| 147 for (CSPDirectiveListVector::const_iterator iter = other->m_policies.begin()
; iter != other->m_policies.end(); ++iter) | |
| 148 addPolicyFromHeaderValue((*iter)->header(), (*iter)->headerType(), (*ite
r)->headerSource()); | |
| 149 } | |
| 150 | |
| 151 void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyRespons
eHeaders& headers) | |
| 152 { | |
| 153 if (!headers.contentSecurityPolicy().isEmpty()) | |
| 154 didReceiveHeader(headers.contentSecurityPolicy(), ContentSecurityPolicyH
eaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP); | |
| 155 if (!headers.contentSecurityPolicyReportOnly().isEmpty()) | |
| 156 didReceiveHeader(headers.contentSecurityPolicyReportOnly(), ContentSecur
ityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP); | |
| 157 } | |
| 158 | |
| 159 void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecuri
tyPolicyHeaderType type, ContentSecurityPolicyHeaderSource source) | |
| 160 { | |
| 161 addPolicyFromHeaderValue(header, type, source); | |
| 162 } | |
| 163 | |
| 164 void ContentSecurityPolicy::addPolicyFromHeaderValue(const String& header, Conte
ntSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source) | |
| 165 { | |
| 166 Document* document = this->document(); | |
| 167 if (document) { | |
| 168 UseCounter::count(*document, getUseCounterType(type)); | |
| 169 | |
| 170 // CSP 1.1 defines report-only in a <meta> element as invalid. Measure f
or now, disable in experimental mode. | |
| 171 if (source == ContentSecurityPolicyHeaderSourceMeta && type == ContentSe
curityPolicyHeaderTypeReport) { | |
| 172 UseCounter::count(*document, UseCounter::ContentSecurityPolicyReport
OnlyInMeta); | |
| 173 if (experimentalFeaturesEnabled()) { | |
| 174 reportReportOnlyInMeta(header); | |
| 175 return; | |
| 176 } | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 | |
| 181 Vector<UChar> characters; | |
| 182 header.appendTo(characters); | |
| 183 | |
| 184 const UChar* begin = characters.data(); | |
| 185 const UChar* end = begin + characters.size(); | |
| 186 | |
| 187 // RFC2616, section 4.2 specifies that headers appearing multiple times can | |
| 188 // be combined with a comma. Walk the header string, and parse each comma | |
| 189 // separated chunk as a separate header. | |
| 190 const UChar* position = begin; | |
| 191 while (position < end) { | |
| 192 skipUntil<UChar>(position, end, ','); | |
| 193 | |
| 194 // header1,header2 OR header1 | |
| 195 // ^ ^ | |
| 196 OwnPtr<CSPDirectiveList> policy = CSPDirectiveList::create(this, begin,
position, type, source); | |
| 197 | |
| 198 // We disable 'eval()' even in the case of report-only policies, and rel
y on the check in the V8Initializer::codeGenerationCheckCallbackInMainThread cal
lback to determine whether the call should execute or not. | |
| 199 if (!policy->allowEval(0, SuppressReport)) | |
| 200 m_client->disableEval(policy->evalDisabledErrorMessage()); | |
| 201 | |
| 202 m_policies.append(policy.release()); | |
| 203 | |
| 204 // Skip the comma, and begin the next header from the current position. | |
| 205 ASSERT(position == end || *position == ','); | |
| 206 skipExactly<UChar>(position, end, ','); | |
| 207 begin = position; | |
| 208 } | |
| 209 | |
| 210 if (document && type != ContentSecurityPolicyHeaderTypeReport && didSetRefer
rerPolicy()) | |
| 211 document->setReferrerPolicy(referrerPolicy()); | |
| 212 } | |
| 213 | |
| 214 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value) | |
| 215 { | |
| 216 m_overrideInlineStyleAllowed = value; | |
| 217 } | |
| 218 | |
| 219 const String& ContentSecurityPolicy::deprecatedHeader() const | |
| 220 { | |
| 221 return m_policies.isEmpty() ? emptyString() : m_policies[0]->header(); | |
| 222 } | |
| 223 | |
| 224 ContentSecurityPolicyHeaderType ContentSecurityPolicy::deprecatedHeaderType() co
nst | |
| 225 { | |
| 226 return m_policies.isEmpty() ? ContentSecurityPolicyHeaderTypeEnforce : m_pol
icies[0]->headerType(); | |
| 227 } | |
| 228 | |
| 229 template<bool (CSPDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatu
s) const> | |
| 230 bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolic
y::ReportingStatus reportingStatus) | |
| 231 { | |
| 232 for (size_t i = 0; i < policies.size(); ++i) { | |
| 233 if (!(policies[i].get()->*allowed)(reportingStatus)) | |
| 234 return false; | |
| 235 } | |
| 236 return true; | |
| 237 } | |
| 238 | |
| 239 template<bool (CSPDirectiveList::*allowed)(ScriptState* state, ContentSecurityPo
licy::ReportingStatus) const> | |
| 240 bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, ScriptState
* state, ContentSecurityPolicy::ReportingStatus reportingStatus) | |
| 241 { | |
| 242 for (size_t i = 0; i < policies.size(); ++i) { | |
| 243 if (!(policies[i].get()->*allowed)(state, reportingStatus)) | |
| 244 return false; | |
| 245 } | |
| 246 return true; | |
| 247 } | |
| 248 | |
| 249 template<bool (CSPDirectiveList::*allowed)(const String&, const WTF::OrdinalNumb
er&, ContentSecurityPolicy::ReportingStatus) const> | |
| 250 bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const Str
ing& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::R
eportingStatus reportingStatus) | |
| 251 { | |
| 252 for (size_t i = 0; i < policies.size(); ++i) { | |
| 253 if (!(policies[i].get()->*allowed)(contextURL, contextLine, reportingSta
tus)) | |
| 254 return false; | |
| 255 } | |
| 256 return true; | |
| 257 } | |
| 258 | |
| 259 template<bool (CSPDirectiveList::*allowed)(const String&) const> | |
| 260 bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const Strin
g& nonce) | |
| 261 { | |
| 262 for (size_t i = 0; i < policies.size(); ++i) { | |
| 263 if (!(policies[i].get()->*allowed)(nonce)) | |
| 264 return false; | |
| 265 } | |
| 266 return true; | |
| 267 } | |
| 268 | |
| 269 template<bool (CSPDirectiveList::*allowed)(const CSPHashValue&) const> | |
| 270 bool isAllowedByAllWithHash(const CSPDirectiveListVector& policies, const CSPHas
hValue& hashValue) | |
| 271 { | |
| 272 for (size_t i = 0; i < policies.size(); ++i) { | |
| 273 if (!(policies[i].get()->*allowed)(hashValue)) | |
| 274 return false; | |
| 275 } | |
| 276 return true; | |
| 277 } | |
| 278 | |
| 279 template<bool (CSPDirectiveList::*allowFromURL)(const KURL&, ContentSecurityPoli
cy::ReportingStatus) const> | |
| 280 bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const KURL& u
rl, ContentSecurityPolicy::ReportingStatus reportingStatus) | |
| 281 { | |
| 282 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) | |
| 283 return true; | |
| 284 | |
| 285 for (size_t i = 0; i < policies.size(); ++i) { | |
| 286 if (!(policies[i].get()->*allowFromURL)(url, reportingStatus)) | |
| 287 return false; | |
| 288 } | |
| 289 return true; | |
| 290 } | |
| 291 | |
| 292 template<bool (CSPDirectiveList::*allowed)(LocalFrame*, ContentSecurityPolicy::R
eportingStatus) const> | |
| 293 bool isAllowedByAllWithFrame(const CSPDirectiveListVector& policies, LocalFrame*
frame, ContentSecurityPolicy::ReportingStatus reportingStatus) | |
| 294 { | |
| 295 for (size_t i = 0; i < policies.size(); ++i) { | |
| 296 if (!(policies[i].get()->*allowed)(frame, reportingStatus)) | |
| 297 return false; | |
| 298 } | |
| 299 return true; | |
| 300 } | |
| 301 | |
| 302 void computeDigest(const CString& source, blink::WebCryptoAlgorithmId algorithmI
d, DigestValue& digest) | |
| 303 { | |
| 304 blink::WebCrypto* crypto = blink::Platform::current()->crypto(); | |
| 305 blink::WebArrayBuffer result; | |
| 306 | |
| 307 ASSERT(crypto); | |
| 308 | |
| 309 crypto->digestSynchronous(algorithmId, reinterpret_cast<const unsigned char*
>(source.data()), source.length(), result); | |
| 310 | |
| 311 ASSERT(!result.isNull()); | |
| 312 | |
| 313 digest.append(reinterpret_cast<uint8_t*>(result.data()), result.byteLength()
); | |
| 314 } | |
| 315 | |
| 316 template<bool (CSPDirectiveList::*allowed)(const CSPHashValue&) const> | |
| 317 bool checkDigest(const String& source, uint8_t hashAlgorithmsUsed, const CSPDire
ctiveListVector& policies) | |
| 318 { | |
| 319 // Any additions or subtractions from this struct should also modify the | |
| 320 // respective entries in the kSupportedPrefixes array in | |
| 321 // CSPSourceList::parseHash(). | |
| 322 static const struct { | |
| 323 ContentSecurityPolicyHashAlgorithm cspHashAlgorithm; | |
| 324 blink::WebCryptoAlgorithmId webCryptoAlgorithmId; | |
| 325 } kAlgorithmMap[] = { | |
| 326 { ContentSecurityPolicyHashAlgorithmSha1, blink::WebCryptoAlgorithmIdSha
1 }, | |
| 327 { ContentSecurityPolicyHashAlgorithmSha256, blink::WebCryptoAlgorithmIdS
ha256 }, | |
| 328 { ContentSecurityPolicyHashAlgorithmSha384, blink::WebCryptoAlgorithmIdS
ha384 }, | |
| 329 { ContentSecurityPolicyHashAlgorithmSha512, blink::WebCryptoAlgorithmIdS
ha512 } | |
| 330 }; | |
| 331 | |
| 332 CString normalizedSource = UTF8Encoding().normalizeAndEncode(source, WTF::En
titiesForUnencodables); | |
| 333 | |
| 334 // See comment in CSPSourceList::parseHash about why we are using this sizeo
f | |
| 335 // calculation instead of WTF_ARRAY_LENGTH. | |
| 336 for (size_t i = 0; i < (sizeof(kAlgorithmMap) / sizeof(kAlgorithmMap[0])); i
++) { | |
| 337 DigestValue digest; | |
| 338 if (kAlgorithmMap[i].cspHashAlgorithm & hashAlgorithmsUsed) { | |
| 339 computeDigest(normalizedSource, kAlgorithmMap[i].webCryptoAlgorithmI
d, digest); | |
| 340 if (isAllowedByAllWithHash<allowed>(policies, CSPHashValue(kAlgorith
mMap[i].cspHashAlgorithm, digest))) | |
| 341 return true; | |
| 342 } | |
| 343 } | |
| 344 | |
| 345 return false; | |
| 346 } | |
| 347 | |
| 348 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const
WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportin
gStatus) const | |
| 349 { | |
| 350 return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>(m_p
olicies, contextURL, contextLine, reportingStatus); | |
| 351 } | |
| 352 | |
| 353 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, c
onst WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus rep
ortingStatus) const | |
| 354 { | |
| 355 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers
>(m_policies, contextURL, contextLine, reportingStatus); | |
| 356 } | |
| 357 | |
| 358 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WT
F::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingS
tatus) const | |
| 359 { | |
| 360 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_pol
icies, contextURL, contextLine, reportingStatus); | |
| 361 } | |
| 362 | |
| 363 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF
::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingSt
atus) const | |
| 364 { | |
| 365 if (m_overrideInlineStyleAllowed) | |
| 366 return true; | |
| 367 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineStyle>(m_poli
cies, contextURL, contextLine, reportingStatus); | |
| 368 } | |
| 369 | |
| 370 bool ContentSecurityPolicy::allowEval(ScriptState* state, ContentSecurityPolicy:
:ReportingStatus reportingStatus) const | |
| 371 { | |
| 372 return isAllowedByAllWithState<&CSPDirectiveList::allowEval>(m_policies, sta
te, reportingStatus); | |
| 373 } | |
| 374 | |
| 375 String ContentSecurityPolicy::evalDisabledErrorMessage() const | |
| 376 { | |
| 377 for (size_t i = 0; i < m_policies.size(); ++i) { | |
| 378 if (!m_policies[i]->allowEval(0, SuppressReport)) | |
| 379 return m_policies[i]->evalDisabledErrorMessage(); | |
| 380 } | |
| 381 return String(); | |
| 382 } | |
| 383 | |
| 384 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& ty
peAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingSt
atus) const | |
| 385 { | |
| 386 for (size_t i = 0; i < m_policies.size(); ++i) { | |
| 387 if (!m_policies[i]->allowPluginType(type, typeAttribute, url, reportingS
tatus)) | |
| 388 return false; | |
| 389 } | |
| 390 return true; | |
| 391 } | |
| 392 | |
| 393 bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, ContentSecuri
tyPolicy::ReportingStatus reportingStatus) const | |
| 394 { | |
| 395 return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_pol
icies, url, reportingStatus); | |
| 396 } | |
| 397 | |
| 398 bool ContentSecurityPolicy::allowScriptNonce(const String& nonce) const | |
| 399 { | |
| 400 return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_polici
es, nonce); | |
| 401 } | |
| 402 | |
| 403 bool ContentSecurityPolicy::allowStyleNonce(const String& nonce) const | |
| 404 { | |
| 405 return isAllowedByAllWithNonce<&CSPDirectiveList::allowStyleNonce>(m_policie
s, nonce); | |
| 406 } | |
| 407 | |
| 408 bool ContentSecurityPolicy::allowScriptHash(const String& source) const | |
| 409 { | |
| 410 return checkDigest<&CSPDirectiveList::allowScriptHash>(source, m_scriptHashA
lgorithmsUsed, m_policies); | |
| 411 } | |
| 412 | |
| 413 bool ContentSecurityPolicy::allowStyleHash(const String& source) const | |
| 414 { | |
| 415 return checkDigest<&CSPDirectiveList::allowStyleHash>(source, m_styleHashAlg
orithmsUsed, m_policies); | |
| 416 } | |
| 417 | |
| 418 void ContentSecurityPolicy::usesScriptHashAlgorithms(uint8_t algorithms) | |
| 419 { | |
| 420 m_scriptHashAlgorithmsUsed |= algorithms; | |
| 421 } | |
| 422 | |
| 423 void ContentSecurityPolicy::usesStyleHashAlgorithms(uint8_t algorithms) | |
| 424 { | |
| 425 m_styleHashAlgorithmsUsed |= algorithms; | |
| 426 } | |
| 427 | |
| 428 bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url, ContentSecuri
tyPolicy::ReportingStatus reportingStatus) const | |
| 429 { | |
| 430 return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_pol
icies, url, reportingStatus); | |
| 431 } | |
| 432 | |
| 433 bool ContentSecurityPolicy::allowChildFrameFromSource(const KURL& url, ContentSe
curityPolicy::ReportingStatus reportingStatus) const | |
| 434 { | |
| 435 return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>(m
_policies, url, reportingStatus); | |
| 436 } | |
| 437 | |
| 438 bool ContentSecurityPolicy::allowImageFromSource(const KURL& url, ContentSecurit
yPolicy::ReportingStatus reportingStatus) const | |
| 439 { | |
| 440 return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>(m_poli
cies, url, reportingStatus); | |
| 441 } | |
| 442 | |
| 443 bool ContentSecurityPolicy::allowStyleFromSource(const KURL& url, ContentSecurit
yPolicy::ReportingStatus reportingStatus) const | |
| 444 { | |
| 445 return isAllowedByAllWithURL<&CSPDirectiveList::allowStyleFromSource>(m_poli
cies, url, reportingStatus); | |
| 446 } | |
| 447 | |
| 448 bool ContentSecurityPolicy::allowFontFromSource(const KURL& url, ContentSecurity
Policy::ReportingStatus reportingStatus) const | |
| 449 { | |
| 450 return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>(m_polic
ies, url, reportingStatus); | |
| 451 } | |
| 452 | |
| 453 bool ContentSecurityPolicy::allowMediaFromSource(const KURL& url, ContentSecurit
yPolicy::ReportingStatus reportingStatus) const | |
| 454 { | |
| 455 return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>(m_poli
cies, url, reportingStatus); | |
| 456 } | |
| 457 | |
| 458 bool ContentSecurityPolicy::allowConnectToSource(const KURL& url, ContentSecurit
yPolicy::ReportingStatus reportingStatus) const | |
| 459 { | |
| 460 return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_poli
cies, url, reportingStatus); | |
| 461 } | |
| 462 | |
| 463 bool ContentSecurityPolicy::allowFormAction(const KURL& url, ContentSecurityPoli
cy::ReportingStatus reportingStatus) const | |
| 464 { | |
| 465 return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>(m_policies,
url, reportingStatus); | |
| 466 } | |
| 467 | |
| 468 bool ContentSecurityPolicy::allowBaseURI(const KURL& url, ContentSecurityPolicy:
:ReportingStatus reportingStatus) const | |
| 469 { | |
| 470 return isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>(m_policies, ur
l, reportingStatus); | |
| 471 } | |
| 472 | |
| 473 bool ContentSecurityPolicy::allowAncestors(LocalFrame* frame, ContentSecurityPol
icy::ReportingStatus reportingStatus) const | |
| 474 { | |
| 475 return isAllowedByAllWithFrame<&CSPDirectiveList::allowAncestors>(m_policies
, frame, reportingStatus); | |
| 476 } | |
| 477 | |
| 478 bool ContentSecurityPolicy::allowChildContextFromSource(const KURL& url, Content
SecurityPolicy::ReportingStatus reportingStatus) const | |
| 479 { | |
| 480 return isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>
(m_policies, url, reportingStatus); | |
| 481 } | |
| 482 | |
| 483 bool ContentSecurityPolicy::allowWorkerContextFromSource(const KURL& url, Conten
tSecurityPolicy::ReportingStatus reportingStatus) const | |
| 484 { | |
| 485 // CSP 1.1 moves workers from 'script-src' to the new 'child-src'. Measure t
he impact of this backwards-incompatible change. | |
| 486 if (m_client->isDocument()) { | |
| 487 Document* document = static_cast<Document*>(m_client); | |
| 488 UseCounter::count(*document, UseCounter::WorkerSubjectToCSP); | |
| 489 if (isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource
>(m_policies, url, SuppressReport) && !isAllowedByAllWithURL<&CSPDirectiveList::
allowScriptFromSource>(m_policies, url, SuppressReport)) | |
| 490 UseCounter::count(*document, UseCounter::WorkerAllowedByChildBlocked
ByScript); | |
| 491 } | |
| 492 | |
| 493 return experimentalFeaturesEnabled() ? | |
| 494 isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_
policies, url, reportingStatus) : | |
| 495 isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_polici
es, url, reportingStatus); | |
| 496 } | |
| 497 | |
| 498 bool ContentSecurityPolicy::isActive() const | |
| 499 { | |
| 500 return !m_policies.isEmpty(); | |
| 501 } | |
| 502 | |
| 503 ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const | |
| 504 { | |
| 505 ReflectedXSSDisposition disposition = ReflectedXSSUnset; | |
| 506 for (size_t i = 0; i < m_policies.size(); ++i) { | |
| 507 if (m_policies[i]->reflectedXSSDisposition() > disposition) | |
| 508 disposition = std::max(disposition, m_policies[i]->reflectedXSSDispo
sition()); | |
| 509 } | |
| 510 return disposition; | |
| 511 } | |
| 512 | |
| 513 ReferrerPolicy ContentSecurityPolicy::referrerPolicy() const | |
| 514 { | |
| 515 ReferrerPolicy policy = ReferrerPolicyDefault; | |
| 516 bool first = true; | |
| 517 for (size_t i = 0; i < m_policies.size(); ++i) { | |
| 518 if (m_policies[i]->didSetReferrerPolicy()) { | |
| 519 if (first) | |
| 520 policy = m_policies[i]->referrerPolicy(); | |
| 521 else | |
| 522 policy = mergeReferrerPolicies(policy, m_policies[i]->referrerPo
licy()); | |
| 523 } | |
| 524 } | |
| 525 return policy; | |
| 526 } | |
| 527 | |
| 528 bool ContentSecurityPolicy::didSetReferrerPolicy() const | |
| 529 { | |
| 530 for (size_t i = 0; i < m_policies.size(); ++i) { | |
| 531 if (m_policies[i]->didSetReferrerPolicy()) | |
| 532 return true; | |
| 533 } | |
| 534 return false; | |
| 535 } | |
| 536 | |
| 537 SecurityOrigin* ContentSecurityPolicy::securityOrigin() const | |
| 538 { | |
| 539 return m_client->securityContext().securityOrigin(); | |
| 540 } | |
| 541 | |
| 542 const KURL ContentSecurityPolicy::url() const | |
| 543 { | |
| 544 return m_client->contextURL(); | |
| 545 } | |
| 546 | |
| 547 KURL ContentSecurityPolicy::completeURL(const String& url) const | |
| 548 { | |
| 549 return m_client->contextCompleteURL(url); | |
| 550 } | |
| 551 | |
| 552 void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) const | |
| 553 { | |
| 554 if (Document* document = this->document()) | |
| 555 document->enforceSandboxFlags(mask); | |
| 556 } | |
| 557 | |
| 558 static String stripURLForUseInReport(Document* document, const KURL& url) | |
| 559 { | |
| 560 if (!url.isValid()) | |
| 561 return String(); | |
| 562 if (!url.isHierarchical() || url.protocolIs("file")) | |
| 563 return url.protocol(); | |
| 564 return document->securityOrigin()->canRequest(url) ? url.strippedForUseAsRef
errer() : SecurityOrigin::create(url)->toString(); | |
| 565 } | |
| 566 | |
| 567 static void gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventI
nit& init, Document* document, const String& directiveText, const String& effect
iveDirective, const KURL& blockedURL, const String& header) | |
| 568 { | |
| 569 init.documentURI = document->url().string(); | |
| 570 init.referrer = document->referrer(); | |
| 571 init.blockedURI = stripURLForUseInReport(document, blockedURL); | |
| 572 init.violatedDirective = directiveText; | |
| 573 init.effectiveDirective = effectiveDirective; | |
| 574 init.originalPolicy = header; | |
| 575 init.sourceFile = String(); | |
| 576 init.lineNumber = 0; | |
| 577 init.columnNumber = 0; | |
| 578 init.statusCode = 0; | |
| 579 | |
| 580 if (!SecurityOrigin::isSecure(document->url()) && document->loader()) | |
| 581 init.statusCode = document->loader()->response().httpStatusCode(); | |
| 582 | |
| 583 RefPtr<ScriptCallStack> stack = createScriptCallStack(1, false); | |
| 584 if (!stack) | |
| 585 return; | |
| 586 | |
| 587 const ScriptCallFrame& callFrame = stack->at(0); | |
| 588 | |
| 589 if (callFrame.lineNumber()) { | |
| 590 KURL source = KURL(ParsedURLString, callFrame.sourceURL()); | |
| 591 init.sourceFile = stripURLForUseInReport(document, source); | |
| 592 init.lineNumber = callFrame.lineNumber(); | |
| 593 init.columnNumber = callFrame.columnNumber(); | |
| 594 } | |
| 595 } | |
| 596 | |
| 597 void ContentSecurityPolicy::reportViolation(const String& directiveText, const S
tring& effectiveDirective, const String& consoleMessage, const KURL& blockedURL,
const Vector<KURL>& reportURIs, const String& header) | |
| 598 { | |
| 599 // FIXME: Support sending reports from worker. | |
| 600 if (!m_client->isDocument()) | |
| 601 return; | |
| 602 | |
| 603 Document* document = this->document(); | |
| 604 LocalFrame* frame = document->frame(); | |
| 605 if (!frame) | |
| 606 return; | |
| 607 | |
| 608 SecurityPolicyViolationEventInit violationData; | |
| 609 gatherSecurityPolicyViolationEventData(violationData, document, directiveTex
t, effectiveDirective, blockedURL, header); | |
| 610 | |
| 611 if (experimentalFeaturesEnabled()) | |
| 612 frame->domWindow()->enqueueDocumentEvent(SecurityPolicyViolationEvent::c
reate(EventTypeNames::securitypolicyviolation, violationData)); | |
| 613 | |
| 614 if (reportURIs.isEmpty()) | |
| 615 return; | |
| 616 | |
| 617 // We need to be careful here when deciding what information to send to the | |
| 618 // report-uri. Currently, we send only the current document's URL and the | |
| 619 // directive that was violated. The document's URL is safe to send because | |
| 620 // it's the document itself that's requesting that it be sent. You could | |
| 621 // make an argument that we shouldn't send HTTPS document URLs to HTTP | |
| 622 // report-uris (for the same reasons that we supress the Referer in that | |
| 623 // case), but the Referer is sent implicitly whereas this request is only | |
| 624 // sent explicitly. As for which directive was violated, that's pretty | |
| 625 // harmless information. | |
| 626 | |
| 627 RefPtr<JSONObject> cspReport = JSONObject::create(); | |
| 628 cspReport->setString("document-uri", violationData.documentURI); | |
| 629 cspReport->setString("referrer", violationData.referrer); | |
| 630 cspReport->setString("violated-directive", violationData.violatedDirective); | |
| 631 if (experimentalFeaturesEnabled()) | |
| 632 cspReport->setString("effective-directive", violationData.effectiveDirec
tive); | |
| 633 cspReport->setString("original-policy", violationData.originalPolicy); | |
| 634 cspReport->setString("blocked-uri", violationData.blockedURI); | |
| 635 if (!violationData.sourceFile.isEmpty() && violationData.lineNumber) { | |
| 636 cspReport->setString("source-file", violationData.sourceFile); | |
| 637 cspReport->setNumber("line-number", violationData.lineNumber); | |
| 638 cspReport->setNumber("column-number", violationData.columnNumber); | |
| 639 } | |
| 640 cspReport->setNumber("status-code", violationData.statusCode); | |
| 641 | |
| 642 RefPtr<JSONObject> reportObject = JSONObject::create(); | |
| 643 reportObject->setObject("csp-report", cspReport.release()); | |
| 644 String stringifiedReport = reportObject->toJSONString(); | |
| 645 | |
| 646 if (!shouldSendViolationReport(stringifiedReport)) | |
| 647 return; | |
| 648 | |
| 649 RefPtr<FormData> report = FormData::create(stringifiedReport.utf8()); | |
| 650 | |
| 651 for (size_t i = 0; i < reportURIs.size(); ++i) | |
| 652 PingLoader::sendViolationReport(frame, reportURIs[i], report, PingLoader
::ContentSecurityPolicyViolationReport); | |
| 653 | |
| 654 didSendViolationReport(stringifiedReport); | |
| 655 } | |
| 656 | |
| 657 void ContentSecurityPolicy::reportInvalidReferrer(const String& invalidValue) co
nst | |
| 658 { | |
| 659 logToConsole("The 'referrer' Content Security Policy directive has the inval
id value \"" + invalidValue + "\". Valid values are \"always\", \"default\", \"n
ever\", and \"origin\"."); | |
| 660 } | |
| 661 | |
| 662 void ContentSecurityPolicy::reportReportOnlyInMeta(const String& header) const | |
| 663 { | |
| 664 logToConsole("The report-only Content Security Policy '" + header + "' was d
elivered via a <meta> element, which is disallowed. The policy has been ignored.
"); | |
| 665 } | |
| 666 | |
| 667 void ContentSecurityPolicy::reportMetaOutsideHead(const String& header) const | |
| 668 { | |
| 669 logToConsole("The Content Security Policy '" + header + "' was delivered via
a <meta> element outside the document's <head>, which is disallowed. The policy
has been ignored."); | |
| 670 } | |
| 671 | |
| 672 void ContentSecurityPolicy::reportInvalidInReportOnly(const String& name) const | |
| 673 { | |
| 674 logToConsole("The Content Security Policy directive '" + name + "' is ignore
d when delivered in a report-only policy."); | |
| 675 } | |
| 676 | |
| 677 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const | |
| 678 { | |
| 679 DEFINE_STATIC_LOCAL(String, allow, ("allow")); | |
| 680 DEFINE_STATIC_LOCAL(String, options, ("options")); | |
| 681 DEFINE_STATIC_LOCAL(String, policyURI, ("policy-uri")); | |
| 682 DEFINE_STATIC_LOCAL(String, allowMessage, ("The 'allow' directive has been r
eplaced with 'default-src'. Please use that directive instead, as 'allow' has no
effect.")); | |
| 683 DEFINE_STATIC_LOCAL(String, optionsMessage, ("The 'options' directive has be
en replaced with 'unsafe-inline' and 'unsafe-eval' source expressions for the 's
cript-src' and 'style-src' directives. Please use those directives instead, as '
options' has no effect.")); | |
| 684 DEFINE_STATIC_LOCAL(String, policyURIMessage, ("The 'policy-uri' directive h
as been removed from the specification. Please specify a complete policy via the
Content-Security-Policy header.")); | |
| 685 | |
| 686 String message = "Unrecognized Content-Security-Policy directive '" + name +
"'.\n"; | |
| 687 if (equalIgnoringCase(name, allow)) | |
| 688 message = allowMessage; | |
| 689 else if (equalIgnoringCase(name, options)) | |
| 690 message = optionsMessage; | |
| 691 else if (equalIgnoringCase(name, policyURI)) | |
| 692 message = policyURIMessage; | |
| 693 | |
| 694 logToConsole(message); | |
| 695 } | |
| 696 | |
| 697 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& dire
ctiveName, const String& sourceExpression) const | |
| 698 { | |
| 699 String message = "The Content Security Policy directive '" + directiveName +
"' contains '" + sourceExpression + "' as a source expression. Did you mean '"
+ directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?"; | |
| 700 logToConsole(message); | |
| 701 } | |
| 702 | |
| 703 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const | |
| 704 { | |
| 705 String message = "Ignoring duplicate Content-Security-Policy directive '" +
name + "'.\n"; | |
| 706 logToConsole(message); | |
| 707 } | |
| 708 | |
| 709 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) c
onst | |
| 710 { | |
| 711 String message; | |
| 712 if (pluginType.isNull()) | |
| 713 message = "'plugin-types' Content Security Policy directive is empty; al
l plugins will be blocked.\n"; | |
| 714 else | |
| 715 message = "Invalid plugin type in 'plugin-types' Content Security Policy
directive: '" + pluginType + "'.\n"; | |
| 716 logToConsole(message); | |
| 717 } | |
| 718 | |
| 719 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags
) const | |
| 720 { | |
| 721 logToConsole("Error while parsing the 'sandbox' Content Security Policy dire
ctive: " + invalidFlags); | |
| 722 } | |
| 723 | |
| 724 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue
) const | |
| 725 { | |
| 726 logToConsole("The 'reflected-xss' Content Security Policy directive has the
invalid value \"" + invalidValue + "\". Valid values are \"allow\", \"filter\",
and \"block\"."); | |
| 727 } | |
| 728 | |
| 729 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& d
irectiveName, const String& value) const | |
| 730 { | |
| 731 String message = "The value for Content Security Policy directive '" + direc
tiveName + "' contains an invalid character: '" + value + "'. Non-whitespace cha
racters outside ASCII 0x21-0x7E must be percent-encoded, as described in RFC 398
6, section 2.1: http://tools.ietf.org/html/rfc3986#section-2.1."; | |
| 732 logToConsole(message); | |
| 733 } | |
| 734 | |
| 735 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveNa
me, const String& value, const char invalidChar) const | |
| 736 { | |
| 737 ASSERT(invalidChar == '#' || invalidChar == '?'); | |
| 738 | |
| 739 String ignoring = "The fragment identifier, including the '#', will be ignor
ed."; | |
| 740 if (invalidChar == '?') | |
| 741 ignoring = "The query component, including the '?', will be ignored."; | |
| 742 String message = "The source list for Content Security Policy directive '" +
directiveName + "' contains a source with an invalid path: '" + value + "'. " +
ignoring; | |
| 743 logToConsole(message); | |
| 744 } | |
| 745 | |
| 746 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiv
eName, const String& source) const | |
| 747 { | |
| 748 String message = "The source list for Content Security Policy directive '" +
directiveName + "' contains an invalid source: '" + source + "'. It will be ign
ored."; | |
| 749 if (equalIgnoringCase(source, "'none'")) | |
| 750 message = message + " Note that 'none' has no effect unless it is the on
ly expression in the source list."; | |
| 751 logToConsole(message); | |
| 752 } | |
| 753 | |
| 754 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const | |
| 755 { | |
| 756 logToConsole("The Content Security Policy '" + policy + "' was delivered in
report-only mode, but does not specify a 'report-uri'; the policy will have no e
ffect. Please either add a 'report-uri' directive, or deliver the policy via the
'Content-Security-Policy' header."); | |
| 757 } | |
| 758 | |
| 759 void ContentSecurityPolicy::logToConsole(const String& message) const | |
| 760 { | |
| 761 m_client->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, messag
e); | |
| 762 } | |
| 763 | |
| 764 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String
& directiveText) const | |
| 765 { | |
| 766 m_client->reportBlockedScriptExecutionToInspector(directiveText); | |
| 767 } | |
| 768 | |
| 769 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const | |
| 770 { | |
| 771 return RuntimeEnabledFeatures::experimentalContentSecurityPolicyFeaturesEnab
led(); | |
| 772 } | |
| 773 | |
| 774 bool ContentSecurityPolicy::shouldBypassMainWorld(ExecutionContext* context) | |
| 775 { | |
| 776 if (context && context->isDocument()) { | |
| 777 Document* document = toDocument(context); | |
| 778 if (document->frame()) | |
| 779 return document->frame()->script().shouldBypassMainWorldContentSecur
ityPolicy(); | |
| 780 } | |
| 781 return false; | |
| 782 } | |
| 783 | |
| 784 bool ContentSecurityPolicy::shouldSendViolationReport(const String& report) cons
t | |
| 785 { | |
| 786 // Collisions have no security impact, so we can save space by storing only
the string's hash rather than the whole report. | |
| 787 return !m_violationReportsSent.contains(report.impl()->hash()); | |
| 788 } | |
| 789 | |
| 790 void ContentSecurityPolicy::didSendViolationReport(const String& report) | |
| 791 { | |
| 792 m_violationReportsSent.add(report.impl()->hash()); | |
| 793 } | |
| 794 | |
| 795 } // namespace WebCore | |
| OLD | NEW |