Issue #1975 - Implement Origin header CSRF mitigation.

Backported from Mozilla bug 446344.
This commit is contained in:
Job Bautista 2022-07-25 18:56:46 +08:00 committed by roytam1
commit faa778ec9d
6 changed files with 82 additions and 15 deletions

View file

@ -1491,6 +1491,10 @@ pref("network.http.referer.XOriginTrimmingPolicy", 0);
// 0=always send, 1=send iff base domains match, 2=send iff hosts match
pref("network.http.referer.XOriginPolicy", 0);
// Include an origin header on non-GET and non-HEAD requests regardless of CORS
// 0=never send, 1=send when same-origin only, 2=always send
pref("network.http.sendOriginHeader", 0);
// Controls whether referrer attributes in <a>, <img>, <area>, <iframe>, and <link> are honoured
pref("network.http.enablePerElementReferrer", true);

View file

@ -1398,22 +1398,10 @@ HttpBaseChannel::SetReferrerWithPolicy(nsIURI *referrer,
referrer = referrerGrip.get();
}
//
// block referrer if not on our white list...
//
static const char *const referrerWhiteList[] = {
"http",
"https",
"ftp",
nullptr
};
match = false;
const char *const *scheme = referrerWhiteList;
for (; *scheme && !match; ++scheme) {
rv = referrer->SchemeIs(*scheme, &match);
if (NS_FAILED(rv)) return rv;
// Enforce Referrer whitelist
if (!IsReferrerSchemeAllowed(referrer)) {
return NS_OK; // kick out....
}
if (!match) return NS_OK; // kick out....
//
// Handle secure referrals.
@ -2844,6 +2832,24 @@ HttpBaseChannel::AddCookiesToRequest()
SetRequestHeader(nsDependentCString(nsHttp::Cookie), cookie, false);
}
/* static */
bool
HttpBaseChannel::IsReferrerSchemeAllowed(nsIURI *aReferrer)
{
NS_ENSURE_TRUE(aReferrer, false);
nsAutoCString scheme;
nsresult rv = aReferrer->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, false);
if (scheme.EqualsIgnoreCase("https") ||
scheme.EqualsIgnoreCase("http") ||
scheme.EqualsIgnoreCase("ftp")) {
return true;
}
return false;
}
bool
HttpBaseChannel::ShouldRewriteRedirectToGET(uint32_t httpStatus,
nsHttpRequestHead::ParsedMethodType method)

View file

@ -319,6 +319,8 @@ public:
public: /* Necko internal use only... */
bool IsNavigation();
static bool IsReferrerSchemeAllowed(nsIURI *aReferrer);
// Return whether upon a redirect code of httpStatus for method, the
// request method should be rewritten to GET.
static bool ShouldRewriteRedirectToGET(uint32_t httpStatus,

View file

@ -64,6 +64,7 @@ HTTP_ATOM(Lock_Token, "Lock-Token")
HTTP_ATOM(Link, "Link")
HTTP_ATOM(Location, "Location")
HTTP_ATOM(Max_Forwards, "Max-Forwards")
HTTP_ATOM(Origin, "Origin")
HTTP_ATOM(Overwrite, "Overwrite")
HTTP_ATOM(Pragma, "Pragma")
HTTP_ATOM(Prefer, "Prefer")

View file

@ -5710,6 +5710,7 @@ nsHttpChannel::BeginConnect()
mRequestHead.SetHTTPS(isHttps);
mRequestHead.SetOrigin(scheme, host, port);
SetOriginHeader();
SetDoNotTrack();
NeckoOriginAttributes originAttributes;
@ -7997,6 +7998,58 @@ nsHttpChannel::ResumeInternal()
return NS_FAILED(rvTransaction) ? rvTransaction : rvCache;
}
// Step 10 of HTTP-network-or-cache fetch
void
nsHttpChannel::SetOriginHeader()
{
if (mRequestHead.IsGet() || mRequestHead.IsHead()) {
return;
}
nsAutoCString existingHeader;
Unused << mRequestHead.GetHeader(nsHttp::Origin, existingHeader);
if (!existingHeader.IsEmpty()) {
LOG(("nsHttpChannel::SetOriginHeader Origin header already present"));
return;
}
DebugOnly<nsresult> rv;
// Instead of consulting Preferences::GetInt() all the time we
// can cache the result to speed things up.
static int32_t sSendOriginHeader = 0;
static bool sIsInited = false;
if (!sIsInited) {
sIsInited = true;
Preferences::AddIntVarCache(&sSendOriginHeader,
"network.http.sendOriginHeader");
}
if (sSendOriginHeader == 0) {
// Origin header suppressed by user setting
return;
}
nsCOMPtr<nsIURI> referrer;
mLoadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(referrer));
nsAutoCString origin("null");
if (referrer && IsReferrerSchemeAllowed(referrer)) {
nsContentUtils::GetASCIIOrigin(referrer, origin);
}
// Restrict Origin to same-origin loads if requested by user
if (sSendOriginHeader == 1) {
nsAutoCString currentOrigin;
nsContentUtils::GetASCIIOrigin(mURI, currentOrigin);
if (!origin.EqualsIgnoreCase(currentOrigin.get())) {
// Origin header suppressed by user setting
return;
}
}
rv = mRequestHead.SetHeader(nsHttp::Origin, origin, false /* merge */);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
void
nsHttpChannel::SetDoNotTrack()
{

View file

@ -452,6 +452,7 @@ private:
void SetPushedStream(Http2PushedStreamWrapper *stream);
void SetOriginHeader();
void SetDoNotTrack();
private: