Bug 1004703 - ignore 'unsafe-inline' if nonce- or hash-source specified (r=sstamm)

This commit is contained in:
Christoph Kerschbaumer 2015-04-07 09:06:05 -07:00
parent 774b799f88
commit 07358f5241
5 changed files with 74 additions and 6 deletions

View File

@ -29,6 +29,13 @@ couldNotProcessUnknownDirective = Couldn't process unknown directive '%1$S'
# LOCALIZATION NOTE (ignoringUnknownOption):
# %1$S is the option that could not be understood
ignoringUnknownOption = Ignoring unknown option %1$S
# LOCALIZATION NOTE (ignoringDuplicateSrc):
# %1$S defines the duplicate src
ignoringDuplicateSrc = Ignoring duplicate source %1$S
# LOCALIZATION NOTE (ignoringSrcWithinScriptSrc):
# %1$S is the ignored src
# script-src is a directive name and should not be localized
ignoringSrcWithinScriptSrc = Ignoring "%1$S" within script-src: nonce-source or hash-source specified
# LOCALIZATION NOTE (reportURInotHttpsOrHttp2):
# %1$S is the ETLD of the report URI that is not HTTP or HTTPS
reportURInotHttpsOrHttp2 = The report URI (%1$S) should be an HTTP or HTTPS URI.

View File

@ -123,7 +123,9 @@ nsCSPTokenizer::tokenizeCSPPolicy(const nsAString &aPolicyString,
nsCSPParser::nsCSPParser(cspTokens& aTokens,
nsIURI* aSelfURI,
uint64_t aInnerWindowID)
: mTokens(aTokens)
: mHasHashOrNonce(false)
, mUnsafeInlineKeywordSrc(nullptr)
, mTokens(aTokens)
, mSelfURI(aSelfURI)
, mInnerWindowID(aInnerWindowID)
{
@ -570,8 +572,22 @@ nsCSPParser::keywordSource()
return CSP_CreateHostSrcFromURI(mSelfURI);
}
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE) ||
CSP_IsKeyword(mCurToken, CSP_UNSAFE_EVAL)) {
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE)) {
// make sure script-src only contains 'unsafe-inline' once;
// ignore duplicates and log warning
if (mUnsafeInlineKeywordSrc) {
const char16_t* params[] = { mCurToken.get() };
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringDuplicateSrc",
params, ArrayLength(params));
return nullptr;
}
// cache if we encounter 'unsafe-inline' so we can invalidate (ignore) it in
// case that script-src directive also contains hash- or nonce-.
mUnsafeInlineKeywordSrc = new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken));
return mUnsafeInlineKeywordSrc;
}
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_EVAL)) {
return new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken));
}
return nullptr;
@ -676,6 +692,8 @@ nsCSPParser::nonceSource()
if (dashIndex < 0) {
return nullptr;
}
// cache if encountering hash or nonce to invalidate unsafe-inline
mHasHashOrNonce = true;
return new nsCSPNonceSrc(Substring(expr,
dashIndex + 1,
expr.Length() - dashIndex + 1));
@ -708,6 +726,8 @@ nsCSPParser::hashSource()
for (uint32_t i = 0; i < kHashSourceValidFnsLen; i++) {
if (algo.LowerCaseEqualsASCII(kHashSourceValidFns[i])) {
// cache if encountering hash or nonce to invalidate unsafe-inline
mHasHashOrNonce = true;
return new nsCSPHashSrc(algo, hash);
}
}
@ -1003,6 +1023,11 @@ nsCSPParser::directive()
return;
}
// make sure to reset cache variables when trying to invalidate unsafe-inline;
// unsafe-inline might not only appear in script-src, but also in default-src
mHasHashOrNonce = false;
mUnsafeInlineKeywordSrc = nullptr;
// Try to parse all the srcs by handing the array off to directiveValue
nsTArray<nsCSPBaseSrc*> srcs;
directiveValue(srcs);
@ -1014,6 +1039,18 @@ nsCSPParser::directive()
srcs.AppendElement(keyword);
}
// if a hash or nonce is specified within script-src, then
// unsafe-inline should be ignored, see:
// http://www.w3.org/TR/CSP2/#directive-script-src
if (cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) &&
mHasHashOrNonce && mUnsafeInlineKeywordSrc) {
mUnsafeInlineKeywordSrc->invalidate();
// log to the console that unsafe-inline will be ignored
const char16_t* params[] = { NS_LITERAL_STRING("'unsafe-inline'").get() };
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcWithinScriptSrc",
params, ArrayLength(params));
}
// Add the newly created srcs to the directive and add the directive to the policy
cspDir->addSrcs(srcs);
mPolicy->addDirective(cspDir);

View File

@ -228,6 +228,10 @@ class nsCSPParser {
nsString mCurToken;
nsTArray<nsString> mCurDir;
// cache variables to ignore unsafe-inline if hash or nonce is specified
bool mHasHashOrNonce; // false, if no hash or nonce is defined
nsCSPKeywordSrc* mUnsafeInlineKeywordSrc; // null, otherwise invlidate()
cspTokens mTokens;
nsIURI* mSelfURI;
nsCSPPolicy* mPolicy;

View File

@ -540,10 +540,11 @@ nsCSPHostSrc::appendPath(const nsAString& aPath)
/* ===== nsCSPKeywordSrc ===================== */
nsCSPKeywordSrc::nsCSPKeywordSrc(enum CSPKeyword aKeyword)
: mKeyword(aKeyword)
, mInvalidated(false)
{
NS_ASSERTION((aKeyword != CSP_SELF),
"'self' should have been replaced in the parser");
mKeyword = aKeyword;
}
nsCSPKeywordSrc::~nsCSPKeywordSrc()
@ -553,8 +554,16 @@ nsCSPKeywordSrc::~nsCSPKeywordSrc()
bool
nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const
{
CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s, mInvalidated: %s",
CSP_EnumToKeyword(aKeyword),
NS_ConvertUTF16toUTF8(aHashOrNonce).get(),
mInvalidated ? "yes" : "false"));
// if unsafe-inline should be ignored, then bail early
if (mInvalidated) {
NS_ASSERTION(mKeyword == CSP_UNSAFE_INLINE,
"should only invalidate unsafe-inline within script-src");
return false;
}
return mKeyword == aKeyword;
}
@ -564,6 +573,14 @@ nsCSPKeywordSrc::toString(nsAString& outStr) const
outStr.AppendASCII(CSP_EnumToKeyword(mKeyword));
}
void
nsCSPKeywordSrc::invalidate()
{
mInvalidated = true;
NS_ASSERTION(mInvalidated == CSP_UNSAFE_INLINE,
"invalidate 'unsafe-inline' only within script-src");
}
/* ===== nsCSPNonceSrc ==================== */
nsCSPNonceSrc::nsCSPNonceSrc(const nsAString& aNonce)

View File

@ -220,9 +220,12 @@ class nsCSPKeywordSrc : public nsCSPBaseSrc {
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
void toString(nsAString& outStr) const;
void invalidate();
private:
CSPKeyword mKeyword;
// invalidate 'unsafe-inline' if nonce- or hash-source specified
bool mInvalidated;
};
/* =============== nsCSPNonceSource =========== */