зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1557346 - Limit referer header length r=ckerschb
Differential Revision: https://phabricator.services.mozilla.com/D35990 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
06b684f035
Коммит
2beb089527
|
@ -120,3 +120,8 @@ FeaturePolicyUnsupportedFeatureName=Feature Policy: Skipping unsupported feature
|
|||
FeaturePolicyInvalidEmptyAllowValue= Feature Policy: Skipping empty allow list for feature: “%S”.
|
||||
# TODO: would be nice to add a link to the Feature-Policy MDN documentation here. See bug 1449501
|
||||
FeaturePolicyInvalidAllowValue=Feature Policy: Skipping unsupported allow value “%S”.
|
||||
|
||||
# LOCALIZATION NOTE: "%1$S" is the limitation length (bytes) of referrer URI, "%2$S" is the origin of the referrer URI.
|
||||
ReferrerLengthOverLimitation=HTTP Referrer header: Length is over “%1$S” bytes limit - stripping referrer header down to origin: “%2$S”
|
||||
# LOCALIZATION NOTE: "%1$S" is the limitation length (bytes) of referrer URI, "%2$S" is the origin of the referrer URI.
|
||||
ReferrerOriginLengthOverLimitation=HTTP Referrer header: Length of origin within referrer is over “%1$S” bytes limit - removing referrer with origin “%2$S”.
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "nsIObjectOutputStream.h"
|
||||
#include "nsIURIFixup.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsIURIMutator.h"
|
||||
|
||||
#include "nsWhitespaceTokenizer.h"
|
||||
#include "nsAlgorithm.h"
|
||||
|
@ -43,6 +42,8 @@ NS_IMPL_ISUPPORTS_CI(ReferrerInfo, nsIReferrerInfo, nsISerializable)
|
|||
#define DEFAULT_PRIVATE_RP 2
|
||||
#define DEFAULT_TRACKER_PRIVATE_RP 2
|
||||
|
||||
#define DEFAULT_REFERRER_HEADER_LENGTH_LIMIT 4096
|
||||
|
||||
#define MAX_REFERRER_SENDING_POLICY 2
|
||||
#define MAX_CROSS_ORIGIN_SENDING_POLICY 2
|
||||
#define MAX_TRIMMING_POLICY 2
|
||||
|
@ -62,6 +63,7 @@ static uint32_t sUserReferrerSendingPolicy = 0;
|
|||
static uint32_t sUserXOriginSendingPolicy = 0;
|
||||
static uint32_t sUserTrimmingPolicy = 0;
|
||||
static uint32_t sUserXOriginTrimmingPolicy = 0;
|
||||
static uint32_t sReferrerHeaderLimit = DEFAULT_REFERRER_HEADER_LENGTH_LIMIT;
|
||||
|
||||
static void CachePreferrenceValue() {
|
||||
static bool sPrefCached = false;
|
||||
|
@ -75,6 +77,9 @@ static void CachePreferrenceValue() {
|
|||
"network.http.referer.hideOnionSource");
|
||||
Preferences::AddUintVarCache(&sUserReferrerSendingPolicy,
|
||||
"network.http.sendRefererHeader");
|
||||
Preferences::AddUintVarCache(&sReferrerHeaderLimit,
|
||||
"network.http.referer.referrerLengthLimit",
|
||||
DEFAULT_REFERRER_HEADER_LENGTH_LIMIT);
|
||||
sUserReferrerSendingPolicy =
|
||||
clamped<uint32_t>(sUserReferrerSendingPolicy, MIN_REFERRER_SENDING_POLICY,
|
||||
MAX_REFERRER_SENDING_POLICY);
|
||||
|
@ -482,23 +487,64 @@ ReferrerInfo::TrimmingPolicy ReferrerInfo::ComputeTrimmingPolicy(
|
|||
return static_cast<TrimmingPolicy>(trimmingPolicy);
|
||||
}
|
||||
|
||||
nsresult ReferrerInfo::TrimReferrerWithPolicy(nsCOMPtr<nsIURI>& aReferrer,
|
||||
TrimmingPolicy aTrimmingPolicy,
|
||||
nsACString& aResult) const {
|
||||
if (aTrimmingPolicy == TrimmingPolicy::ePolicyFullURI) {
|
||||
// use the full URI
|
||||
return aReferrer->GetAsciiSpec(aResult);
|
||||
nsresult ReferrerInfo::LimitReferrerLength(
|
||||
nsIHttpChannel* aChannel, nsIURI* aReferrer, TrimmingPolicy aTrimmingPolicy,
|
||||
nsACString& aInAndOutTrimmedReferrer) const {
|
||||
if (!sReferrerHeaderLimit) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// All output strings start with: scheme+host+port
|
||||
if (aInAndOutTrimmedReferrer.Length() <= sReferrerHeaderLimit) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString referrerLengthLimit;
|
||||
referrerLengthLimit.AppendInt(sReferrerHeaderLimit);
|
||||
if (aTrimmingPolicy == ePolicyFullURI ||
|
||||
aTrimmingPolicy == ePolicySchemeHostPortPath) {
|
||||
// If referrer header is over max Length, down to origin
|
||||
nsresult rv = GetOriginFromReferrerURI(aReferrer, aInAndOutTrimmedReferrer);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Step 6 within https://w3c.github.io/webappsec-referrer-policy/#strip-url
|
||||
// states that the trailing "/" does not need to get stripped. However,
|
||||
// GetOriginFromReferrerURI() also removes any trailing "/" hence we have to
|
||||
// add it back here.
|
||||
aInAndOutTrimmedReferrer.AppendLiteral("/");
|
||||
if (aInAndOutTrimmedReferrer.Length() <= sReferrerHeaderLimit) {
|
||||
AutoTArray<nsString, 2> params = {
|
||||
referrerLengthLimit, NS_ConvertUTF8toUTF16(aInAndOutTrimmedReferrer)};
|
||||
LogMessageToConsole(aChannel, "ReferrerLengthOverLimitation", params);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If we end up here either the trimmingPolicy is equal to
|
||||
// 'ePolicySchemeHostPort' or the 'origin' of any other policy is still over
|
||||
// the length limit. If so, truncate the referrer entirely.
|
||||
AutoTArray<nsString, 2> params = {
|
||||
referrerLengthLimit, NS_ConvertUTF8toUTF16(aInAndOutTrimmedReferrer)};
|
||||
LogMessageToConsole(aChannel, "ReferrerOriginLengthOverLimitation", params);
|
||||
aInAndOutTrimmedReferrer.Truncate();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult ReferrerInfo::GetOriginFromReferrerURI(nsIURI* aReferrer,
|
||||
nsACString& aResult) const {
|
||||
MOZ_ASSERT(aReferrer);
|
||||
aResult.Truncate();
|
||||
// We want the IDN-normalized PrePath. That's not something currently
|
||||
// available and there doesn't yet seem to be justification for adding it to
|
||||
// the interfaces, so just build it up ourselves from scheme+AsciiHostPort
|
||||
// the interfaces, so just build it up from scheme+AsciiHostPort
|
||||
nsAutoCString scheme, asciiHostPort;
|
||||
nsresult rv = aReferrer->GetScheme(scheme);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aResult = scheme;
|
||||
aResult.AppendLiteral("://");
|
||||
// Note we explicitly cleared UserPass above, so do not need to build it.
|
||||
|
@ -506,10 +552,26 @@ nsresult ReferrerInfo::TrimReferrerWithPolicy(nsCOMPtr<nsIURI>& aReferrer,
|
|||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
aResult.Append(asciiHostPort);
|
||||
|
||||
switch (aTrimmingPolicy) {
|
||||
case TrimmingPolicy::ePolicySchemeHostPortPath: {
|
||||
aResult.Append(asciiHostPort);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult ReferrerInfo::TrimReferrerWithPolicy(nsIURI* aReferrer,
|
||||
TrimmingPolicy aTrimmingPolicy,
|
||||
nsACString& aResult) const {
|
||||
MOZ_ASSERT(aReferrer);
|
||||
|
||||
if (aTrimmingPolicy == TrimmingPolicy::ePolicyFullURI) {
|
||||
return aReferrer->GetAsciiSpec(aResult);
|
||||
}
|
||||
|
||||
nsresult rv = GetOriginFromReferrerURI(aReferrer, aResult);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (aTrimmingPolicy == TrimmingPolicy::ePolicySchemeHostPortPath) {
|
||||
nsCOMPtr<nsIURL> url(do_QueryInterface(aReferrer));
|
||||
if (url) {
|
||||
nsAutoCString path;
|
||||
|
@ -517,35 +579,63 @@ nsresult ReferrerInfo::TrimReferrerWithPolicy(nsCOMPtr<nsIURI>& aReferrer,
|
|||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aResult.Append(path);
|
||||
rv = NS_MutateURI(url)
|
||||
.SetQuery(EmptyCString())
|
||||
.SetRef(EmptyCString())
|
||||
.Finalize(aReferrer);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
return NS_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// No URL, so fall through to truncating the path and any query/ref off
|
||||
// as well.
|
||||
}
|
||||
MOZ_FALLTHROUGH;
|
||||
default: // (User Pref limited to [0,2])
|
||||
case TrimmingPolicy::ePolicySchemeHostPort:
|
||||
aResult.AppendLiteral("/");
|
||||
// This nukes any query/ref present as well in the case of nsStandardURL
|
||||
rv = NS_MutateURI(aReferrer)
|
||||
.SetPathQueryRef(EmptyCString())
|
||||
.Finalize(aReferrer);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Step 6 within https://w3c.github.io/webappsec-referrer-policy/#strip-url
|
||||
// states that the trailing "/" does not need to get stripped. However,
|
||||
// GetOriginFromReferrerURI() also removes any trailing "/" hence we have to
|
||||
// add it back here.
|
||||
aResult.AppendLiteral("/");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void ReferrerInfo::LogMessageToConsole(
|
||||
nsIHttpChannel* aChannel, const char* aMsg,
|
||||
const nsTArray<nsString>& aParams) const {
|
||||
MOZ_ASSERT(aChannel);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t windowID = 0;
|
||||
|
||||
rv = aChannel->GetTopLevelContentWindowId(&windowID);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!windowID) {
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadGroup) {
|
||||
windowID = nsContentUtils::GetInnerWindowID(loadGroup);
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString localizedMsg;
|
||||
rv = nsContentUtils::FormatLocalizedString(
|
||||
nsContentUtils::eSECURITY_PROPERTIES, aMsg, aParams, localizedMsg);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
rv = nsContentUtils::ReportToConsoleByWindowID(
|
||||
localizedMsg, nsIScriptError::infoFlag, NS_LITERAL_CSTRING("Security"),
|
||||
windowID, uri);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
ReferrerInfo::ReferrerInfo()
|
||||
: mOriginalReferrer(nullptr),
|
||||
mPolicy(mozilla::net::RP_Unset),
|
||||
|
@ -907,15 +997,24 @@ nsresult ReferrerInfo::ComputeReferrer(nsIHttpChannel* aChannel) {
|
|||
|
||||
TrimmingPolicy trimmingPolicy = ComputeTrimmingPolicy(aChannel);
|
||||
|
||||
nsAutoCString referrerSpec;
|
||||
rv = TrimReferrerWithPolicy(referrer, trimmingPolicy, referrerSpec);
|
||||
nsAutoCString trimmedReferrer;
|
||||
// We first trim the referrer according to the policy by calling
|
||||
// 'TrimReferrerWithPolicy' and right after we have to call
|
||||
// 'LimitReferrerLength' (using the same arguments) because the trimmed
|
||||
// referrer might exceed the allowed max referrer length.
|
||||
rv = TrimReferrerWithPolicy(referrer, trimmingPolicy, trimmedReferrer);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// finally, remember the referrer URI.
|
||||
rv = LimitReferrerLength(aChannel, referrer, trimmingPolicy, trimmedReferrer);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// finally, remember the referrer spec.
|
||||
mComputedReferrer.reset();
|
||||
mComputedReferrer.emplace(referrerSpec);
|
||||
mComputedReferrer.emplace(trimmedReferrer);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -255,13 +255,54 @@ class ReferrerInfo : public nsIReferrerInfo {
|
|||
*/
|
||||
bool IsPolicyOverrided() { return mOverridePolicyByDefault; }
|
||||
|
||||
/*
|
||||
* Get origin string from a given valid referrer URI (http, https, ftp)
|
||||
*
|
||||
* @aReferrer - the full referrer URI
|
||||
* @aResult - the resulting aReferrer in string format.
|
||||
*/
|
||||
nsresult GetOriginFromReferrerURI(nsIURI* aReferrer,
|
||||
nsACString& aResult) const;
|
||||
|
||||
/*
|
||||
* Trim a given referrer with a given a trimming policy,
|
||||
*/
|
||||
nsresult TrimReferrerWithPolicy(nsCOMPtr<nsIURI>& aReferrer,
|
||||
nsresult TrimReferrerWithPolicy(nsIURI* aReferrer,
|
||||
TrimmingPolicy aTrimmingPolicy,
|
||||
nsACString& aResult) const;
|
||||
|
||||
/*
|
||||
* Limit referrer length using the following ruleset:
|
||||
* - If the length of referrer URL is over max length, strip down to origin.
|
||||
* - If the origin is still over max length, remove the referrer entirely.
|
||||
*
|
||||
* This function comlements TrimReferrerPolicy and needs to be called right
|
||||
* after TrimReferrerPolicy.
|
||||
*
|
||||
* @aChannel - used to query information needed for logging to the console.
|
||||
* @aReferrer - the full referrer URI; needs to be identical to aReferrer
|
||||
* passed to TrimReferrerPolicy.
|
||||
* @aTrimmingPolicy - represents the trimming policy which was applied to the
|
||||
* referrer; needs to be identical to aTrimmingPolicy
|
||||
* passed to TrimReferrerPolicy.
|
||||
* @aInAndOutTrimmedReferrer - an in and outgoing argument representing the
|
||||
* referrer value. Please pass the result of
|
||||
* TrimReferrerWithPolicy as
|
||||
* aInAndOutTrimmedReferrer which will then be
|
||||
* reduced to the origin or completely truncated
|
||||
* in case the referrer value exceeds the length
|
||||
* limitation.
|
||||
*/
|
||||
nsresult LimitReferrerLength(nsIHttpChannel* aChannel, nsIURI* aReferrer,
|
||||
TrimmingPolicy aTrimmingPolicy,
|
||||
nsACString& aInAndOutTrimmedReferrer) const;
|
||||
|
||||
/*
|
||||
* Write message to the error console
|
||||
*/
|
||||
void LogMessageToConsole(nsIHttpChannel* aChannel, const char* aMsg,
|
||||
const nsTArray<nsString>& aParams) const;
|
||||
|
||||
nsCOMPtr<nsIURI> mOriginalReferrer;
|
||||
|
||||
uint32_t mPolicy;
|
||||
|
|
|
@ -1605,6 +1605,9 @@ pref("network.http.referer.trimmingPolicy", 0);
|
|||
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);
|
||||
// The maximum allowed length for a referrer header - 4096 default
|
||||
// 0 means no limit.
|
||||
pref("network.http.referer.referrerLengthLimit", 4096);
|
||||
|
||||
// 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
|
||||
|
|
|
@ -212,6 +212,22 @@ function run_test() {
|
|||
referer_uri_2
|
||||
);
|
||||
|
||||
// test referrer length limitation
|
||||
// referer_uri's length is 27 and origin's length is 23
|
||||
prefs.setIntPref("network.http.referer.referrerLengthLimit", 27);
|
||||
Assert.equal(getTestReferrer(server_uri, referer_uri), referer_uri);
|
||||
prefs.setIntPref("network.http.referer.referrerLengthLimit", 26);
|
||||
Assert.equal(
|
||||
getTestReferrer(server_uri, referer_uri),
|
||||
"http://foo.example.com/"
|
||||
);
|
||||
prefs.setIntPref("network.http.referer.referrerLengthLimit", 22);
|
||||
Assert.equal(getTestReferrer(server_uri, referer_uri), null);
|
||||
prefs.setIntPref("network.http.referer.referrerLengthLimit", 0);
|
||||
Assert.equal(getTestReferrer(server_uri, referer_uri), referer_uri);
|
||||
prefs.setIntPref("network.http.referer.referrerLengthLimit", 4096);
|
||||
Assert.equal(getTestReferrer(server_uri, referer_uri), referer_uri);
|
||||
|
||||
// combination test: send spoofed path-only when hosts match
|
||||
var combo_referer_uri = "http://blah.foo.com/path?q=hot";
|
||||
var dest_uri = "http://blah.foo.com:9999/spoofedpath?q=bad";
|
||||
|
|
Загрузка…
Ссылка в новой задаче