blob: 817dc1ef02660a8a4c3762127bc1a81d9baa96e4 [file] [log] [blame]
/*
* Copyright (C) 2011 Google, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ContentSecurityPolicy_h
#define ContentSecurityPolicy_h
#include <memory>
#include <utility>
#include "bindings/core/v8/SourceLocation.h"
#include "core/CoreExport.h"
#include "core/dom/ExecutionContext.h"
#include "core/dom/SecurityContext.h"
#include "core/inspector/ConsoleTypes.h"
#include "platform/bindings/ScriptState.h"
#include "platform/heap/Handle.h"
#include "platform/loader/fetch/Resource.h"
#include "platform/loader/fetch/ResourceRequest.h"
#include "platform/network/ContentSecurityPolicyParsers.h"
#include "platform/network/HTTPParsers.h"
#include "platform/weborigin/SchemeRegistry.h"
#include "platform/weborigin/SecurityViolationReportingPolicy.h"
#include "platform/wtf/HashSet.h"
#include "platform/wtf/Vector.h"
#include "platform/wtf/text/StringHash.h"
#include "platform/wtf/text/TextPosition.h"
#include "platform/wtf/text/WTFString.h"
#include "public/platform/WebInsecureRequestPolicy.h"
namespace WTF {
class OrdinalNumber;
}
namespace blink {
class ContentSecurityPolicyResponseHeaders;
class ConsoleMessage;
class CSPDirectiveList;
class CSPSource;
class Document;
class Element;
class LocalFrameClient;
class KURL;
class ResourceRequest;
class SecurityOrigin;
class SecurityPolicyViolationEventInit;
class SourceLocation;
typedef int SandboxFlags;
typedef HeapVector<Member<CSPDirectiveList>> CSPDirectiveListVector;
typedef HeapVector<Member<ConsoleMessage>> ConsoleMessageVector;
typedef std::pair<String, ContentSecurityPolicyHeaderType> CSPHeaderAndType;
using RedirectStatus = ResourceRequest::RedirectStatus;
class CORE_EXPORT ContentSecurityPolicy
: public GarbageCollectedFinalized<ContentSecurityPolicy> {
public:
enum ExceptionStatus { kWillThrowException, kWillNotThrowException };
// This covers the possible values of a violation's 'resource', as defined in
// https://w3c.github.io/webappsec-csp/#violation-resource. By the time we
// generate a report, we're guaranteed that the value isn't 'null', so we
// don't need that state in this enum.
enum ViolationType { kInlineViolation, kEvalViolation, kURLViolation };
enum class InlineType { kBlock, kAttribute };
enum class DirectiveType {
kUndefined,
kBaseURI,
kBlockAllMixedContent,
kChildSrc,
kConnectSrc,
kDefaultSrc,
kFrameAncestors,
kFrameSrc,
kFontSrc,
kFormAction,
kImgSrc,
kManifestSrc,
kMediaSrc,
kObjectSrc,
kPluginTypes,
kReportURI,
kRequireSRIFor,
kSandbox,
kScriptSrc,
kStyleSrc,
kTreatAsPublicAddress,
kUpgradeInsecureRequests,
kWorkerSrc,
};
// CheckHeaderType can be passed to Allow*FromSource methods to control which
// types of CSP headers are checked.
enum class CheckHeaderType {
// Check both Content-Security-Policy and
// Content-Security-Policy-Report-Only headers.
kCheckAll,
// Check Content-Security-Policy headers only and ignore
// Content-Security-Policy-Report-Only headers.
kCheckEnforce,
// Check Content-Security-Policy-Report-Only headers only and ignore
// Content-Security-Policy headers.
kCheckReportOnly
};
static ContentSecurityPolicy* Create() { return new ContentSecurityPolicy(); }
~ContentSecurityPolicy();
DECLARE_TRACE();
void BindToExecutionContext(ExecutionContext*);
void SetupSelf(const SecurityOrigin&);
void CopyStateFrom(const ContentSecurityPolicy*);
void CopyPluginTypesFrom(const ContentSecurityPolicy*);
void DidReceiveHeaders(const ContentSecurityPolicyResponseHeaders&);
void DidReceiveHeader(const String&,
ContentSecurityPolicyHeaderType,
ContentSecurityPolicyHeaderSource);
void AddPolicyFromHeaderValue(const String&,
ContentSecurityPolicyHeaderType,
ContentSecurityPolicyHeaderSource);
void ReportAccumulatedHeaders(LocalFrameClient*) const;
std::unique_ptr<Vector<CSPHeaderAndType>> Headers() const;
// |element| will not be present for navigations to javascript URLs,
// as those checks happen in the middle of the navigation algorithm,
// and we generally don't have access to the responsible element.
bool AllowJavaScriptURLs(Element*,
const String& source,
const String& context_url,
const WTF::OrdinalNumber& context_line,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport) const;
// |element| will be present almost all of the time, but because of
// strangeness around targeting handlers for '<body>', '<svg>', and
// '<frameset>', it will be 'nullptr' for handlers on those
// elements.
bool AllowInlineEventHandler(
Element*,
const String& source,
const String& context_url,
const WTF::OrdinalNumber& context_line,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport) const;
// When the reporting status is |SendReport|, the |ExceptionStatus|
// should indicate whether the caller will throw a JavaScript
// exception in the event of a violation. When the caller will throw
// an exception, ContentSecurityPolicy does not log a violation
// message to the console because it would be redundant.
bool AllowEval(ScriptState* = nullptr,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
ExceptionStatus = kWillNotThrowException) const;
bool AllowPluginType(const String& type,
const String& type_attribute,
const KURL&,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport) const;
// Checks whether the plugin type should be allowed in the given
// document; enforces the CSP rule that PluginDocuments inherit
// plugin-types directives from the parent document.
bool AllowPluginTypeForDocument(
const Document&,
const String& type,
const String& type_attribute,
const KURL&,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport) const;
bool AllowObjectFromSource(
const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowFrameFromSource(const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowImageFromSource(const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowFontFromSource(const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowMediaFromSource(const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowConnectToSource(const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowFormAction(const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowBaseURI(const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport) const;
bool AllowWorkerContextFromSource(
const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowManifestFromSource(
const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
// Passing 'String()' into the |nonce| arguments in the following methods
// represents an unnonced resource load.
bool AllowScriptFromSource(
const KURL&,
const String& nonce,
const IntegrityMetadataSet& hashes,
ParserDisposition,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowStyleFromSource(const KURL&,
const String& nonce,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowInlineScript(Element*,
const String& context_url,
const String& nonce,
const WTF::OrdinalNumber& context_line,
const String& script_content,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport) const;
bool AllowInlineStyle(Element*,
const String& context_url,
const String& nonce,
const WTF::OrdinalNumber& context_line,
const String& style_content,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport) const;
// |allowAncestors| does not need to know whether the resource was a
// result of a redirect. After a redirect, source paths are usually
// ignored to stop a page from learning the path to which the
// request was redirected, but this is not a concern for ancestors,
// because a child frame can't manipulate the URL of a cross-origin
// parent.
bool AllowAncestors(LocalFrame*,
const KURL&,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport) const;
bool IsFrameAncestorsEnforced() const;
// The hash allow functions are guaranteed to not have any side
// effects, including reporting.
// Hash functions check all policies relating to use of a script/style
// with the given hash and return true all CSP policies allow it.
// If these return true, callers can then process the content or
// issue a load and be safe disabling any further CSP checks.
//
// TODO(mkwst): Fold hashes into 'allow{Script,Style}' checks above, just
// as we've done with nonces. https://crbug.com/617065
bool AllowScriptWithHash(const String& source, InlineType) const;
bool AllowStyleWithHash(const String& source, InlineType) const;
bool AllowRequestWithoutIntegrity(
WebURLRequest::RequestContext,
const KURL&,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
bool AllowRequest(WebURLRequest::RequestContext,
const KURL&,
const String& nonce,
const IntegrityMetadataSet&,
ParserDisposition,
RedirectStatus = RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy =
SecurityViolationReportingPolicy::kReport,
CheckHeaderType = CheckHeaderType::kCheckAll) const;
void UsesScriptHashAlgorithms(uint8_t content_security_policy_hash_algorithm);
void UsesStyleHashAlgorithms(uint8_t content_security_policy_hash_algorithm);
void SetOverrideAllowInlineStyle(bool);
void SetOverrideURLForSelf(const KURL&);
bool IsActive() const;
// If a frame is passed in, the message will be logged to its active
// document's console. Otherwise, the message will be logged to this object's
// |m_executionContext|.
void LogToConsole(ConsoleMessage*, LocalFrame* = nullptr);
void ReportDirectiveAsSourceExpression(const String& directive_name,
const String& source_expression);
void ReportDuplicateDirective(const String&);
void ReportInvalidDirectiveValueCharacter(const String& directive_name,
const String& value);
void ReportInvalidPathCharacter(const String& directive_name,
const String& value,
const char);
void ReportInvalidPluginTypes(const String&);
void ReportInvalidRequireSRIForTokens(const String&);
void ReportInvalidSandboxFlags(const String&);
void ReportInvalidSourceExpression(const String& directive_name,
const String& source);
void ReportMissingReportURI(const String&);
void ReportUnsupportedDirective(const String&);
void ReportInvalidInReportOnly(const String&);
void ReportInvalidDirectiveInMeta(const String& directive_name);
void ReportReportOnlyInMeta(const String&);
void ReportMetaOutsideHead(const String&);
void ReportValueForEmptyDirective(const String& directive_name,
const String& value);
// If a frame is passed in, the report will be sent using it as a context. If
// no frame is passed in, the report will be sent via this object's
// |m_executionContext| (or dropped on the floor if no such context is
// available).
// If |sourceLocation| is not set, the source location will be the context's
// current location.
void ReportViolation(const String& directive_text,
const DirectiveType& effective_type,
const String& console_message,
const KURL& blocked_url,
const Vector<String>& report_endpoints,
const String& header,
ContentSecurityPolicyHeaderType,
ViolationType,
std::unique_ptr<SourceLocation>,
LocalFrame* = nullptr,
RedirectStatus = RedirectStatus::kFollowedRedirect,
Element* = nullptr,
const String& source = g_empty_string);
// Called when mixed content is detected on a page; will trigger a violation
// report if the 'block-all-mixed-content' directive is specified for a
// policy.
void ReportMixedContent(const KURL& mixed_url, RedirectStatus);
void ReportBlockedScriptExecutionToInspector(
const String& directive_text) const;
const KURL Url() const;
void EnforceSandboxFlags(SandboxFlags);
void TreatAsPublicAddress();
String EvalDisabledErrorMessage() const;
// Upgrade-Insecure-Requests and Block-All-Mixed-Content are represented in
// |m_insecureRequestPolicy|
void EnforceStrictMixedContentChecking();
void UpgradeInsecureRequests();
WebInsecureRequestPolicy GetInsecureRequestPolicy() const {
return insecure_request_policy_;
}
bool UrlMatchesSelf(const KURL&) const;
bool ProtocolEqualsSelf(const String&) const;
const String& GetSelfProtocol() const;
bool ExperimentalFeaturesEnabled() const;
bool ShouldSendCSPHeader(Resource::Type) const;
CSPSource* GetSelfSource() const { return self_source_; }
static bool ShouldBypassMainWorld(const ExecutionContext*);
static bool ShouldBypassContentSecurityPolicy(
const KURL&,
SchemeRegistry::PolicyAreas = SchemeRegistry::kPolicyAreaAll);
static bool IsNonceableElement(const Element*);
// This method checks whether the request should be allowed for an
// experimental EmbeddingCSP feature
// Please, see https://w3c.github.io/webappsec-csp/embedded/#origin-allowed.
static bool ShouldEnforceEmbeddersPolicy(const ResourceResponse&,
SecurityOrigin*);
static const char* GetDirectiveName(const DirectiveType&);
static DirectiveType GetDirectiveType(const String& name);
// This method checks if if this policy subsumes a given policy.
// Note the correct result is guaranteed if this policy contains only one
// CSPDirectiveList. More information here:
// https://w3c.github.io/webappsec-csp/embedded/#subsume-policy
bool Subsumes(const ContentSecurityPolicy&) const;
Document* GetDocument() const;
bool HasHeaderDeliveredPolicy() const { return header_delivered_; }
static bool IsValidCSPAttr(const String& attr);
private:
FRIEND_TEST_ALL_PREFIXES(ContentSecurityPolicyTest, NonceInline);
FRIEND_TEST_ALL_PREFIXES(ContentSecurityPolicyTest, NonceSinglePolicy);
FRIEND_TEST_ALL_PREFIXES(ContentSecurityPolicyTest, NonceMultiplePolicy);
FRIEND_TEST_ALL_PREFIXES(BaseFetchContextTest,
RedirectChecksReportedAndEnforcedCSP);
FRIEND_TEST_ALL_PREFIXES(BaseFetchContextTest,
AllowResponseChecksReportedAndEnforcedCSP);
FRIEND_TEST_ALL_PREFIXES(FrameFetchContextTest,
PopulateResourceRequestChecksReportOnlyCSP);
ContentSecurityPolicy();
void ApplyPolicySideEffectsToExecutionContext();
KURL CompleteURL(const String&) const;
void LogToConsole(const String& message, MessageLevel = kErrorMessageLevel);
void AddAndReportPolicyFromHeaderValue(const String&,
ContentSecurityPolicyHeaderType,
ContentSecurityPolicyHeaderSource);
bool ShouldSendViolationReport(const String&) const;
void DidSendViolationReport(const String&);
void DispatchViolationEvents(const SecurityPolicyViolationEventInit&,
Element*);
void PostViolationReport(const SecurityPolicyViolationEventInit&,
LocalFrame*,
const Vector<String>& report_endpoints);
Member<ExecutionContext> execution_context_;
bool override_inline_style_allowed_;
CSPDirectiveListVector policies_;
ConsoleMessageVector console_messages_;
bool header_delivered_{false};
HashSet<unsigned, AlreadyHashed> violation_reports_sent_;
// We put the hash functions used on the policy object so that we only need
// to calculate a hash once and then distribute it to all of the directives
// for validation.
uint8_t script_hash_algorithms_used_;
uint8_t style_hash_algorithms_used_;
// State flags used to configure the environment after parsing a policy.
SandboxFlags sandbox_mask_;
bool treat_as_public_address_;
String disable_eval_error_message_;
WebInsecureRequestPolicy insecure_request_policy_;
Member<CSPSource> self_source_;
String self_protocol_;
};
} // namespace blink
#endif