Bug 1307596 - Add a preference for trimming third-party referrers. r?dragana
MozReview-Commit-ID: EL2L4yMnwAi
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1448,16 +1448,18 @@ pref("network.http.accept.default", "tex
// Prefs allowing granular control of referers
// 0=don't send any, 1=send only on clicks, 2=send on image requests as well
pref("network.http.sendRefererHeader", 2);
// false=real referer, true=spoof referer (use target URI as referer)
pref("network.http.referer.spoofSource", false);
// 0=full URI, 1=scheme+host+port+path, 2=scheme+host+port
pref("network.http.referer.trimmingPolicy", 0);
+// 0=full URI, 1=scheme+host+port+path, 2=scheme+host+port
+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);
// Controls whether we send HTTPS referres to other HTTPS sites.
// By default this is enabled for compatibility (see bug 141641)
pref("network.http.sendSecureXSiteReferrer", true);
// Controls whether referrer attributes in <a>, <img>, <area>, <iframe>, and <link> are honoured
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1512,16 +1512,25 @@ HttpBaseChannel::SetReferrerWithPolicy(n
// strip away any userpass; we don't want to be giving out passwords ;-)
// This is required by Referrer Policy stripping algorithm.
rv = clone->SetUserPass(EmptyCString());
if (NS_FAILED(rv)) return rv;
nsAutoCString spec;
+ // Apply the user cross-origin trimming policy if it's more
+ // restrictive than the general one.
+ if (isCrossOrigin) {
+ int userReferrerXOriginTrimmingPolicy =
+ gHttpHandler->ReferrerXOriginTrimmingPolicy();
+ userReferrerTrimmingPolicy =
+ std::max(userReferrerTrimmingPolicy, userReferrerXOriginTrimmingPolicy);
+ }
+
// site-specified referrer trimming may affect the trim level
// "unsafe-url" behaves like "origin" (send referrer in the same situations) but
// "unsafe-url" sends the whole referrer and origin removes the path.
// "origin-when-cross-origin" trims the referrer only when the request is
// cross-origin.
// "Strict" request from https->http case was bailed out, so here:
// "strict-origin" behaves the same as "origin".
// "strict-origin-when-cross-origin" behaves the same as "origin-when-cross-origin"
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -161,16 +161,17 @@ nsHttpHandler *gHttpHandler = nullptr;
nsHttpHandler::nsHttpHandler()
: mHttpVersion(NS_HTTP_VERSION_1_1)
, mProxyHttpVersion(NS_HTTP_VERSION_1_1)
, mCapabilities(NS_HTTP_ALLOW_KEEPALIVE)
, mReferrerLevel(0xff) // by default we always send a referrer
, mSpoofReferrerSource(false)
, mReferrerTrimmingPolicy(0)
+ , mReferrerXOriginTrimmingPolicy(0)
, mReferrerXOriginPolicy(0)
, mFastFallbackToIPv4(false)
, mProxyPipelining(true)
, mIdleTimeout(PR_SecondsToInterval(10))
, mSpdyTimeout(PR_SecondsToInterval(180))
, mResponseTimeout(PR_SecondsToInterval(300))
, mResponseTimeoutEnabled(false)
, mNetworkChangedTimeout(5000)
@@ -1080,16 +1081,22 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
}
if (PREF_CHANGED(HTTP_PREF("referer.trimmingPolicy"))) {
rv = prefs->GetIntPref(HTTP_PREF("referer.trimmingPolicy"), &val);
if (NS_SUCCEEDED(rv))
mReferrerTrimmingPolicy = (uint8_t) clamped(val, 0, 2);
}
+ if (PREF_CHANGED(HTTP_PREF("referer.XOriginTrimmingPolicy"))) {
+ rv = prefs->GetIntPref(HTTP_PREF("referer.XOriginTrimmingPolicy"), &val);
+ if (NS_SUCCEEDED(rv))
+ mReferrerXOriginTrimmingPolicy = (uint8_t) clamped(val, 0, 2);
+ }
+
if (PREF_CHANGED(HTTP_PREF("referer.XOriginPolicy"))) {
rv = prefs->GetIntPref(HTTP_PREF("referer.XOriginPolicy"), &val);
if (NS_SUCCEEDED(rv))
mReferrerXOriginPolicy = (uint8_t) clamped(val, 0, 0xff);
}
if (PREF_CHANGED(HTTP_PREF("redirection-limit"))) {
rv = prefs->GetIntPref(HTTP_PREF("redirection-limit"), &val);
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -77,16 +77,19 @@ public:
const nsAFlatCString &UserAgent();
nsHttpVersion HttpVersion() { return mHttpVersion; }
nsHttpVersion ProxyHttpVersion() { return mProxyHttpVersion; }
uint8_t ReferrerLevel() { return mReferrerLevel; }
bool SpoofReferrerSource() { return mSpoofReferrerSource; }
uint8_t ReferrerTrimmingPolicy() { return mReferrerTrimmingPolicy; }
+ uint8_t ReferrerXOriginTrimmingPolicy() {
+ return mReferrerXOriginTrimmingPolicy;
+ }
uint8_t ReferrerXOriginPolicy() { return mReferrerXOriginPolicy; }
bool SendSecureXSiteReferrer() { return mSendSecureXSiteReferrer; }
bool PackagedAppsEnabled() { return mPackagedAppsEnabled; }
uint8_t RedirectionLimit() { return mRedirectionLimit; }
PRIntervalTime IdleTimeout() { return mIdleTimeout; }
PRIntervalTime SpdyTimeout() { return mSpdyTimeout; }
PRIntervalTime ResponseTimeout() {
return mResponseTimeoutEnabled ? mResponseTimeout : 0;
@@ -412,16 +415,17 @@ private:
//
uint8_t mHttpVersion;
uint8_t mProxyHttpVersion;
uint32_t mCapabilities;
uint8_t mReferrerLevel;
uint8_t mSpoofReferrerSource;
uint8_t mReferrerTrimmingPolicy;
+ uint8_t mReferrerXOriginTrimmingPolicy;
uint8_t mReferrerXOriginPolicy;
bool mFastFallbackToIPv4;
bool mProxyPipelining;
PRIntervalTime mIdleTimeout;
PRIntervalTime mSpdyTimeout;
PRIntervalTime mResponseTimeout;
bool mResponseTimeoutEnabled;
--- a/netwerk/test/unit/test_referrer.js
+++ b/netwerk/test/unit/test_referrer.js
@@ -1,19 +1,24 @@
Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
function getTestReferrer(server_uri, referer_uri) {
var uri = NetUtil.newURI(server_uri, "", null)
+ let referrer = NetUtil.newURI(referer_uri, null, null);
+ let triggeringPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(referrer, {});
var chan = NetUtil.newChannel({
uri: uri,
- loadUsingSystemPrincipal: true
+ loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+ triggeringPrincipal: triggeringPrincipal,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
});
chan.QueryInterface(Components.interfaces.nsIHttpChannel);
- chan.referrer = NetUtil.newURI(referer_uri, null, null);
+ chan.referrer = referrer;
var header = null;
try {
header = chan.getRequestHeader("Referer");
}
catch (NS_ERROR_NOT_AVAILABLE) {}
return header;
}
@@ -26,16 +31,17 @@ function run_test() {
var referer_uri = "http://foo.example.com/path";
var referer_uri_2 = "http://bar.examplesite.com/path3?q=blah";
var referer_uri_2_anchor = "http://bar.examplesite.com/path3?q=blah#anchor";
var referer_uri_idn = "http://sub1.\xe4lt.example/path";
// for https tests
var server_uri_https = "https://bar.example.com/anotherpath";
var referer_uri_https = "https://bar.example.com/path3?q=blah";
+ var referer_uri_2_https = "https://bar.examplesite.com/path3?q=blah";
// tests for sendRefererHeader
prefs.setIntPref("network.http.sendRefererHeader", 0);
do_check_null(getTestReferrer(server_uri, referer_uri));
prefs.setIntPref("network.http.sendRefererHeader", 2);
do_check_eq(getTestReferrer(server_uri, referer_uri), referer_uri);
// test that https ref is not sent to http
@@ -67,16 +73,36 @@ function run_test() {
do_check_eq(getTestReferrer(server_uri, referer_uri_2), "http://bar.examplesite.com/");
do_check_eq(getTestReferrer(server_uri, referer_uri_idn), "http://sub1.xn--lt-uia.example/");
// https test
do_check_eq(getTestReferrer(server_uri_https, referer_uri_https), "https://bar.example.com/");
prefs.setIntPref("network.http.referer.trimmingPolicy", 0);
// test that anchor is lopped off in ordinary case
do_check_eq(getTestReferrer(server_uri, referer_uri_2_anchor), referer_uri_2);
+ // tests for referer.XOriginTrimmingPolicy
+ prefs.setIntPref("network.http.referer.XOriginTrimmingPolicy", 1);
+ do_check_eq(getTestReferrer(server_uri, referer_uri), "http://foo.example.com/path");
+ do_check_eq(getTestReferrer(server_uri, referer_uri_idn), "http://sub1.xn--lt-uia.example/path");
+ do_check_eq(getTestReferrer(server_uri, referer_uri_2), "http://bar.examplesite.com/path3?q=blah");
+ prefs.setIntPref("network.http.referer.trimmingPolicy", 1);
+ do_check_eq(getTestReferrer(server_uri, referer_uri_2), "http://bar.examplesite.com/path3");
+ prefs.setIntPref("network.http.referer.XOriginTrimmingPolicy", 2);
+ do_check_eq(getTestReferrer(server_uri, referer_uri), "http://foo.example.com/");
+ do_check_eq(getTestReferrer(server_uri, referer_uri_idn), "http://sub1.xn--lt-uia.example/");
+ do_check_eq(getTestReferrer(server_uri, referer_uri_2), "http://bar.examplesite.com/path3");
+ prefs.setIntPref("network.http.referer.trimmingPolicy", 0);
+ do_check_eq(getTestReferrer(server_uri, referer_uri_2), "http://bar.examplesite.com/path3?q=blah");
+ // https tests
+ do_check_eq(getTestReferrer(server_uri_https, referer_uri_https), "https://bar.example.com/path3?q=blah");
+ do_check_eq(getTestReferrer(server_uri_https, referer_uri_2_https), "https://bar.examplesite.com/");
+ prefs.setIntPref("network.http.referer.XOriginTrimmingPolicy", 0);
+ // test that anchor is lopped off in ordinary case
+ do_check_eq(getTestReferrer(server_uri, referer_uri_2_anchor), referer_uri_2);
+
// 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";
prefs.setIntPref("network.http.referer.trimmingPolicy", 1);
prefs.setBoolPref("network.http.referer.spoofSource", true);
prefs.setIntPref("network.http.referer.XOriginPolicy", 2);
do_check_eq(getTestReferrer(dest_uri, combo_referer_uri), "http://blah.foo.com:9999/spoofedpath");
do_check_null(getTestReferrer(dest_uri, "http://gah.foo.com/anotherpath"));