Bug 671389 - Part 1: Implement CSP sandbox directive r=ckerschb,smaug

This commit is contained in:
Deian Stefan 2015-02-03 23:40:00 +01:00
Родитель fb68322470
Коммит 53375db2ef
11 изменённых файлов: 258 добавлений и 28 удалений

Просмотреть файл

@ -1241,18 +1241,36 @@ nsContentUtils::GetParserService()
return sParserService; 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 helper function that parses a sandbox attribute (of an <iframe> or
* a CSP directive) and converts it to the set of flags used internally. * a CSP directive) and converts it to the set of flags used internally.
* *
* @param sandboxAttr the sandbox attribute * @param aSandboxAttr the sandbox attribute
* @return the set of flags (0 if sandboxAttr is null) * @return the set of flags (SANDBOXED_NONE if aSandboxAttr is null)
*/ */
uint32_t uint32_t
nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr) nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* aSandboxAttr)
{ {
// No sandbox attribute, no sandbox flags. // No sandbox attribute, no sandbox flags.
if (!sandboxAttr) { return 0; } if (!aSandboxAttr) { return SANDBOXED_NONE; }
// Start off by setting all the restriction flags. // Start off by setting all the restriction flags.
uint32_t out = SANDBOXED_NAVIGATION uint32_t out = SANDBOXED_NAVIGATION
@ -1266,19 +1284,35 @@ nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr)
| SANDBOXED_POINTER_LOCK | SANDBOXED_POINTER_LOCK
| SANDBOXED_DOMAIN; | SANDBOXED_DOMAIN;
// Macro for updating the flag according to the keywords MOZ_ASSERT(ArrayLength(sSandboxFlagAttrs) == ArrayLength(sSandboxFlagValues),
#define IF_KEYWORD(atom, flags) \ "Lengths of SandboxFlagAttrs and SandboxFlagvalues do not match");
if (sandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { out &= ~(flags); }
IF_KEYWORD(allowsameorigin, SANDBOXED_ORIGIN) // For each flag: if it's in the attribute, update the (out) flag
IF_KEYWORD(allowforms, SANDBOXED_FORMS) for (uint32_t i = 0; i < ArrayLength(sSandboxFlagAttrs); i++) {
IF_KEYWORD(allowscripts, SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES) if (aSandboxAttr->Contains(*sSandboxFlagAttrs[i], eIgnoreCase)) {
IF_KEYWORD(allowtopnavigation, SANDBOXED_TOPLEVEL_NAVIGATION) out &= ~(sSandboxFlagValues[i]);
IF_KEYWORD(allowpointerlock, SANDBOXED_POINTER_LOCK) }
IF_KEYWORD(allowpopups, SANDBOXED_AUXILIARY_NAVIGATION) }
return out; 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* nsIBidiKeyboard*

Просмотреть файл

@ -832,10 +832,19 @@ public:
* A helper function that parses a sandbox attribute (of an <iframe> or * 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. * a CSP directive) and converts it to the set of flags used internally.
* *
* @param sandboxAttr the sandbox attribute * @param aSandboxAttr the sandbox attribute
* @return the set of flags (0 if sandboxAttr is null) * @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); NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
// Figure out if we need to apply an app default CSP or a CSP from an app manifest // 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(); uint16_t appStatus = principal->GetAppStatus();
bool applyAppDefaultCSP = false; bool applyAppDefaultCSP = false;
@ -3036,11 +3036,30 @@ nsDocument::InitCSP(nsIChannel* aChannel)
// speculative loads. // 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); rv = principal->SetCsp(csp);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
#ifdef PR_LOGGING #ifdef PR_LOGGING
PR_LOG(gCspPRLog, PR_LOG_DEBUG, PR_LOG(gCspPRLog, PR_LOG_DEBUG,
("Inserted CSP into principal %p", principal)); ("Inserted CSP into principal %p", principal.get()));
#endif #endif
return NS_OK; return NS_OK;

Просмотреть файл

@ -11,6 +11,11 @@
#ifndef nsSandboxFlags_h___ #ifndef nsSandboxFlags_h___
#define 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 * This flag prevents content from navigating browsing contexts other than
* itself, browsing contexts nested inside it, the top-level browsing context * itself, browsing contexts nested inside it, the top-level browsing context

Просмотреть файл

@ -20,7 +20,7 @@ interface nsIURI;
typedef unsigned short CSPDirective; typedef unsigned short CSPDirective;
[scriptable, uuid(68434447-b816-4473-a731-efc4f6d59902)] [scriptable, uuid(9454a677-5342-4220-8154-e619410e07e7)]
interface nsIContentSecurityPolicy : nsISerializable interface nsIContentSecurityPolicy : nsISerializable
{ {
/** /**
@ -48,6 +48,7 @@ interface nsIContentSecurityPolicy : nsISerializable
const unsigned short BASE_URI_DIRECTIVE = 13; const unsigned short BASE_URI_DIRECTIVE = 13;
const unsigned short FORM_ACTION_DIRECTIVE = 14; const unsigned short FORM_ACTION_DIRECTIVE = 14;
const unsigned short REFERRER_DIRECTIVE = 15; 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 * 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); 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 * Delegate method called by the service when sub-elements of the protected
* document are being loaded. Given a bit of information about the request, * 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): # LOCALIZATION NOTE (hostNameMightBeKeyword):
# %1$S is the hostname in question and %2$S is the keyword # %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). 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): # LOCALIZATION NOTE (notSupportingDirective):
# directive is not supported (e.g. 'reflected-xss') # directive is not supported (e.g. 'reflected-xss')
notSupportingDirective = Not supporting directive '%1$S'. Directive and values will be ignored. 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): # LOCALIZATION NOTE (duplicateDirective):
# %1$S is the name of the duplicate directive # %1$S is the name of the duplicate directive
duplicateDirective = Duplicate %1$S directives detected. All but the first instance will be ignored. 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 "prlog.h"
#include "mozilla/dom/CSPReportBinding.h" #include "mozilla/dom/CSPReportBinding.h"
#include "mozilla/net/ReferrerPolicy.h" #include "mozilla/net/ReferrerPolicy.h"
#include "nsSandboxFlags.h"
using namespace mozilla; using namespace mozilla;
@ -1186,6 +1187,49 @@ nsCSPContext::Permits(nsIURI* aURI,
return NS_OK; 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 ========== */ /* ========== CSPViolationReportListener implementation ========== */
NS_IMPL_ISUPPORTS(CSPViolationReportListener, nsIStreamListener, nsIRequestObserver, nsISupports); NS_IMPL_ISUPPORTS(CSPViolationReportListener, nsIStreamListener, nsIRequestObserver, nsISupports);

Просмотреть файл

@ -15,6 +15,7 @@
#include "nsServiceManagerUtils.h" #include "nsServiceManagerUtils.h"
#include "nsUnicharUtils.h" #include "nsUnicharUtils.h"
#include "mozilla/net/ReferrerPolicy.h" #include "mozilla/net/ReferrerPolicy.h"
#include "nsContentUtils.h"
using namespace mozilla; 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 ","> ) // directive-value = *( WSP / <VCHAR except ";" and ","> )
void void
nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs) nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs)
@ -933,6 +967,13 @@ nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs)
return; 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 // Otherwise just forward to sourceList
sourceList(outSrcs); sourceList(outSrcs);
} }

Просмотреть файл

@ -128,14 +128,15 @@ class nsCSPParser {
bool port(); bool port();
bool path(nsCSPHostSrc* aCspHost); bool path(nsCSPHostSrc* aCspHost);
bool subHost(); // helper function to parse subDomains bool subHost(); // helper function to parse subDomains
bool atValidUnreservedChar(); // helper function to parse unreserved bool atValidUnreservedChar(); // helper function to parse unreserved
bool atValidSubDelimChar(); // helper function to parse sub-delims bool atValidSubDelimChar(); // helper function to parse sub-delims
bool atValidPctEncodedChar(); // helper function to parse pct-encoded bool atValidPctEncodedChar(); // helper function to parse pct-encoded
bool subPath(nsCSPHostSrc* aCspHost); // helper function to parse paths bool subPath(nsCSPHostSrc* aCspHost); // helper function to parse paths
void reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse report-uris void reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse report-uris
void percentDecodeStr(const nsAString& aEncStr, // helper function to percent-decode void percentDecodeStr(const nsAString& aEncStr, // helper function to percent-decode
nsAString& outDecStr); nsAString& outDecStr);
void sandboxFlagList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse sandbox flags
inline bool atEnd() inline bool atEnd()
{ {

Просмотреть файл

@ -12,6 +12,9 @@
#include "nsIStringBundle.h" #include "nsIStringBundle.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "nsReadableUtils.h" #include "nsReadableUtils.h"
#include "nsContentUtils.h"
#include "nsAttrValue.h"
#include "nsSandboxFlags.h"
#if defined(PR_LOGGING) #if defined(PR_LOGGING)
static PRLogModuleInfo* static PRLogModuleInfo*
@ -672,6 +675,23 @@ nsCSPReportURI::toString(nsAString& outStr) const
outStr.AppendASCII(spec.get()); 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::nsCSPDirective(CSPDirective aDirective) 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 "reflected-xss", // REFLECTED_XSS_DIRECTIVE
"base-uri", // BASE_URI_DIRECTIVE "base-uri", // BASE_URI_DIRECTIVE
"form-action", // FORM_ACTION_DIRECTIVE "form-action", // FORM_ACTION_DIRECTIVE
"referrer" // REFERRER_DIRECTIVE "referrer", // REFERRER_DIRECTIVE
"sandbox", // SANDBOX_DIRECTIVE
}; };
inline const char* CSP_CSPDirectiveToString(CSPDirective aDir) inline const char* CSP_CSPDirectiveToString(CSPDirective aDir)
@ -268,6 +269,19 @@ class nsCSPReportURI : public nsCSPBaseSrc {
nsCOMPtr<nsIURI> mReportURI; 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 ============= */ /* =============== nsCSPDirective ============= */
class nsCSPDirective { class nsCSPDirective {
@ -349,6 +363,8 @@ class nsCSPPolicy {
inline uint32_t getNumDirectives() const inline uint32_t getNumDirectives() const
{ return mDirectives.Length(); } { return mDirectives.Length(); }
uint32_t getSandboxFlags() const;
private: private:
nsTArray<nsCSPDirective*> mDirectives; nsTArray<nsCSPDirective*> mDirectives;
bool mReportOnly; bool mReportOnly;