зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1434357: Exempt Web Extensions from insecure redirects to data: URIs. r=kmag,mayhemer
This commit is contained in:
Родитель
67731d41ae
Коммит
a6c1ffb498
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче