зеркало из https://github.com/mozilla/gecko-dev.git
Bug 671389 - Part 1: Implement CSP sandbox directive r=ckerschb,smaug
This commit is contained in:
Родитель
fb68322470
Коммит
53375db2ef
|
@ -1241,18 +1241,36 @@ nsContentUtils::GetParserService()
|
|||
return sParserService;
|
||||
}
|
||||
|
||||
static nsIAtom** sSandboxFlagAttrs[] = {
|
||||
&nsGkAtoms::allowsameorigin, // SANDBOXED_ORIGIN
|
||||
&nsGkAtoms::allowforms, // SANDBOXED_FORMS
|
||||
&nsGkAtoms::allowscripts, // SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES
|
||||
&nsGkAtoms::allowtopnavigation, // SANDBOXED_TOPLEVEL_NAVIGATION
|
||||
&nsGkAtoms::allowpointerlock, // SANDBOXED_POINTER_LOCK
|
||||
&nsGkAtoms::allowpopups // SANDBOXED_AUXILIARY_NAVIGATION
|
||||
};
|
||||
|
||||
static const uint32_t sSandboxFlagValues[] = {
|
||||
SANDBOXED_ORIGIN, // allow-same-origin
|
||||
SANDBOXED_FORMS, // allow-forms
|
||||
SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES, // allow-scripts
|
||||
SANDBOXED_TOPLEVEL_NAVIGATION, // allow-top-navigation
|
||||
SANDBOXED_POINTER_LOCK, // allow-pointer-lock
|
||||
SANDBOXED_AUXILIARY_NAVIGATION // allow-popups
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper function that parses a sandbox attribute (of an <iframe> or
|
||||
* a CSP directive) and converts it to the set of flags used internally.
|
||||
*
|
||||
* @param sandboxAttr the sandbox attribute
|
||||
* @return the set of flags (0 if sandboxAttr is null)
|
||||
* @param aSandboxAttr the sandbox attribute
|
||||
* @return the set of flags (SANDBOXED_NONE if aSandboxAttr is null)
|
||||
*/
|
||||
uint32_t
|
||||
nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr)
|
||||
nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* aSandboxAttr)
|
||||
{
|
||||
// No sandbox attribute, no sandbox flags.
|
||||
if (!sandboxAttr) { return 0; }
|
||||
if (!aSandboxAttr) { return SANDBOXED_NONE; }
|
||||
|
||||
// Start off by setting all the restriction flags.
|
||||
uint32_t out = SANDBOXED_NAVIGATION
|
||||
|
@ -1266,19 +1284,35 @@ nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr)
|
|||
| SANDBOXED_POINTER_LOCK
|
||||
| SANDBOXED_DOMAIN;
|
||||
|
||||
// Macro for updating the flag according to the keywords
|
||||
#define IF_KEYWORD(atom, flags) \
|
||||
if (sandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { out &= ~(flags); }
|
||||
MOZ_ASSERT(ArrayLength(sSandboxFlagAttrs) == ArrayLength(sSandboxFlagValues),
|
||||
"Lengths of SandboxFlagAttrs and SandboxFlagvalues do not match");
|
||||
|
||||
IF_KEYWORD(allowsameorigin, SANDBOXED_ORIGIN)
|
||||
IF_KEYWORD(allowforms, SANDBOXED_FORMS)
|
||||
IF_KEYWORD(allowscripts, SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES)
|
||||
IF_KEYWORD(allowtopnavigation, SANDBOXED_TOPLEVEL_NAVIGATION)
|
||||
IF_KEYWORD(allowpointerlock, SANDBOXED_POINTER_LOCK)
|
||||
IF_KEYWORD(allowpopups, SANDBOXED_AUXILIARY_NAVIGATION)
|
||||
// For each flag: if it's in the attribute, update the (out) flag
|
||||
for (uint32_t i = 0; i < ArrayLength(sSandboxFlagAttrs); i++) {
|
||||
if (aSandboxAttr->Contains(*sSandboxFlagAttrs[i], eIgnoreCase)) {
|
||||
out &= ~(sSandboxFlagValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
#undef IF_KEYWORD
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that checks if a string matches (case-insensitive) a valid
|
||||
* sandbox flag.
|
||||
*
|
||||
* @param aFlag the potential sandbox flag
|
||||
* @return true if the flag is a sandbox flag
|
||||
*/
|
||||
bool
|
||||
nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag)
|
||||
{
|
||||
for (uint32_t i = 0; i < ArrayLength(sSandboxFlagAttrs); i++) {
|
||||
if (EqualsIgnoreASCIICase(nsDependentAtomString(*sSandboxFlagAttrs[i]), aFlag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIBidiKeyboard*
|
||||
|
|
|
@ -832,10 +832,19 @@ public:
|
|||
* A helper function that parses a sandbox attribute (of an <iframe> or
|
||||
* a CSP directive) and converts it to the set of flags used internally.
|
||||
*
|
||||
* @param sandboxAttr the sandbox attribute
|
||||
* @return the set of flags (0 if sandboxAttr is null)
|
||||
* @param aSandboxAttr the sandbox attribute
|
||||
* @return the set of flags (SANDBOXED_NONE if aSandboxAttr is null)
|
||||
*/
|
||||
static uint32_t ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr);
|
||||
static uint32_t ParseSandboxAttributeToFlags(const nsAttrValue* aSandboxAttr);
|
||||
|
||||
/**
|
||||
* A helper function that checks if a string matches a valid sandbox
|
||||
* flag.
|
||||
*
|
||||
* @param aFlag the potential sandbox flag
|
||||
* @return true if the flag is a sandbox flag
|
||||
*/
|
||||
static bool IsValidSandboxFlag(const nsAString& aFlag);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -2864,7 +2864,7 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
|||
NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
|
||||
|
||||
// Figure out if we need to apply an app default CSP or a CSP from an app manifest
|
||||
nsIPrincipal* principal = NodePrincipal();
|
||||
nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
|
||||
|
||||
uint16_t appStatus = principal->GetAppStatus();
|
||||
bool applyAppDefaultCSP = false;
|
||||
|
@ -3036,11 +3036,30 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
|||
// speculative loads.
|
||||
}
|
||||
|
||||
// ----- Set sandbox flags according to CSP header
|
||||
// The document may already have some sandbox flags set (e.g., if the
|
||||
// document is an iframe with the sandbox attribute set). If we have a CSP
|
||||
// sandbox directive, intersect the CSP sandbox flags with the existing
|
||||
// flags. This corresponds to the _least_ permissive policy.
|
||||
uint32_t cspSandboxFlags = SANDBOXED_NONE;
|
||||
rv = csp->GetCSPSandboxFlags(&cspSandboxFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mSandboxFlags |= cspSandboxFlags;
|
||||
|
||||
if (cspSandboxFlags & SANDBOXED_ORIGIN) {
|
||||
// If the new CSP sandbox flags do not have the allow-same-origin flag
|
||||
// reset the document principal to a null principal
|
||||
principal = do_CreateInstance("@mozilla.org/nullprincipal;1");
|
||||
SetPrincipal(principal);
|
||||
}
|
||||
|
||||
|
||||
rv = principal->SetCsp(csp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Inserted CSP into principal %p", principal));
|
||||
("Inserted CSP into principal %p", principal.get()));
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
#ifndef nsSandboxFlags_h___
|
||||
#define nsSandboxFlags_h___
|
||||
|
||||
/**
|
||||
* This constant denotes the lack of a sandbox attribute/directive.
|
||||
*/
|
||||
const unsigned long SANDBOXED_NONE = 0x0;
|
||||
|
||||
/**
|
||||
* This flag prevents content from navigating browsing contexts other than
|
||||
* itself, browsing contexts nested inside it, the top-level browsing context
|
||||
|
|
|
@ -20,7 +20,7 @@ interface nsIURI;
|
|||
|
||||
typedef unsigned short CSPDirective;
|
||||
|
||||
[scriptable, uuid(68434447-b816-4473-a731-efc4f6d59902)]
|
||||
[scriptable, uuid(9454a677-5342-4220-8154-e619410e07e7)]
|
||||
interface nsIContentSecurityPolicy : nsISerializable
|
||||
{
|
||||
/**
|
||||
|
@ -48,6 +48,7 @@ interface nsIContentSecurityPolicy : nsISerializable
|
|||
const unsigned short BASE_URI_DIRECTIVE = 13;
|
||||
const unsigned short FORM_ACTION_DIRECTIVE = 14;
|
||||
const unsigned short REFERRER_DIRECTIVE = 15;
|
||||
const unsigned short SANDBOX_DIRECTIVE = 16;
|
||||
|
||||
/**
|
||||
* Accessor method for a read-only string version of the policy at a given
|
||||
|
@ -262,6 +263,17 @@ interface nsIContentSecurityPolicy : nsISerializable
|
|||
*/
|
||||
boolean permits(in nsIURI aURI, in CSPDirective aDir, in boolean aSpecific);
|
||||
|
||||
/**
|
||||
* Delegate method called by the service when the protected document is loaded.
|
||||
* Returns the intersection of all the sandbox flags contained in
|
||||
* CSP policies. This is the most restricting sandbox policy.
|
||||
* See nsSandboxFlags.h for the possible flags.
|
||||
*
|
||||
* @return
|
||||
* sandbox flags or SANDBOXED_NONE if no sandbox directive exists
|
||||
*/
|
||||
uint32_t getCSPSandboxFlags();
|
||||
|
||||
/**
|
||||
* Delegate method called by the service when sub-elements of the protected
|
||||
* document are being loaded. Given a bit of information about the request,
|
||||
|
|
|
@ -50,6 +50,9 @@ scriptFromStringBlocked = An attempt to call JavaScript from a string (by callin
|
|||
# LOCALIZATION NOTE (hostNameMightBeKeyword):
|
||||
# %1$S is the hostname in question and %2$S is the keyword
|
||||
hostNameMightBeKeyword = Interpreting %1$S as a hostname, not a keyword. If you intended this to be a keyword, use '%2$S' (wrapped in single quotes).
|
||||
# LOCALIZATION NOTE (ignoringReportOnlyDirective):
|
||||
# %1$S is the directive that is ignore in report-only mode.
|
||||
ignoringReportOnlyDirective = Ignoring sandbox directive when delivered in a report-only policy '%1$S'.
|
||||
# LOCALIZATION NOTE (notSupportingDirective):
|
||||
# directive is not supported (e.g. 'reflected-xss')
|
||||
notSupportingDirective = Not supporting directive '%1$S'. Directive and values will be ignored.
|
||||
|
@ -70,3 +73,6 @@ couldntParsePort = Couldn't parse port in %1$S
|
|||
# LOCALIZATION NOTE (duplicateDirective):
|
||||
# %1$S is the name of the duplicate directive
|
||||
duplicateDirective = Duplicate %1$S directives detected. All but the first instance will be ignored.
|
||||
# LOCALIZATION NOTE (couldntParseInvalidSandboxFlag):
|
||||
# %1$S is the option that could not be understood
|
||||
couldntParseInvalidSandboxFlag = Couldn't parse invalid sandbox flag %1$S
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "prlog.h"
|
||||
#include "mozilla/dom/CSPReportBinding.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
#include "nsSandboxFlags.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -1186,6 +1187,49 @@ nsCSPContext::Permits(nsIURI* aURI,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCSPContext::GetCSPSandboxFlags(uint32_t* aOutSandboxFlags)
|
||||
{
|
||||
if (aOutSandboxFlags == nullptr) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
*aOutSandboxFlags = SANDBOXED_NONE;
|
||||
|
||||
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
|
||||
uint32_t flags = mPolicies[i]->getSandboxFlags();
|
||||
|
||||
// current policy doesn't have sandbox flag, check next policy
|
||||
if (!flags) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// current policy has sandbox flags, if the policy is in
|
||||
// enforcement-mode (i.e., not report-only) set these flags
|
||||
// and check for policies with more restrictions
|
||||
if (!mPolicies[i]->getReportOnlyFlag()) {
|
||||
*aOutSandboxFlags |= flags;
|
||||
} else {
|
||||
// sandbox directive is ignored in report-only mode, warn about
|
||||
// it and continue the loop checking for an enforcement-mode policy
|
||||
nsAutoString policy;
|
||||
mPolicies[i]->toString(policy);
|
||||
|
||||
CSPCONTEXTLOG(("nsCSPContext::ShouldSandbox, report only policy, ignoring sandbox in: %s",
|
||||
policy.get()));
|
||||
|
||||
const char16_t* params[] = { policy.get() };
|
||||
CSP_LogLocalizedStr(MOZ_UTF16("ignoringReportOnlyDirective"),
|
||||
params, ArrayLength(params),
|
||||
EmptyString(),
|
||||
EmptyString(),
|
||||
0, 0,
|
||||
nsIScriptError::warningFlag,
|
||||
"CSP", mInnerWindowID);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* ========== CSPViolationReportListener implementation ========== */
|
||||
|
||||
NS_IMPL_ISUPPORTS(CSPViolationReportListener, nsIStreamListener, nsIRequestObserver, nsISupports);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -912,6 +913,39 @@ nsCSPParser::reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs)
|
|||
}
|
||||
}
|
||||
|
||||
/* Helper function for parsing sandbox flags. This function solely
|
||||
* concatenates all the source list tokens (the sandbox flags) so the
|
||||
* attribute parser (nsContentUtils::ParseSandboxAttributeToFlags) can
|
||||
* use them.
|
||||
*/
|
||||
void
|
||||
nsCSPParser::sandboxFlagList(nsTArray<nsCSPBaseSrc*>& outSrcs)
|
||||
{
|
||||
nsAutoString flags;
|
||||
|
||||
// remember, srcs start at index 1
|
||||
for (uint32_t i = 1; i < mCurDir.Length(); i++) {
|
||||
mCurToken = mCurDir[i];
|
||||
|
||||
CSPPARSERLOG(("nsCSPParser::sandboxFlagList, mCurToken: %s, mCurValue: %s",
|
||||
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
||||
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
||||
|
||||
if (!nsContentUtils::IsValidSandboxFlag(mCurToken)) {
|
||||
const char16_t* params[] = { mCurToken.get() };
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSandboxFlag",
|
||||
params, ArrayLength(params));
|
||||
continue;
|
||||
}
|
||||
flags.Append(mCurToken);
|
||||
if (i != mCurDir.Length() - 1) {
|
||||
flags.AppendASCII(" ");
|
||||
}
|
||||
}
|
||||
nsCSPSandboxFlags* sandboxFlags = new nsCSPSandboxFlags(flags);
|
||||
outSrcs.AppendElement(sandboxFlags);
|
||||
}
|
||||
|
||||
// directive-value = *( WSP / <VCHAR except ";" and ","> )
|
||||
void
|
||||
nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs)
|
||||
|
@ -933,6 +967,13 @@ nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs)
|
|||
return;
|
||||
}
|
||||
|
||||
// For the sandbox flag the source list is a list of flags, so we're
|
||||
// special casing this directive
|
||||
if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
|
||||
sandboxFlagList(outSrcs);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise just forward to sourceList
|
||||
sourceList(outSrcs);
|
||||
}
|
||||
|
|
|
@ -128,14 +128,15 @@ class nsCSPParser {
|
|||
bool port();
|
||||
bool path(nsCSPHostSrc* aCspHost);
|
||||
|
||||
bool subHost(); // helper function to parse subDomains
|
||||
bool atValidUnreservedChar(); // helper function to parse unreserved
|
||||
bool atValidSubDelimChar(); // helper function to parse sub-delims
|
||||
bool atValidPctEncodedChar(); // helper function to parse pct-encoded
|
||||
bool subPath(nsCSPHostSrc* aCspHost); // helper function to parse paths
|
||||
void reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse report-uris
|
||||
void percentDecodeStr(const nsAString& aEncStr, // helper function to percent-decode
|
||||
bool subHost(); // helper function to parse subDomains
|
||||
bool atValidUnreservedChar(); // helper function to parse unreserved
|
||||
bool atValidSubDelimChar(); // helper function to parse sub-delims
|
||||
bool atValidPctEncodedChar(); // helper function to parse pct-encoded
|
||||
bool subPath(nsCSPHostSrc* aCspHost); // helper function to parse paths
|
||||
void reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse report-uris
|
||||
void percentDecodeStr(const nsAString& aEncStr, // helper function to percent-decode
|
||||
nsAString& outDecStr);
|
||||
void sandboxFlagList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse sandbox flags
|
||||
|
||||
inline bool atEnd()
|
||||
{
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#include "nsIStringBundle.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsAttrValue.h"
|
||||
#include "nsSandboxFlags.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
static PRLogModuleInfo*
|
||||
|
@ -672,6 +675,23 @@ nsCSPReportURI::toString(nsAString& outStr) const
|
|||
outStr.AppendASCII(spec.get());
|
||||
}
|
||||
|
||||
/* ===== nsCSPSandboxFlags ===================== */
|
||||
|
||||
nsCSPSandboxFlags::nsCSPSandboxFlags(const nsAString& aFlags)
|
||||
: mFlags(aFlags)
|
||||
{
|
||||
}
|
||||
|
||||
nsCSPSandboxFlags::~nsCSPSandboxFlags()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPSandboxFlags::toString(nsAString& outStr) const
|
||||
{
|
||||
outStr.Append(mFlags);
|
||||
}
|
||||
|
||||
/* ===== nsCSPDirective ====================== */
|
||||
|
||||
nsCSPDirective::nsCSPDirective(CSPDirective aDirective)
|
||||
|
@ -974,3 +994,26 @@ nsCSPPolicy::getReportURIs(nsTArray<nsString>& outReportURIs) const
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function that returns the underlying bit representation of
|
||||
* sandbox flags. The function returns SANDBOXED_NONE if there is no
|
||||
* sandbox directives.
|
||||
*/
|
||||
uint32_t
|
||||
nsCSPPolicy::getSandboxFlags() const
|
||||
{
|
||||
nsAutoString flags;
|
||||
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
|
||||
if (mDirectives[i]->equals(nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
|
||||
flags.Truncate();
|
||||
mDirectives[i]->toString(flags);
|
||||
|
||||
nsAttrValue attr;
|
||||
attr.ParseAtomArray(flags);
|
||||
|
||||
return nsContentUtils::ParseSandboxAttributeToFlags(&attr);
|
||||
}
|
||||
}
|
||||
return SANDBOXED_NONE;
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ static const char* CSPStrDirectives[] = {
|
|||
"reflected-xss", // REFLECTED_XSS_DIRECTIVE
|
||||
"base-uri", // BASE_URI_DIRECTIVE
|
||||
"form-action", // FORM_ACTION_DIRECTIVE
|
||||
"referrer" // REFERRER_DIRECTIVE
|
||||
"referrer", // REFERRER_DIRECTIVE
|
||||
"sandbox", // SANDBOX_DIRECTIVE
|
||||
};
|
||||
|
||||
inline const char* CSP_CSPDirectiveToString(CSPDirective aDir)
|
||||
|
@ -268,6 +269,19 @@ class nsCSPReportURI : public nsCSPBaseSrc {
|
|||
nsCOMPtr<nsIURI> mReportURI;
|
||||
};
|
||||
|
||||
/* =============== nsCSPSandboxFlag ============ */
|
||||
|
||||
class nsCSPSandboxFlags : public nsCSPBaseSrc {
|
||||
public:
|
||||
explicit nsCSPSandboxFlags(const nsAString& aFlags);
|
||||
virtual ~nsCSPSandboxFlags();
|
||||
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
private:
|
||||
nsString mFlags;
|
||||
};
|
||||
|
||||
/* =============== nsCSPDirective ============= */
|
||||
|
||||
class nsCSPDirective {
|
||||
|
@ -349,6 +363,8 @@ class nsCSPPolicy {
|
|||
inline uint32_t getNumDirectives() const
|
||||
{ return mDirectives.Length(); }
|
||||
|
||||
uint32_t getSandboxFlags() const;
|
||||
|
||||
private:
|
||||
nsTArray<nsCSPDirective*> mDirectives;
|
||||
bool mReportOnly;
|
||||
|
|
Загрузка…
Ссылка в новой задаче