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/csp/ContentSecurityPolicy.h" 28 29 #include "bindings/core/v8/ScriptCallStackFactory.h" 30 #include "bindings/core/v8/ScriptController.h" 31 #include "core/dom/DOMStringList.h" 32 #include "core/dom/Document.h" 33 #include "core/events/SecurityPolicyViolationEvent.h" 34 #include "core/frame/LocalDOMWindow.h" 35 #include "core/frame/LocalFrame.h" 36 #include "core/frame/UseCounter.h" 37 #include "core/frame/csp/CSPDirectiveList.h" 38 #include "core/frame/csp/CSPSource.h" 39 #include "core/frame/csp/CSPSourceList.h" 40 #include "core/frame/csp/MediaListDirective.h" 41 #include "core/frame/csp/SourceListDirective.h" 42 #include "core/inspector/ConsoleMessage.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/Crypto.h" 48 #include "platform/JSONValues.h" 49 #include "platform/NotImplemented.h" 50 #include "platform/ParsingUtilities.h" 51 #include "platform/RuntimeEnabledFeatures.h" 52 #include "platform/network/ContentSecurityPolicyParsers.h" 53 #include "platform/network/ContentSecurityPolicyResponseHeaders.h" 54 #include "platform/network/FormData.h" 55 #include "platform/network/ResourceResponse.h" 56 #include "platform/weborigin/KURL.h" 57 #include "platform/weborigin/KnownPorts.h" 58 #include "platform/weborigin/SchemeRegistry.h" 59 #include "platform/weborigin/SecurityOrigin.h" 60 #include "public/platform/Platform.h" 61 #include "public/platform/WebArrayBuffer.h" 62 #include "public/platform/WebCrypto.h" 63 #include "public/platform/WebCryptoAlgorithm.h" 64 #include "wtf/StringHasher.h" 65 #include "wtf/text/StringBuilder.h" 66 #include "wtf/text/StringUTF8Adaptor.h" 67 68 namespace blink { 69 70 // CSP 1.0 Directives 71 const char ContentSecurityPolicy::ConnectSrc[] = "connect-src"; 72 const char ContentSecurityPolicy::DefaultSrc[] = "default-src"; 73 const char ContentSecurityPolicy::FontSrc[] = "font-src"; 74 const char ContentSecurityPolicy::FrameSrc[] = "frame-src"; 75 const char ContentSecurityPolicy::ImgSrc[] = "img-src"; 76 const char ContentSecurityPolicy::MediaSrc[] = "media-src"; 77 const char ContentSecurityPolicy::ObjectSrc[] = "object-src"; 78 const char ContentSecurityPolicy::ReportURI[] = "report-uri"; 79 const char ContentSecurityPolicy::Sandbox[] = "sandbox"; 80 const char ContentSecurityPolicy::ScriptSrc[] = "script-src"; 81 const char ContentSecurityPolicy::StyleSrc[] = "style-src"; 82 83 // CSP 1.1 Directives 84 const char ContentSecurityPolicy::BaseURI[] = "base-uri"; 85 const char ContentSecurityPolicy::ChildSrc[] = "child-src"; 86 const char ContentSecurityPolicy::FormAction[] = "form-action"; 87 const char ContentSecurityPolicy::FrameAncestors[] = "frame-ancestors"; 88 const char ContentSecurityPolicy::PluginTypes[] = "plugin-types"; 89 const char ContentSecurityPolicy::ReflectedXSS[] = "reflected-xss"; 90 const char ContentSecurityPolicy::Referrer[] = "referrer"; 91 92 bool ContentSecurityPolicy::isDirectiveName(const String& name) 93 { 94 return (equalIgnoringCase(name, ConnectSrc) 95 || equalIgnoringCase(name, DefaultSrc) 96 || equalIgnoringCase(name, FontSrc) 97 || equalIgnoringCase(name, FrameSrc) 98 || equalIgnoringCase(name, ImgSrc) 99 || equalIgnoringCase(name, MediaSrc) 100 || equalIgnoringCase(name, ObjectSrc) 101 || equalIgnoringCase(name, ReportURI) 102 || equalIgnoringCase(name, Sandbox) 103 || equalIgnoringCase(name, ScriptSrc) 104 || equalIgnoringCase(name, StyleSrc) 105 || equalIgnoringCase(name, BaseURI) 106 || equalIgnoringCase(name, ChildSrc) 107 || equalIgnoringCase(name, FormAction) 108 || equalIgnoringCase(name, FrameAncestors) 109 || equalIgnoringCase(name, PluginTypes) 110 || equalIgnoringCase(name, ReflectedXSS) 111 || equalIgnoringCase(name, Referrer) 112 ); 113 } 114 115 static UseCounter::Feature getUseCounterType(ContentSecurityPolicyHeaderType type) 116 { 117 switch (type) { 118 case ContentSecurityPolicyHeaderTypeEnforce: 119 return UseCounter::ContentSecurityPolicy; 120 case ContentSecurityPolicyHeaderTypeReport: 121 return UseCounter::ContentSecurityPolicyReportOnly; 122 } 123 ASSERT_NOT_REACHED(); 124 return UseCounter::NumberOfFeatures; 125 } 126 127 static ReferrerPolicy mergeReferrerPolicies(ReferrerPolicy a, ReferrerPolicy b) 128 { 129 if (a != b) 130 return ReferrerPolicyNever; 131 return a; 132 } 133 134 ContentSecurityPolicy::ContentSecurityPolicy() 135 : m_executionContext(0) 136 , m_overrideInlineStyleAllowed(false) 137 , m_scriptHashAlgorithmsUsed(ContentSecurityPolicyHashAlgorithmNone) 138 , m_styleHashAlgorithmsUsed(ContentSecurityPolicyHashAlgorithmNone) 139 , m_sandboxMask(0) 140 , m_referrerPolicy(ReferrerPolicyDefault) 141 { 142 } 143 144 void ContentSecurityPolicy::bindToExecutionContext(ExecutionContext* executionContext) 145 { 146 m_executionContext = executionContext; 147 applyPolicySideEffectsToExecutionContext(); 148 } 149 150 void ContentSecurityPolicy::applyPolicySideEffectsToExecutionContext() 151 { 152 ASSERT(m_executionContext); 153 // Ensure that 'self' processes correctly. 154 m_selfSource = adoptPtr(new CSPSource(this, securityOrigin()->protocol(), securityOrigin()->host(), securityOrigin()->port(), String(), CSPSource::NoWildcard, CSPSource::NoWildcard)); 155 156 // If we're in a Document, set the referrer policy and sandbox flags, then dump all the 157 // parsing error messages, then poke at histograms. 158 if (Document* document = this->document()) { 159 document->enforceSandboxFlags(m_sandboxMask); 160 if (didSetReferrerPolicy()) 161 document->setReferrerPolicy(m_referrerPolicy); 162 163 for (ConsoleMessageVector::const_iterator iter = m_consoleMessages.begin(); iter != m_consoleMessages.end(); ++iter) 164 m_executionContext->addConsoleMessage(*iter); 165 m_consoleMessages.clear(); 166 167 for (CSPDirectiveListVector::const_iterator iter = m_policies.begin(); iter != m_policies.end(); ++iter) 168 UseCounter::count(*document, getUseCounterType((*iter)->headerType())); 169 } 170 171 // We disable 'eval()' even in the case of report-only policies, and rely on the check in the 172 // V8Initializer::codeGenerationCheckCallbackInMainThread callback to determine whether the 173 // call should execute or not. 174 if (!m_disableEvalErrorMessage.isNull()) 175 m_executionContext->disableEval(m_disableEvalErrorMessage); 176 } 177 178 ContentSecurityPolicy::~ContentSecurityPolicy() 179 { 180 } 181 182 Document* ContentSecurityPolicy::document() const 183 { 184 return m_executionContext->isDocument() ? toDocument(m_executionContext) : 0; 185 } 186 187 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) 188 { 189 ASSERT(m_policies.isEmpty()); 190 for (CSPDirectiveListVector::const_iterator iter = other->m_policies.begin(); iter != other->m_policies.end(); ++iter) 191 addPolicyFromHeaderValue((*iter)->header(), (*iter)->headerType(), (*iter)->headerSource()); 192 } 193 194 void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers) 195 { 196 if (!headers.contentSecurityPolicy().isEmpty()) 197 addPolicyFromHeaderValue(headers.contentSecurityPolicy(), ContentSecurityPolicyHeaderTypeEnforce, ContentSecurityPolicyHeaderSourceHTTP); 198 if (!headers.contentSecurityPolicyReportOnly().isEmpty()) 199 addPolicyFromHeaderValue(headers.contentSecurityPolicyReportOnly(), ContentSecurityPolicyHeaderTypeReport, ContentSecurityPolicyHeaderSourceHTTP); 200 } 201 202 void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source) 203 { 204 addPolicyFromHeaderValue(header, type, source); 205 206 // This might be called after we've been bound to an execution context. For example, a <meta> 207 // element might be injected after page load. 208 if (m_executionContext) 209 applyPolicySideEffectsToExecutionContext(); 210 } 211 212 void ContentSecurityPolicy::addPolicyFromHeaderValue(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source) 213 { 214 // If this is a report-only header inside a <meta> element, bail out. 215 if (source == ContentSecurityPolicyHeaderSourceMeta && type == ContentSecurityPolicyHeaderTypeReport && experimentalFeaturesEnabled()) { 216 reportReportOnlyInMeta(header); 217 return; 218 } 219 220 Vector<UChar> characters; 221 header.appendTo(characters); 222 223 const UChar* begin = characters.data(); 224 const UChar* end = begin + characters.size(); 225 226 // RFC2616, section 4.2 specifies that headers appearing multiple times can 227 // be combined with a comma. Walk the header string, and parse each comma 228 // separated chunk as a separate header. 229 const UChar* position = begin; 230 while (position < end) { 231 skipUntil<UChar>(position, end, ','); 232 233 // header1,header2 OR header1 234 // ^ ^ 235 OwnPtr<CSPDirectiveList> policy = CSPDirectiveList::create(this, begin, position, type, source); 236 237 if (type != ContentSecurityPolicyHeaderTypeReport && policy->didSetReferrerPolicy()) { 238 // FIXME: We need a 'ReferrerPolicyUnset' enum to avoid confusing code like this. 239 m_referrerPolicy = didSetReferrerPolicy() ? mergeReferrerPolicies(m_referrerPolicy, policy->referrerPolicy()) : policy->referrerPolicy(); 240 } 241 242 if (!policy->allowEval(0, SuppressReport) && m_disableEvalErrorMessage.isNull()) 243 m_disableEvalErrorMessage = policy->evalDisabledErrorMessage(); 244 245 m_policies.append(policy.release()); 246 247 // Skip the comma, and begin the next header from the current position. 248 ASSERT(position == end || *position == ','); 249 skipExactly<UChar>(position, end, ','); 250 begin = position; 251 } 252 } 253 254 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value) 255 { 256 m_overrideInlineStyleAllowed = value; 257 } 258 259 void ContentSecurityPolicy::setOverrideURLForSelf(const KURL& url) 260 { 261 // Create a temporary CSPSource so that 'self' expressions can be resolved before we bind to 262 // an execution context (for 'frame-ancestor' resolution, for example). This CSPSource will 263 // be overwritten when we bind this object to an execution context. 264 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); 265 m_selfSource = adoptPtr(new CSPSource(this, origin->protocol(), origin->host(), origin->port(), String(), CSPSource::NoWildcard, CSPSource::NoWildcard)); 266 } 267 268 const String& ContentSecurityPolicy::deprecatedHeader() const 269 { 270 return m_policies.isEmpty() ? emptyString() : m_policies[0]->header(); 271 } 272 273 ContentSecurityPolicyHeaderType ContentSecurityPolicy::deprecatedHeaderType() const 274 { 275 return m_policies.isEmpty() ? ContentSecurityPolicyHeaderTypeEnforce : m_policies[0]->headerType(); 276 } 277 278 template<bool (CSPDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatus) const> 279 bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolicy::ReportingStatus reportingStatus) 280 { 281 for (size_t i = 0; i < policies.size(); ++i) { 282 if (!(policies[i].get()->*allowed)(reportingStatus)) 283 return false; 284 } 285 return true; 286 } 287 288 template<bool (CSPDirectiveList::*allowed)(ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus) const> 289 bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus reportingStatus) 290 { 291 for (size_t i = 0; i < policies.size(); ++i) { 292 if (!(policies[i].get()->*allowed)(scriptState, reportingStatus)) 293 return false; 294 } 295 return true; 296 } 297 298 template<bool (CSPDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const> 299 bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) 300 { 301 for (size_t i = 0; i < policies.size(); ++i) { 302 if (!(policies[i].get()->*allowed)(contextURL, contextLine, reportingStatus)) 303 return false; 304 } 305 return true; 306 } 307 308 template<bool (CSPDirectiveList::*allowed)(const String&) const> 309 bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce) 310 { 311 for (size_t i = 0; i < policies.size(); ++i) { 312 if (!(policies[i].get()->*allowed)(nonce)) 313 return false; 314 } 315 return true; 316 } 317 318 template<bool (CSPDirectiveList::*allowed)(const CSPHashValue&) const> 319 bool isAllowedByAllWithHash(const CSPDirectiveListVector& policies, const CSPHashValue& hashValue) 320 { 321 for (size_t i = 0; i < policies.size(); ++i) { 322 if (!(policies[i].get()->*allowed)(hashValue)) 323 return false; 324 } 325 return true; 326 } 327 328 template<bool (CSPDirectiveList::*allowFromURL)(const KURL&, ContentSecurityPolicy::ReportingStatus) const> 329 bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) 330 { 331 if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) 332 return true; 333 334 for (size_t i = 0; i < policies.size(); ++i) { 335 if (!(policies[i].get()->*allowFromURL)(url, reportingStatus)) 336 return false; 337 } 338 return true; 339 } 340 341 template<bool (CSPDirectiveList::*allowed)(LocalFrame*, const KURL&, ContentSecurityPolicy::ReportingStatus) const> 342 bool isAllowedByAllWithFrame(const CSPDirectiveListVector& policies, LocalFrame* frame, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) 343 { 344 for (size_t i = 0; i < policies.size(); ++i) { 345 if (!(policies[i].get()->*allowed)(frame, url, reportingStatus)) 346 return false; 347 } 348 return true; 349 } 350 351 template<bool (CSPDirectiveList::*allowed)(const CSPHashValue&) const> 352 bool checkDigest(const String& source, uint8_t hashAlgorithmsUsed, const CSPDirectiveListVector& policies) 353 { 354 // Any additions or subtractions from this struct should also modify the 355 // respective entries in the kSupportedPrefixes array in 356 // CSPSourceList::parseHash(). 357 static const struct { 358 ContentSecurityPolicyHashAlgorithm cspHashAlgorithm; 359 HashAlgorithm algorithm; 360 } kAlgorithmMap[] = { 361 { ContentSecurityPolicyHashAlgorithmSha1, HashAlgorithmSha1 }, 362 { ContentSecurityPolicyHashAlgorithmSha256, HashAlgorithmSha256 }, 363 { ContentSecurityPolicyHashAlgorithmSha384, HashAlgorithmSha384 }, 364 { ContentSecurityPolicyHashAlgorithmSha512, HashAlgorithmSha512 } 365 }; 366 367 // Only bother normalizing the source/computing digests if there are any checks to be done. 368 if (hashAlgorithmsUsed == ContentSecurityPolicyHashAlgorithmNone) 369 return false; 370 371 StringUTF8Adaptor normalizedSource(source, StringUTF8Adaptor::Normalize, WTF::EntitiesForUnencodables); 372 373 // See comment in CSPSourceList::parseHash about why we are using this sizeof 374 // calculation instead of WTF_ARRAY_LENGTH. 375 for (size_t i = 0; i < (sizeof(kAlgorithmMap) / sizeof(kAlgorithmMap[0])); i++) { 376 DigestValue digest; 377 if (kAlgorithmMap[i].cspHashAlgorithm & hashAlgorithmsUsed) { 378 bool digestSuccess = computeDigest(kAlgorithmMap[i].algorithm, normalizedSource.data(), normalizedSource.length(), digest); 379 if (digestSuccess && isAllowedByAllWithHash<allowed>(policies, CSPHashValue(kAlgorithmMap[i].cspHashAlgorithm, digest))) 380 return true; 381 } 382 } 383 384 return false; 385 } 386 387 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 388 { 389 return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus); 390 } 391 392 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 393 { 394 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus); 395 } 396 397 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 398 { 399 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus); 400 } 401 402 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 403 { 404 if (m_overrideInlineStyleAllowed) 405 return true; 406 return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus); 407 } 408 409 bool ContentSecurityPolicy::allowEval(ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus reportingStatus) const 410 { 411 return isAllowedByAllWithState<&CSPDirectiveList::allowEval>(m_policies, scriptState, reportingStatus); 412 } 413 414 String ContentSecurityPolicy::evalDisabledErrorMessage() const 415 { 416 for (size_t i = 0; i < m_policies.size(); ++i) { 417 if (!m_policies[i]->allowEval(0, SuppressReport)) 418 return m_policies[i]->evalDisabledErrorMessage(); 419 } 420 return String(); 421 } 422 423 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 424 { 425 for (size_t i = 0; i < m_policies.size(); ++i) { 426 if (!m_policies[i]->allowPluginType(type, typeAttribute, url, reportingStatus)) 427 return false; 428 } 429 return true; 430 } 431 432 bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 433 { 434 return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus); 435 } 436 437 bool ContentSecurityPolicy::allowScriptWithNonce(const String& nonce) const 438 { 439 return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce); 440 } 441 442 bool ContentSecurityPolicy::allowStyleWithNonce(const String& nonce) const 443 { 444 return isAllowedByAllWithNonce<&CSPDirectiveList::allowStyleNonce>(m_policies, nonce); 445 } 446 447 bool ContentSecurityPolicy::allowScriptWithHash(const String& source) const 448 { 449 return checkDigest<&CSPDirectiveList::allowScriptHash>(source, m_scriptHashAlgorithmsUsed, m_policies); 450 } 451 452 bool ContentSecurityPolicy::allowStyleWithHash(const String& source) const 453 { 454 return checkDigest<&CSPDirectiveList::allowStyleHash>(source, m_styleHashAlgorithmsUsed, m_policies); 455 } 456 457 void ContentSecurityPolicy::usesScriptHashAlgorithms(uint8_t algorithms) 458 { 459 m_scriptHashAlgorithmsUsed |= algorithms; 460 } 461 462 void ContentSecurityPolicy::usesStyleHashAlgorithms(uint8_t algorithms) 463 { 464 m_styleHashAlgorithmsUsed |= algorithms; 465 } 466 467 bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 468 { 469 return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus); 470 } 471 472 bool ContentSecurityPolicy::allowChildFrameFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 473 { 474 return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus); 475 } 476 477 bool ContentSecurityPolicy::allowImageFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 478 { 479 return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus); 480 } 481 482 bool ContentSecurityPolicy::allowStyleFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 483 { 484 return isAllowedByAllWithURL<&CSPDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus); 485 } 486 487 bool ContentSecurityPolicy::allowFontFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 488 { 489 return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus); 490 } 491 492 bool ContentSecurityPolicy::allowMediaFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 493 { 494 return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus); 495 } 496 497 bool ContentSecurityPolicy::allowConnectToSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 498 { 499 return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus); 500 } 501 502 bool ContentSecurityPolicy::allowFormAction(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 503 { 504 return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>(m_policies, url, reportingStatus); 505 } 506 507 bool ContentSecurityPolicy::allowBaseURI(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 508 { 509 return isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>(m_policies, url, reportingStatus); 510 } 511 512 bool ContentSecurityPolicy::allowAncestors(LocalFrame* frame, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 513 { 514 return isAllowedByAllWithFrame<&CSPDirectiveList::allowAncestors>(m_policies, frame, url, reportingStatus); 515 } 516 517 bool ContentSecurityPolicy::allowChildContextFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 518 { 519 return isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, reportingStatus); 520 } 521 522 bool ContentSecurityPolicy::allowWorkerContextFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 523 { 524 // CSP 1.1 moves workers from 'script-src' to the new 'child-src'. Measure the impact of this backwards-incompatible change. 525 if (Document* document = this->document()) { 526 UseCounter::count(*document, UseCounter::WorkerSubjectToCSP); 527 if (isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, SuppressReport) && !isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, SuppressReport)) 528 UseCounter::count(*document, UseCounter::WorkerAllowedByChildBlockedByScript); 529 } 530 531 return experimentalFeaturesEnabled() ? 532 isAllowedByAllWithURL<&CSPDirectiveList::allowChildContextFromSource>(m_policies, url, reportingStatus) : 533 isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus); 534 } 535 536 bool ContentSecurityPolicy::isActive() const 537 { 538 return !m_policies.isEmpty(); 539 } 540 541 ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const 542 { 543 ReflectedXSSDisposition disposition = ReflectedXSSUnset; 544 for (size_t i = 0; i < m_policies.size(); ++i) { 545 if (m_policies[i]->reflectedXSSDisposition() > disposition) 546 disposition = std::max(disposition, m_policies[i]->reflectedXSSDisposition()); 547 } 548 return disposition; 549 } 550 551 ReferrerPolicy ContentSecurityPolicy::referrerPolicy() const 552 { 553 ReferrerPolicy policy = ReferrerPolicyDefault; 554 bool first = true; 555 for (size_t i = 0; i < m_policies.size(); ++i) { 556 if (m_policies[i]->didSetReferrerPolicy()) { 557 if (first) 558 policy = m_policies[i]->referrerPolicy(); 559 else 560 policy = mergeReferrerPolicies(policy, m_policies[i]->referrerPolicy()); 561 } 562 } 563 return policy; 564 } 565 566 bool ContentSecurityPolicy::didSetReferrerPolicy() const 567 { 568 for (size_t i = 0; i < m_policies.size(); ++i) { 569 if (m_policies[i]->didSetReferrerPolicy()) 570 return true; 571 } 572 return false; 573 } 574 575 SecurityOrigin* ContentSecurityPolicy::securityOrigin() const 576 { 577 return m_executionContext->securityContext().securityOrigin(); 578 } 579 580 const KURL ContentSecurityPolicy::url() const 581 { 582 return m_executionContext->contextURL(); 583 } 584 585 KURL ContentSecurityPolicy::completeURL(const String& url) const 586 { 587 return m_executionContext->contextCompleteURL(url); 588 } 589 590 void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) 591 { 592 m_sandboxMask |= mask; 593 } 594 595 static String stripURLForUseInReport(Document* document, const KURL& url) 596 { 597 if (!url.isValid()) 598 return String(); 599 if (!url.isHierarchical() || url.protocolIs("file")) 600 return url.protocol(); 601 return document->securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url)->toString(); 602 } 603 604 static void gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventInit& init, Document* document, const String& directiveText, const String& effectiveDirective, const KURL& blockedURL, const String& header) 605 { 606 if (equalIgnoringCase(effectiveDirective, ContentSecurityPolicy::FrameAncestors)) { 607 // If this load was blocked via 'frame-ancestors', then the URL of |document| has not yet 608 // been initialized. In this case, we'll set both 'documentURI' and 'blockedURI' to the 609 // blocked document's URL. 610 init.documentURI = blockedURL.string(); 611 init.blockedURI = blockedURL.string(); 612 } else { 613 init.documentURI = document->url().string(); 614 init.blockedURI = stripURLForUseInReport(document, blockedURL); 615 } 616 init.referrer = document->referrer(); 617 init.violatedDirective = directiveText; 618 init.effectiveDirective = effectiveDirective; 619 init.originalPolicy = header; 620 init.sourceFile = String(); 621 init.lineNumber = 0; 622 init.columnNumber = 0; 623 init.statusCode = 0; 624 625 if (!SecurityOrigin::isSecure(document->url()) && document->loader()) 626 init.statusCode = document->loader()->response().httpStatusCode(); 627 628 RefPtrWillBeRawPtr<ScriptCallStack> stack = createScriptCallStack(1, false); 629 if (!stack) 630 return; 631 632 const ScriptCallFrame& callFrame = stack->at(0); 633 634 if (callFrame.lineNumber()) { 635 KURL source = KURL(ParsedURLString, callFrame.sourceURL()); 636 init.sourceFile = stripURLForUseInReport(document, source); 637 init.lineNumber = callFrame.lineNumber(); 638 init.columnNumber = callFrame.columnNumber(); 639 } 640 } 641 642 void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, const Vector<String>& reportEndpoints, const String& header, LocalFrame* contextFrame) 643 { 644 ASSERT((m_executionContext && !contextFrame) || (equalIgnoringCase(effectiveDirective, ContentSecurityPolicy::FrameAncestors) && contextFrame)); 645 646 // FIXME: Support sending reports from worker. 647 Document* document = contextFrame ? contextFrame->document() : this->document(); 648 if (!document) 649 return; 650 651 LocalFrame* frame = document->frame(); 652 if (!frame) 653 return; 654 655 SecurityPolicyViolationEventInit violationData; 656 gatherSecurityPolicyViolationEventData(violationData, document, directiveText, effectiveDirective, blockedURL, header); 657 658 if (experimentalFeaturesEnabled()) 659 frame->domWindow()->enqueueDocumentEvent(SecurityPolicyViolationEvent::create(EventTypeNames::securitypolicyviolation, violationData)); 660 661 if (reportEndpoints.isEmpty()) 662 return; 663 664 // We need to be careful here when deciding what information to send to the 665 // report-uri. Currently, we send only the current document's URL and the 666 // directive that was violated. The document's URL is safe to send because 667 // it's the document itself that's requesting that it be sent. You could 668 // make an argument that we shouldn't send HTTPS document URLs to HTTP 669 // report-uris (for the same reasons that we supress the Referer in that 670 // case), but the Referer is sent implicitly whereas this request is only 671 // sent explicitly. As for which directive was violated, that's pretty 672 // harmless information. 673 674 RefPtr<JSONObject> cspReport = JSONObject::create(); 675 cspReport->setString("document-uri", violationData.documentURI); 676 cspReport->setString("referrer", violationData.referrer); 677 cspReport->setString("violated-directive", violationData.violatedDirective); 678 if (experimentalFeaturesEnabled()) 679 cspReport->setString("effective-directive", violationData.effectiveDirective); 680 cspReport->setString("original-policy", violationData.originalPolicy); 681 cspReport->setString("blocked-uri", violationData.blockedURI); 682 if (!violationData.sourceFile.isEmpty() && violationData.lineNumber) { 683 cspReport->setString("source-file", violationData.sourceFile); 684 cspReport->setNumber("line-number", violationData.lineNumber); 685 cspReport->setNumber("column-number", violationData.columnNumber); 686 } 687 cspReport->setNumber("status-code", violationData.statusCode); 688 689 RefPtr<JSONObject> reportObject = JSONObject::create(); 690 reportObject->setObject("csp-report", cspReport.release()); 691 String stringifiedReport = reportObject->toJSONString(); 692 693 if (!shouldSendViolationReport(stringifiedReport)) 694 return; 695 696 RefPtr<FormData> report = FormData::create(stringifiedReport.utf8()); 697 698 for (size_t i = 0; i < reportEndpoints.size(); ++i) { 699 // If we have a context frame we're dealing with 'frame-ancestors' and we don't have our 700 // own execution context. Use the frame's document to complete the endpoint URL, overriding 701 // its URL with the blocked document's URL. 702 ASSERT(!contextFrame || !m_executionContext); 703 ASSERT(!contextFrame || equalIgnoringCase(effectiveDirective, FrameAncestors)); 704 KURL endpoint = contextFrame ? frame->document()->completeURLWithOverride(reportEndpoints[i], blockedURL) : completeURL(reportEndpoints[i]); 705 PingLoader::sendViolationReport(frame, completeURL(reportEndpoints[i]), report, PingLoader::ContentSecurityPolicyViolationReport); 706 } 707 708 didSendViolationReport(stringifiedReport); 709 } 710 711 void ContentSecurityPolicy::reportInvalidReferrer(const String& invalidValue) 712 { 713 logToConsole("The 'referrer' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Valid values are \"always\", \"default\", \"never\", and \"origin\"."); 714 } 715 716 void ContentSecurityPolicy::reportReportOnlyInMeta(const String& header) 717 { 718 logToConsole("The report-only Content Security Policy '" + header + "' was delivered via a <meta> element, which is disallowed. The policy has been ignored."); 719 } 720 721 void ContentSecurityPolicy::reportMetaOutsideHead(const String& header) 722 { 723 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."); 724 } 725 726 void ContentSecurityPolicy::reportInvalidInReportOnly(const String& name) 727 { 728 logToConsole("The Content Security Policy directive '" + name + "' is ignored when delivered in a report-only policy."); 729 } 730 731 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) 732 { 733 DEFINE_STATIC_LOCAL(String, allow, ("allow")); 734 DEFINE_STATIC_LOCAL(String, options, ("options")); 735 DEFINE_STATIC_LOCAL(String, policyURI, ("policy-uri")); 736 DEFINE_STATIC_LOCAL(String, allowMessage, ("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect.")); 737 DEFINE_STATIC_LOCAL(String, optionsMessage, ("The 'options' directive has been replaced with 'unsafe-inline' and 'unsafe-eval' source expressions for the 'script-src' and 'style-src' directives. Please use those directives instead, as 'options' has no effect.")); 738 DEFINE_STATIC_LOCAL(String, policyURIMessage, ("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header.")); 739 740 String message = "Unrecognized Content-Security-Policy directive '" + name + "'.\n"; 741 MessageLevel level = ErrorMessageLevel; 742 if (equalIgnoringCase(name, allow)) { 743 message = allowMessage; 744 } else if (equalIgnoringCase(name, options)) { 745 message = optionsMessage; 746 } else if (equalIgnoringCase(name, policyURI)) { 747 message = policyURIMessage; 748 } else if (isDirectiveName(name)) { 749 message = "The Content-Security-Policy directive '" + name + "' is implemented behind a flag which is currently disabled.\n"; 750 level = InfoMessageLevel; 751 } 752 753 logToConsole(message, level); 754 } 755 756 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) 757 { 758 String message = "The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?"; 759 logToConsole(message); 760 } 761 762 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) 763 { 764 String message = "Ignoring duplicate Content-Security-Policy directive '" + name + "'.\n"; 765 logToConsole(message); 766 } 767 768 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) 769 { 770 String message; 771 if (pluginType.isNull()) 772 message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n"; 773 else 774 message = "Invalid plugin type in 'plugin-types' Content Security Policy directive: '" + pluginType + "'.\n"; 775 logToConsole(message); 776 } 777 778 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) 779 { 780 logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags); 781 } 782 783 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) 784 { 785 logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Valid values are \"allow\", \"filter\", and \"block\"."); 786 } 787 788 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) 789 { 790 String message = "The value for Content Security Policy directive '" + directiveName + "' contains an invalid character: '" + value + "'. Non-whitespace characters outside ASCII 0x21-0x7E must be percent-encoded, as described in RFC 3986, section 2.1: http://tools.ietf.org/html/rfc3986#section-2.1."; 791 logToConsole(message); 792 } 793 794 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) 795 { 796 ASSERT(invalidChar == '#' || invalidChar == '?'); 797 798 String ignoring = "The fragment identifier, including the '#', will be ignored."; 799 if (invalidChar == '?') 800 ignoring = "The query component, including the '?', will be ignored."; 801 String message = "The source list for Content Security Policy directive '" + directiveName + "' contains a source with an invalid path: '" + value + "'. " + ignoring; 802 logToConsole(message); 803 } 804 805 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) 806 { 807 String message = "The source list for Content Security Policy directive '" + directiveName + "' contains an invalid source: '" + source + "'. It will be ignored."; 808 if (equalIgnoringCase(source, "'none'")) 809 message = message + " Note that 'none' has no effect unless it is the only expression in the source list."; 810 logToConsole(message); 811 } 812 813 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) 814 { 815 logToConsole("The Content Security Policy '" + policy + "' was delivered in report-only mode, but does not specify a 'report-uri'; the policy will have no effect. Please either add a 'report-uri' directive, or deliver the policy via the 'Content-Security-Policy' header."); 816 } 817 818 void ContentSecurityPolicy::logToConsole(const String& message, MessageLevel level) 819 { 820 logToConsole(ConsoleMessage::create(SecurityMessageSource, level, message)); 821 } 822 823 void ContentSecurityPolicy::logToConsole(PassRefPtrWillBeRawPtr<ConsoleMessage> consoleMessage, LocalFrame* frame) 824 { 825 if (frame) 826 frame->document()->addConsoleMessage(consoleMessage); 827 else if (m_executionContext) 828 m_executionContext->addConsoleMessage(consoleMessage); 829 else 830 m_consoleMessages.append(consoleMessage); 831 } 832 833 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const 834 { 835 m_executionContext->reportBlockedScriptExecutionToInspector(directiveText); 836 } 837 838 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const 839 { 840 return RuntimeEnabledFeatures::experimentalContentSecurityPolicyFeaturesEnabled(); 841 } 842 843 bool ContentSecurityPolicy::urlMatchesSelf(const KURL& url) const 844 { 845 return m_selfSource->matches(url); 846 } 847 848 bool ContentSecurityPolicy::protocolMatchesSelf(const KURL& url) const 849 { 850 String protectedResourceScheme(securityOrigin()->protocol()); 851 if (equalIgnoringCase("http", protectedResourceScheme)) 852 return url.protocolIsInHTTPFamily(); 853 return equalIgnoringCase(url.protocol(), protectedResourceScheme); 854 } 855 856 bool ContentSecurityPolicy::shouldBypassMainWorld(ExecutionContext* context) 857 { 858 if (context && context->isDocument()) { 859 Document* document = toDocument(context); 860 if (document->frame()) 861 return document->frame()->script().shouldBypassMainWorldCSP(); 862 } 863 return false; 864 } 865 866 bool ContentSecurityPolicy::shouldSendViolationReport(const String& report) const 867 { 868 // Collisions have no security impact, so we can save space by storing only the string's hash rather than the whole report. 869 return !m_violationReportsSent.contains(report.impl()->hash()); 870 } 871 872 void ContentSecurityPolicy::didSendViolationReport(const String& report) 873 { 874 m_violationReportsSent.add(report.impl()->hash()); 875 } 876 877 } // namespace blink 878