Bug 1434357: Exempt Web Extensions from insecure redirects to data: URIs. r=kmag,mayhemer

This commit is contained in:
Christoph Kerschbaumer 2018-02-18 19:52:52 +01:00
Родитель 67731d41ae
Коммит a6c1ffb498
13 изменённых файлов: 113 добавлений и 32 удалений

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

@ -109,6 +109,55 @@ nsContentSecurityManager::AllowTopLevelNavigationToDataURI(nsIChannel* aChannel)
return false;
}
/* static */ bool
nsContentSecurityManager::AllowInsecureRedirectToDataURI(nsIChannel* aNewChannel)
{
nsCOMPtr<nsILoadInfo> loadInfo = aNewChannel->GetLoadInfo();
if (!loadInfo) {
return true;
}
if (loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_SCRIPT) {
return true;
}
nsCOMPtr<nsIURI> newURI;
nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
if (NS_FAILED(rv) || !newURI) {
return true;
}
bool isDataURI = (NS_SUCCEEDED(newURI->SchemeIs("data", &isDataURI)) && isDataURI);
if (!isDataURI) {
return true;
}
// Web Extensions are exempt from that restriction and are allowed to redirect
// a channel to a data: URI. When a web extension redirects a channel, we set
// a flag on the loadInfo which allows us to identify such redirects here.
if (loadInfo->GetAllowInsecureRedirectToDataURI()) {
return true;
}
nsAutoCString dataSpec;
newURI->GetSpec(dataSpec);
if (dataSpec.Length() > 50) {
dataSpec.Truncate(50);
dataSpec.AppendLiteral("...");
}
nsCOMPtr<nsIDocument> doc;
nsINode* node = loadInfo->LoadingNode();
if (node) {
doc = node->OwnerDoc();
}
NS_ConvertUTF8toUTF16 specUTF16(NS_UnescapeURL(dataSpec));
const char16_t* params[] = { specUTF16.get() };
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("DATA_URI_BLOCKED"),
doc,
nsContentUtils::eSECURITY_PROPERTIES,
"BlockSubresourceRedirectToData",
params, ArrayLength(params));
return false;
}
static nsresult
ValidateSecurityFlags(nsILoadInfo* aLoadInfo)
{
@ -608,33 +657,10 @@ nsContentSecurityManager::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
NS_ENSURE_STATE(oldPrincipal && newURI);
// Do not allow insecure redirects to data: URIs
if (loadInfo && loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_SCRIPT) {
bool isDataURI = (NS_SUCCEEDED(newURI->SchemeIs("data", &isDataURI)) && isDataURI);
if (isDataURI) {
nsAutoCString dataSpec;
newURI->GetSpec(dataSpec);
if (dataSpec.Length() > 50) {
dataSpec.Truncate(50);
dataSpec.AppendLiteral("...");
}
nsCOMPtr<nsIDocument> doc;
nsINode* node = loadInfo->LoadingNode();
if (node) {
doc = node->OwnerDoc();
}
NS_ConvertUTF8toUTF16 specUTF16(NS_UnescapeURL(dataSpec));
const char16_t* params[] = { specUTF16.get() };
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("DATA_URI_BLOCKED"),
doc,
nsContentUtils::eSECURITY_PROPERTIES,
"BlockSubresourceRedirectToData",
params, ArrayLength(params));
// cancel the old channel and return an error
aOldChannel->Cancel(NS_ERROR_CONTENT_BLOCKED);
return NS_ERROR_CONTENT_BLOCKED;
}
if (!AllowInsecureRedirectToDataURI(aNewChannel)) {
// cancel the old channel and return an error
aOldChannel->Cancel(NS_ERROR_CONTENT_BLOCKED);
return NS_ERROR_CONTENT_BLOCKED;
}
const uint32_t flags =

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

@ -34,6 +34,7 @@ public:
nsCOMPtr<nsIStreamListener>& aInAndOutListener);
static bool AllowTopLevelNavigationToDataURI(nsIChannel* aChannel);
static bool AllowInsecureRedirectToDataURI(nsIChannel* aNewChannel);
private:
static nsresult CheckChannel(nsIChannel* aChannel);

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

@ -407,6 +407,7 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
aLoadInfo->GetEnforceSRI(),
aLoadInfo->GetAllowDocumentToBeAgnosticToCSP(),
aLoadInfo->GetForceAllowDataURI(),
aLoadInfo->GetAllowInsecureRedirectToDataURI(),
aLoadInfo->GetForceInheritPrincipalDropped(),
aLoadInfo->GetInnerWindowID(),
aLoadInfo->GetOuterWindowID(),
@ -552,6 +553,7 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
loadInfoArgs.enforceSRI(),
loadInfoArgs.allowDocumentToBeAgnosticToCSP(),
loadInfoArgs.forceAllowDataURI(),
loadInfoArgs.allowInsecureRedirectToDataURI(),
loadInfoArgs.forceInheritPrincipalDropped(),
loadInfoArgs.innerWindowID(),
loadInfoArgs.outerWindowID(),

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

@ -67,6 +67,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
, mEnforceSRI(false)
, mAllowDocumentToBeAgnosticToCSP(false)
, mForceAllowDataURI(false)
, mAllowInsecureRedirectToDataURI(false)
, mOriginalFrameSrcLoad(false)
, mForceInheritPrincipalDropped(false)
, mInnerWindowID(0)
@ -272,6 +273,7 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
, mEnforceSRI(false)
, mAllowDocumentToBeAgnosticToCSP(false)
, mForceAllowDataURI(false)
, mAllowInsecureRedirectToDataURI(false)
, mOriginalFrameSrcLoad(false)
, mForceInheritPrincipalDropped(false)
, mInnerWindowID(0)
@ -347,6 +349,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
, mEnforceSRI(rhs.mEnforceSRI)
, mAllowDocumentToBeAgnosticToCSP(rhs.mAllowDocumentToBeAgnosticToCSP)
, mForceAllowDataURI(rhs.mForceAllowDataURI)
, mAllowInsecureRedirectToDataURI(rhs.mAllowInsecureRedirectToDataURI)
, mOriginalFrameSrcLoad(rhs.mOriginalFrameSrcLoad)
, mForceInheritPrincipalDropped(rhs.mForceInheritPrincipalDropped)
, mInnerWindowID(rhs.mInnerWindowID)
@ -389,6 +392,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
bool aEnforceSRI,
bool aAllowDocumentToBeAgnosticToCSP,
bool aForceAllowDataURI,
bool aAllowInsecureRedirectToDataURI,
bool aForceInheritPrincipalDropped,
uint64_t aInnerWindowID,
uint64_t aOuterWindowID,
@ -425,6 +429,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
, mEnforceSRI(aEnforceSRI)
, mAllowDocumentToBeAgnosticToCSP(aAllowDocumentToBeAgnosticToCSP)
, mForceAllowDataURI(aForceAllowDataURI)
, mAllowInsecureRedirectToDataURI(aAllowInsecureRedirectToDataURI)
, mOriginalFrameSrcLoad(false)
, mForceInheritPrincipalDropped(aForceInheritPrincipalDropped)
, mInnerWindowID(aInnerWindowID)
@ -824,6 +829,20 @@ LoadInfo::GetForceAllowDataURI(bool* aForceAllowDataURI)
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetAllowInsecureRedirectToDataURI(bool aAllowInsecureRedirectToDataURI)
{
mAllowInsecureRedirectToDataURI = aAllowInsecureRedirectToDataURI;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetAllowInsecureRedirectToDataURI(bool* aAllowInsecureRedirectToDataURI)
{
*aAllowInsecureRedirectToDataURI = mAllowInsecureRedirectToDataURI;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetOriginalFrameSrcLoad(bool aOriginalFrameSrcLoad)
{

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

@ -116,6 +116,7 @@ private:
bool aEnforceSRI,
bool aAllowDocumentToBeAgnosticToCSP,
bool aForceAllowDataURI,
bool aAllowInsecureRedirectToDataURI,
bool aForceInheritPrincipalDropped,
uint64_t aInnerWindowID,
uint64_t aOuterWindowID,
@ -180,6 +181,7 @@ private:
bool mEnforceSRI;
bool mAllowDocumentToBeAgnosticToCSP;
bool mForceAllowDataURI;
bool mAllowInsecureRedirectToDataURI;
bool mOriginalFrameSrcLoad;
bool mForceInheritPrincipalDropped;
uint64_t mInnerWindowID;

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

@ -529,6 +529,11 @@ interface nsILoadInfo : nsISupports
*/
[infallible] attribute boolean forceAllowDataURI;
/**
* If true, insecure redirects to a data: URI are allowed.
*/
[infallible] attribute boolean allowInsecureRedirectToDataURI;
/**
* If true, this is the load of a frame's original src attribute
*/

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

@ -51,6 +51,7 @@ struct LoadInfoArgs
bool enforceSRI;
bool allowDocumentToBeAgnosticToCSP;
bool forceAllowDataURI;
bool allowInsecureRedirectToDataURI;
bool forceInheritPrincipalDropped;
uint64_t innerWindowID;
uint64_t outerWindowID;

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

@ -2209,6 +2209,10 @@ HttpBaseChannel::RedirectTo(nsIURI *targetURI)
NS_ENSURE_FALSE(mOnStartRequestCalled, NS_ERROR_NOT_AVAILABLE);
mAPIRedirectToURI = targetURI;
// Only Web Extensions are allowed to redirect a channel to a data:
// URI. To avoid any bypasses after the channel was flagged by
// the WebRequst API, we are dropping the flag here.
mLoadInfo->SetAllowInsecureRedirectToDataURI(false);
return NS_OK;
}

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

@ -1650,6 +1650,7 @@ class Redirect1Event : public NeckoTargetChannelEvent<HttpChannelChild>
const uint32_t& registrarId,
const URIParams& newURI,
const uint32_t& redirectFlags,
const bool& allowInsecureRedirectToDataURI,
const nsHttpResponseHead& responseHead,
const nsACString& securityInfoSerialization,
const uint64_t& channelId)
@ -1657,6 +1658,7 @@ class Redirect1Event : public NeckoTargetChannelEvent<HttpChannelChild>
, mRegistrarId(registrarId)
, mNewURI(newURI)
, mRedirectFlags(redirectFlags)
, mAllowInsecureRedirectToDataURI(allowInsecureRedirectToDataURI)
, mResponseHead(responseHead)
, mSecurityInfoSerialization(securityInfoSerialization)
, mChannelId(channelId) {}
@ -1664,14 +1666,15 @@ class Redirect1Event : public NeckoTargetChannelEvent<HttpChannelChild>
void Run() override
{
mChild->Redirect1Begin(mRegistrarId, mNewURI, mRedirectFlags,
mResponseHead, mSecurityInfoSerialization,
mChannelId);
mAllowInsecureRedirectToDataURI, mResponseHead,
mSecurityInfoSerialization, mChannelId);
}
private:
uint32_t mRegistrarId;
URIParams mNewURI;
uint32_t mRedirectFlags;
bool mAllowInsecureRedirectToDataURI;
nsHttpResponseHead mResponseHead;
nsCString mSecurityInfoSerialization;
uint64_t mChannelId;
@ -1681,6 +1684,7 @@ mozilla::ipc::IPCResult
HttpChannelChild::RecvRedirect1Begin(const uint32_t& registrarId,
const URIParams& newUri,
const uint32_t& redirectFlags,
const bool& allowInsecureRedirectToDataURI,
const nsHttpResponseHead& responseHead,
const nsCString& securityInfoSerialization,
const uint64_t& channelId,
@ -1693,8 +1697,8 @@ HttpChannelChild::RecvRedirect1Begin(const uint32_t& registrarId,
mPeerAddr = oldPeerAddr;
mEventQ->RunOrEnqueue(new Redirect1Event(this, registrarId, newUri,
redirectFlags, responseHead,
securityInfoSerialization,
redirectFlags, allowInsecureRedirectToDataURI,
responseHead, securityInfoSerialization,
channelId));
return IPC_OK();
}
@ -1766,6 +1770,7 @@ void
HttpChannelChild::Redirect1Begin(const uint32_t& registrarId,
const URIParams& newOriginalURI,
const uint32_t& redirectFlags,
const bool& allowInsecureRedirectToDataURI,
const nsHttpResponseHead& responseHead,
const nsACString& securityInfoSerialization,
const uint64_t& channelId)
@ -1774,6 +1779,8 @@ HttpChannelChild::Redirect1Begin(const uint32_t& registrarId,
LOG(("HttpChannelChild::Redirect1Begin [this=%p]\n", this));
mLoadInfo->SetAllowInsecureRedirectToDataURI(allowInsecureRedirectToDataURI);
nsCOMPtr<nsIURI> uri = DeserializeURI(newOriginalURI);
if (!securityInfoSerialization.IsEmpty()) {

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

@ -151,6 +151,7 @@ protected:
mozilla::ipc::IPCResult RecvRedirect1Begin(const uint32_t& registrarId,
const URIParams& newURI,
const uint32_t& redirectFlags,
const bool& allowInsecureRedirectToDataURI,
const nsHttpResponseHead& responseHead,
const nsCString& securityInfoSerialization,
const uint64_t& channelId,
@ -434,6 +435,7 @@ private:
void Redirect1Begin(const uint32_t& registrarId,
const URIParams& newUri,
const uint32_t& redirectFlags,
const bool& allowInsecureRedirectToDataURI,
const nsHttpResponseHead& responseHead,
const nsACString& securityInfoSerialization,
const uint64_t& channelId);

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

@ -1905,11 +1905,13 @@ HttpChannelParent::StartRedirect(uint32_t registrarId,
rv = httpChannel->GetChannelId(&channelId);
NS_ENSURE_SUCCESS(rv, NS_BINDING_ABORTED);
}
nsCOMPtr<nsILoadInfo> loadInfo;
mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
nsHttpResponseHead *responseHead = mChannel->GetResponseHead();
bool result = false;
if (!mIPCClosed) {
result = SendRedirect1Begin(registrarId, uriParams, redirectFlags,
loadInfo->GetAllowInsecureRedirectToDataURI(),
responseHead ? *responseHead
: nsHttpResponseHead(),
secInfoSerialization,

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

@ -122,6 +122,7 @@ child:
async Redirect1Begin(uint32_t registrarId,
URIParams newOriginalUri,
uint32_t redirectFlags,
bool allowInsecureRedirectToDataURI,
nsHttpResponseHead responseHead,
nsCString securityInfoSerialization,
uint64_t channelId,

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

@ -834,6 +834,15 @@ HttpObserverManager = {
try {
channel.suspended = false;
channel.redirectTo(Services.io.newURI(result.redirectUrl));
// Web Extensions using the WebRequest API are allowed
// to redirect a channel to a data: URI, hence we mark
// the channel to let the redirect blocker know. Please
// note that this markind needs to happen after the
// channel.redirectTo is called because the channel's
// RedirectTo() implementation explicitly drops the flag
// to avoid additional redirects not caused by the
// Web Extension.
channel.loadInfo.allowInsecureRedirectToDataURI = true;
return;
} catch (e) {
Cu.reportError(e);