--- a/dom/security/test/general/test_same_site_cookies_cross_origin_context.html
+++ b/dom/security/test/general/test_same_site_cookies_cross_origin_context.html
@@ -19,36 +19,55 @@
* in the context of http://mochi.test
* 2) We load an iframe from http://example.com and check if the cookie
* is available.
* 3) We observe that:
* (a) same site cookie has been discarded in a cross origin context.
* (b) the regular cookie is available.
*/
+SimpleTest.registerCleanupFunction(() => {
+ SpecialPowers.clearUserPref("network.cookie.same-site.enabled");
+});
SimpleTest.waitForExplicitFinish();
const CROSS_ORIGIN = "http://example.com/";
const PATH = "tests/dom/security/test/general/file_same_site_cookies_cross_origin_context.sjs";
let curTest = 0;
var tests = [
{
- description: "regular cookie in cross origin context",
+ description: "regular cookie in cross origin context (same-site: on)",
imgSRC: CROSS_ORIGIN + PATH + "?setRegularCookie",
frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: true,
result: "myKey=regularCookie",
},
{
- description: "same-site cookie in cross origin context",
+ description: "same-site cookie in cross origin context (same-site: on)",
imgSRC: CROSS_ORIGIN + PATH + "?setSameSiteCookie",
frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: true,
result: "", // no cookie should be set
},
+ {
+ description: "regular cookie in cross origin context (same-site: off)",
+ imgSRC: CROSS_ORIGIN + PATH + "?setRegularCookie",
+ frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: false,
+ result: "myKey=regularCookie",
+ },
+ {
+ description: "same-site cookie in cross origin context (same-site: off)",
+ imgSRC: CROSS_ORIGIN + PATH + "?setSameSiteCookie",
+ frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: false,
+ result: "myKey=strictSameSiteCookie",
+ },
];
window.addEventListener("message", receiveMessage);
function receiveMessage(event) {
is(event.data.result, tests[curTest].result, tests[curTest].description);
curTest += 1;
@@ -63,16 +82,17 @@ function receiveMessage(event) {
}
function setupQueryResultAndRunTest() {
let testframe = document.getElementById("testframe");
testframe.src = tests[curTest].frameSRC + curTest;
}
function setCookieAndInitTest() {
+ SpecialPowers.setBoolPref("network.cookie.same-site.enabled", tests[curTest].sameSiteEnabled);
var cookieImage = document.getElementById("cookieImage");
cookieImage.onload = function() {
ok(true, "trying to set cookie for test (" + tests[curTest].description + ")");
setupQueryResultAndRunTest();
}
cookieImage.onerror = function() {
ok(false, "could not load image for test (" + tests[curTest].description + ")");
}
--- a/dom/security/test/general/test_same_site_cookies_from_script.html
+++ b/dom/security/test/general/test_same_site_cookies_from_script.html
@@ -18,37 +18,56 @@
* inline script in top-level context of http://mochi.test.
* 2) We load an iframe from http://example.com and check if the cookie
* is available.
* 3) We observe that:
* (a) same site cookie is available in same origin context.
* (a) same site cookie has been discarded in a cross origin context.
*/
+SimpleTest.registerCleanupFunction(() => {
+ SpecialPowers.clearUserPref("network.cookie.same-site.enabled");
+});
SimpleTest.waitForExplicitFinish();
const SAME_ORIGIN = "http://mochi.test:8888/";
const CROSS_ORIGIN = "http://example.com/";
const PATH = "tests/dom/security/test/general/file_same_site_cookies_from_script.sjs";
let curTest = 0;
var tests = [
{
description: "same-site cookie inline script within same-site context",
setCookieSrc: SAME_ORIGIN + PATH + "?setSameSiteCookieUsingInlineScript",
getCookieSrc: SAME_ORIGIN + PATH + "?getCookieFrame",
+ sameSiteEnabled: true,
result: "myKey=sameSiteCookieInlineScript",
},
{
description: "same-site cookie inline script within cross-site context",
setCookieSrc: CROSS_ORIGIN + PATH + "?setSameSiteCookieUsingInlineScript",
getCookieSrc: CROSS_ORIGIN + PATH + "?getCookieFrame",
+ sameSiteEnabled: true,
result: "", // same-site cookie should be discarded in cross site context
},
+ {
+ description: "same-site cookie inline script within same-site context",
+ setCookieSrc: SAME_ORIGIN + PATH + "?setSameSiteCookieUsingInlineScript",
+ getCookieSrc: SAME_ORIGIN + PATH + "?getCookieFrame",
+ sameSiteEnabled: false,
+ result: "myKey=sameSiteCookieInlineScript",
+ },
+ {
+ description: "same-site cookie inline script within cross-site context",
+ setCookieSrc: CROSS_ORIGIN + PATH + "?setSameSiteCookieUsingInlineScript",
+ getCookieSrc: CROSS_ORIGIN + PATH + "?getCookieFrame",
+ sameSiteEnabled: false,
+ result: "myKey=sameSiteCookieInlineScript",
+ },
];
window.addEventListener("message", receiveMessage);
function receiveMessage(event) {
is(event.data.result, tests[curTest].result, tests[curTest].description);
curTest += 1;
// lets see if we ran all the tests
@@ -62,16 +81,17 @@ function receiveMessage(event) {
}
function setupQueryResultAndRunTest() {
let getCookieFrame = document.getElementById("getCookieFrame");
getCookieFrame.src = tests[curTest].getCookieSrc + curTest;
}
function setCookieAndInitTest() {
+ SpecialPowers.setBoolPref("network.cookie.same-site.enabled", tests[curTest].sameSiteEnabled);
var cookieFrame = document.getElementById("setCookieFrame");
setCookieFrame.onload = function() {
ok(true, "trying to set cookie for test (" + tests[curTest].description + ")");
setupQueryResultAndRunTest();
}
setCookieFrame.onerror = function() {
ok(false, "could not load image for test (" + tests[curTest].description + ")");
}
--- a/dom/security/test/general/test_same_site_cookies_subrequest.html
+++ b/dom/security/test/general/test_same_site_cookies_subrequest.html
@@ -22,49 +22,84 @@
*
* In detail:
* We perform an XHR request to the *.sjs file which is processed async on
* the server and waits till the image request has been processed by the server.
* Once the image requets was processed, the server responds to the initial
* XHR request with the expecuted result (the cookie value).
*/
+SimpleTest.registerCleanupFunction(() => {
+ SpecialPowers.clearUserPref("network.cookie.same-site.enabled");
+});
SimpleTest.waitForExplicitFinish();
const SAME_ORIGIN = "http://mochi.test:8888/";
const CROSS_ORIGIN = "http://example.com/";
const PATH = "tests/dom/security/test/general/file_same_site_cookies_subrequest.sjs";
let curTest = 0;
var tests = [
{
description: "same origin site using cookie policy 'samesite=strict'",
imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie",
frameSRC: SAME_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: true,
result: "myKey=strictSameSiteCookie",
},
{
description: "cross origin site using cookie policy 'samesite=strict'",
imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie",
frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: true,
result: "myKey=noCookie",
},
{
description: "same origin site using cookie policy 'samesite=lax'",
imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie",
frameSRC: SAME_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: true,
result: "myKey=laxSameSiteCookie",
},
{
description: "cross origin site using cookie policy 'samesite=lax'",
imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie",
frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: true,
result: "myKey=noCookie",
},
+ {
+ description: "same origin site using cookie policy 'samesite=strict'",
+ imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie",
+ frameSRC: SAME_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: false,
+ result: "myKey=strictSameSiteCookie",
+ },
+ {
+ description: "cross origin site using cookie policy 'samesite=strict'",
+ imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie",
+ frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: false,
+ result: "myKey=strictSameSiteCookie",
+ },
+ {
+ description: "same origin site using cookie policy 'samesite=lax'",
+ imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie",
+ frameSRC: SAME_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: false,
+ result: "myKey=laxSameSiteCookie",
+ },
+ {
+ description: "cross origin site using cookie policy 'samesite=lax'",
+ imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie",
+ frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: false,
+ result: "myKey=laxSameSiteCookie",
+ },
];
function checkResult(aCookieVal) {
is(aCookieVal, tests[curTest].result, tests[curTest].description);
curTest += 1;
// lets see if we ran all the tests
if (curTest == tests.length) {
@@ -89,16 +124,17 @@ function setupQueryResultAndRunTest() {
// give it some time and load the test frame
SimpleTest.executeSoon(function() {
let testframe = document.getElementById("testframe");
testframe.src = tests[curTest].frameSRC + curTest;
});
}
function setCookieAndInitTest() {
+ SpecialPowers.setBoolPref("network.cookie.same-site.enabled", tests[curTest].sameSiteEnabled);
var cookieImage = document.getElementById("cookieImage");
cookieImage.onload = function() {
ok(true, "set cookie for test (" + tests[curTest].description + ")");
setupQueryResultAndRunTest();
}
cookieImage.onerror = function() {
ok(false, "could not set cookie for test (" + tests[curTest].description + ")");
}
--- a/dom/security/test/general/test_same_site_cookies_toplevel_nav.html
+++ b/dom/security/test/general/test_same_site_cookies_toplevel_nav.html
@@ -23,47 +23,82 @@
*
* In detail:
* We perform an XHR request to the *.sjs file which is processed async on
* the server and waits till the image request has been processed by the server.
* Once the image requets was processed, the server responds to the initial
* XHR request with the expecuted result (the cookie value).
*/
+SimpleTest.registerCleanupFunction(() => {
+ SpecialPowers.clearUserPref("network.cookie.same-site.enabled");
+});
SimpleTest.waitForExplicitFinish();
const SAME_ORIGIN = "http://mochi.test:8888/";
const CROSS_ORIGIN = "http://example.com/";
const PATH = "tests/dom/security/test/general/file_same_site_cookies_toplevel_nav.sjs";
let curTest = 0;
var tests = [
{
description: "same origin navigation using cookie policy 'samesite=strict'",
imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie",
frameSRC: SAME_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: true,
result: "myKey=strictSameSiteCookie",
},
{
description: "cross origin navigation using cookie policy 'samesite=strict'",
imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie",
frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: true,
result: "myKey=noCookie",
},
{
description: "same origin navigation using cookie policy 'samesite=lax'",
imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie",
frameSRC: SAME_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: true,
result: "myKey=laxSameSiteCookie",
},
{
description: "cross origin navigation using cookie policy 'samesite=lax'",
imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie",
frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: true,
+ result: "myKey=laxSameSiteCookie",
+ },
+ {
+ description: "same origin navigation using cookie policy 'samesite=strict'",
+ imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie",
+ frameSRC: SAME_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: false,
+ result: "myKey=strictSameSiteCookie",
+ },
+ {
+ description: "cross origin navigation using cookie policy 'samesite=strict'",
+ imgSRC: SAME_ORIGIN + PATH + "?setStrictSameSiteCookie",
+ frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: false,
+ result: "myKey=strictSameSiteCookie",
+ },
+ {
+ description: "same origin navigation using cookie policy 'samesite=lax'",
+ imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie",
+ frameSRC: SAME_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: false,
+ result: "myKey=laxSameSiteCookie",
+ },
+ {
+ description: "cross origin navigation using cookie policy 'samesite=lax'",
+ imgSRC: SAME_ORIGIN + PATH + "?setLaxSameSiteCookie",
+ frameSRC: CROSS_ORIGIN + PATH + "?loadFrame",
+ sameSiteEnabled: false,
result: "myKey=laxSameSiteCookie",
},
];
function checkResult(aCookieVal) {
is(aCookieVal, tests[curTest].result, tests[curTest].description);
curTest += 1;
@@ -90,16 +125,17 @@ function setupQueryResultAndRunTest() {
// give it some time and load the test window
SimpleTest.executeSoon(function() {
let testframe = document.getElementById("testframe");
testframe.src = tests[curTest].frameSRC + curTest;
});
}
function setCookieAndInitTest() {
+ SpecialPowers.setBoolPref("network.cookie.same-site.enabled", tests[curTest].sameSiteEnabled);
var cookieImage = document.getElementById("cookieImage");
cookieImage.onload = function() {
ok(true, "set cookie for test (" + tests[curTest].description + ")");
setupQueryResultAndRunTest();
}
cookieImage.onerror = function() {
ok(false, "could not set cookie for test (" + tests[curTest].description + ")");
}
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2221,16 +2221,17 @@ pref("network.proxy.failover_timeout",
pref("network.online", true); //online/offline
pref("network.cookie.cookieBehavior", 0); // 0-Accept, 1-dontAcceptForeign, 2-dontAcceptAny, 3-limitForeign
#ifdef ANDROID
pref("network.cookie.cookieBehavior", 0); // Keep the old default of accepting all cookies
#endif
pref("network.cookie.thirdparty.sessionOnly", false);
pref("network.cookie.thirdparty.nonsecureSessionOnly", false);
pref("network.cookie.leave-secure-alone", true);
+pref("network.cookie.same-site.enabled", true); // Honor the SameSite cookie attribute
pref("network.cookie.ipc.sync", false);
pref("network.cookie.lifetimePolicy", 0); // 0-accept, 1-dontUse 2-acceptForSession, 3-acceptForNDays
pref("network.cookie.prefsMigrated", false);
pref("network.cookie.lifetime.days", 90); // Ignored unless network.cookie.lifetimePolicy is 3.
// The PAC file to load. Ignored unless network.proxy.type is 2.
pref("network.proxy.autoconfig_url", "");
// Strip off paths when sending URLs to PAC scripts
--- a/netwerk/cookie/CookieServiceChild.cpp
+++ b/netwerk/cookie/CookieServiceChild.cpp
@@ -309,17 +309,17 @@ CookieServiceChild::GetCookieStringFromC
continue;
// if the cookie is secure and the host scheme isn't, we can't send it
if (cookie->IsSecure() && !isSecure)
continue;
int32_t sameSiteAttr = 0;
cookie->GetSameSite(&sameSiteAttr);
- if (aIsSameSiteForeign) {
+ if (aIsSameSiteForeign && nsCookieService::IsSameSiteEnabled()) {
// it if's a cross origin request and the cookie is same site only (strict)
// don't send it
if (sameSiteAttr == nsICookie2::SAMESITE_STRICT) {
continue;
}
// if it's a cross origin request, the cookie is same site lax, but it's not
// a top-level navigation, don't send it
if (sameSiteAttr == nsICookie2::SAMESITE_LAX && !aIsSafeTopLevelNav) {
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -71,16 +71,17 @@ using namespace mozilla::net;
nsCookieKey(baseDomain, OriginAttributes())
/******************************************************************************
* nsCookieService impl:
* useful types & constants
******************************************************************************/
static StaticRefPtr<nsCookieService> gCookieService;
+bool nsCookieService::sSameSiteEnabled = false;
// XXX_hack. See bug 178993.
// This is a hack to hide HttpOnly cookies from older browsers
#define HTTP_ONLY_PREFIX "#HttpOnly_"
#define COOKIES_FILE "cookies.sqlite"
#define COOKIES_SCHEMA_VERSION 9
@@ -3070,16 +3071,28 @@ nsCookieService::DomainMatches(nsCookie*
// first, check for an exact host or domain cookie match, e.g. "google.com"
// or ".google.com"; second a subdomain match, e.g.
// host = "mail.google.com", cookie domain = ".google.com".
return aCookie->RawHost() == aHost ||
(aCookie->IsDomain() && StringEndsWith(aHost, aCookie->Host()));
}
bool
+nsCookieService::IsSameSiteEnabled()
+{
+ static bool prefInitialized = false;
+ if (!prefInitialized) {
+ Preferences::AddBoolVarCache(&sSameSiteEnabled,
+ "network.cookie.same-site.enabled", false);
+ prefInitialized = true;
+ }
+ return sSameSiteEnabled;
+}
+
+bool
nsCookieService::PathMatches(nsCookie* aCookie,
const nsACString& aPath)
{
// calculate cookie path length, excluding trailing '/'
uint32_t cookiePathLen = aCookie->Path().Length();
if (cookiePathLen > 0 && aCookie->Path().Last() == '/')
--cookiePathLen;
@@ -3199,17 +3212,17 @@ nsCookieService::GetCookiesForURI(nsIURI
continue;
// if the cookie is secure and the host scheme isn't, we can't send it
if (cookie->IsSecure() && !isSecure)
continue;
int32_t sameSiteAttr = 0;
cookie->GetSameSite(&sameSiteAttr);
- if (aIsSameSiteForeign) {
+ if (aIsSameSiteForeign && IsSameSiteEnabled()) {
// it if's a cross origin request and the cookie is same site only (strict)
// don't send it
if (sameSiteAttr == nsICookie2::SAMESITE_STRICT) {
continue;
}
// if it's a cross origin request, the cookie is same site lax, but it's not
// a top-level navigation, don't send it
if (sameSiteAttr == nsICookie2::SAMESITE_LAX && !aIsSafeTopLevelNav) {
@@ -3466,17 +3479,18 @@ nsCookieService::CanSetCookie(nsIURI*
"non-https cookie can't set secure flag");
Telemetry::Accumulate(Telemetry::COOKIE_LEAVE_SECURE_ALONE,
BLOCKED_SECURE_SET_FROM_HTTP);
return newCookie;
}
// If the new cookie is same-site but in a cross site context,
// browser must ignore the cookie.
- if (aCookieAttributes.sameSite != nsICookie2::SAMESITE_UNSET) {
+ if ((aCookieAttributes.sameSite != nsICookie2::SAMESITE_UNSET) &&
+ IsSameSiteEnabled()) {
bool isThirdParty = NS_IsSameSiteForeign(aChannel, aHostURI);
if (isThirdParty) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
"failed the samesite tests");
return newCookie;
}
}
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -241,16 +241,18 @@ class nsCookieService final : public nsI
, public nsICookieManager
, public nsIObserver
, public nsSupportsWeakReference
, public nsIMemoryReporter
{
private:
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ static bool sSameSiteEnabled; // cached pref
+
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSICOOKIESERVICE
NS_DECL_NSICOOKIEMANAGER
NS_DECL_NSIMEMORYREPORTER
nsCookieService();
@@ -263,16 +265,17 @@ class nsCookieService final : public nsI
* (thus instantiating it, if necessary) and clear all the cookies for that
* app.
*/
static void AppClearDataObserverInit();
static nsCString GetPathFromURI(nsIURI *aHostURI);
static nsresult GetBaseDomain(nsIEffectiveTLDService *aTLDService, nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch);
static nsresult GetBaseDomainFromHost(nsIEffectiveTLDService *aTLDService, const nsACString &aHost, nsCString &aBaseDomain);
static bool DomainMatches(nsCookie* aCookie, const nsACString& aHost);
+ static bool IsSameSiteEnabled();
static bool PathMatches(nsCookie* aCookie, const nsACString& aPath);
static bool CanSetCookie(nsIURI *aHostURI, const nsCookieKey& aKey, nsCookieAttributes &aCookieAttributes, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp, nsIChannel* aChannel, bool aLeaveSercureAlone, bool &aSetCookie, mozIThirdPartyUtil* aThirdPartyUtil);
static CookieStatus CheckPrefs(nsICookiePermission *aPermissionServices, uint8_t aCookieBehavior, bool aThirdPartySession, bool aThirdPartyNonsecureSession, nsIURI *aHostURI, bool aIsForeign, const char *aCookieHeader, const int aNumOfCookies, const OriginAttributes& aOriginAttrs);
static int64_t ParseServerTime(const nsCString &aServerTime);
void GetCookiesForURI(nsIURI *aHostURI, bool aIsForeign, bool aIsSafeTopLevelNav, bool aIsTopLevelForeign, bool aHttpBound, const OriginAttributes& aOriginAttrs, nsTArray<nsCookie*>& aCookieList);
protected:
virtual ~nsCookieService();