#499: same-site cookie support

This commit is contained in:
Cameron Kaiser 2018-05-25 17:12:00 -07:00
parent d9f9fb2ca3
commit 93c7398fd1
14 changed files with 306 additions and 25 deletions

View File

@ -65,6 +65,7 @@ table.headers.cookies.expires=Expires on
table.headers.cookies.value=Value
table.headers.cookies.lastAccessed=Last accessed on
table.headers.cookies.creationTime=Created on
table.headers.cookies.sameSite=sameSite
# LOCALIZATION NOTE (table.headers.cookies.isHttpOnly):
# This string is used in the header for the column which denotes whether a
# cookie is HTTP only or not.

View File

@ -15,6 +15,14 @@ const promise = require("promise");
const {isWindowIncluded} = require("devtools/shared/layout/utils");
const {setTimeout, clearTimeout} = require("sdk/timers");
// "Lax", "Strict" and "Unset" are special values of the sameSite property
// that should not be translated.
const COOKIE_SAMESITE = {
LAX: "Lax",
STRICT: "Strict",
UNSET: "Unset"
};
loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
loader.lazyImporter(this, "Sqlite", "resource://gre/modules/Sqlite.jsm");
loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm", "Task");
@ -83,7 +91,8 @@ types.addDictType("cookieobject", {
isHttpOnly: "boolean",
creationTime: "number",
lastAccessed: "number",
expires: "number"
expires: "number",
sameSite: "string"
});
// Array of cookie store objects
@ -546,10 +555,22 @@ StorageActors.createActor({
value: new LongStringActor(this.conn, cookie.value || ""),
isDomain: cookie.isDomain,
isSecure: cookie.isSecure,
isHttpOnly: cookie.isHttpOnly
isHttpOnly: cookie.isHttpOnly,
sameSite: this.getSameSiteStringFromCookie(cookie)
};
},
getSameSiteStringFromCookie(cookie) {
switch (cookie.sameSite) {
case cookie.SAMESITE_LAX:
return COOKIE_SAMESITE.LAX;
case cookie.SAMESITE_STRICT:
return COOKIE_SAMESITE.STRICT;
}
// cookie.SAMESITE_UNSET
return COOKIE_SAMESITE.UNSET;
},
populateStoresForHost: function(host) {
this.hostVsStores.set(host, new Map());

View File

@ -1841,6 +1841,7 @@ pref("network.cookie.cookieBehavior", 0); // 0-Accept, 1-dontAcceptForeign
pref("network.cookie.cookieBehavior", 0); // Keep the old default of accepting all cookies
#endif
pref("network.cookie.thirdparty.sessionOnly", false);
pref("network.cookie.same-site.enabled", true); // Honor the SameSite cookie attribute
pref("network.cookie.lifetimePolicy", 0); // 0-accept, 1-dontUse 2-acceptForSession, 3-acceptForNDays
pref("network.cookie.alwaysAcceptSessionCookies", false);
pref("network.cookie.prefsMigrated", false);

View File

@ -58,6 +58,9 @@
#include "nsInterfaceRequestorAgg.h"
#include "plstr.h"
#include "nsINestedURI.h"
#include "HttpBaseChannel.h"
#include "mozIThirdPartyUtil.h"
#include "nsQueryObject.h"
#ifdef MOZ_WIDGET_GONK
#include "nsINetworkManager.h"
@ -1303,6 +1306,80 @@ NS_HasBeenCrossOrigin(nsIChannel* aChannel, bool aReport)
return NS_FAILED(loadingPrincipal->CheckMayLoad(uri, aReport, dataInherits));
}
bool
NS_IsSafeTopLevelNav(nsIChannel* aChannel)
{
if (!aChannel) {
return false;
}
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
if (!loadInfo) {
return false;
}
if (loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT) {
return false;
}
RefPtr<HttpBaseChannel> baseChan = do_QueryObject(aChannel);
if (!baseChan) {
return false;
}
nsHttpRequestHead *requestHead = baseChan->GetRequestHead();
if (!requestHead) {
return false;
}
return requestHead->IsSafeMethod();
}
bool NS_IsSameSiteForeign(nsIChannel* aChannel, nsIURI* aHostURI)
{
if (!aChannel) {
return false;
}
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
if (!loadInfo) {
return false;
}
nsCOMPtr<nsIURI> uri;
if (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) {
// for loads of TYPE_DOCUMENT we query the hostURI from the triggeringPricnipal
// which returns the URI of the document that caused the navigation.
loadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(uri));
}
else {
uri = aHostURI;
}
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
do_GetService(THIRDPARTYUTIL_CONTRACTID);
if (!thirdPartyUtil) {
return false;
}
bool isForeign = false;
thirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
// if we are dealing with a cross origin request, we can return here
// because we already know the request is 'foreign'.
if (isForeign) {
return true;
}
// for the purpose of same-site cookies we have to treat any cross-origin
// redirects as foreign. E.g. cross-site to same-site redirect is a problem
// with regards to CSRF.
nsCOMPtr<nsIURI> redirectURI;
for (nsIPrincipal* principal : loadInfo->RedirectChain()) {
principal->GetURI(getter_AddRefs(redirectURI));
thirdPartyUtil->IsThirdPartyChannel(aChannel, redirectURI, &isForeign);
// if at any point we encounter a cross-origin redirect we can return.
if (isForeign) {
return true;
}
}
return isForeign;
}
nsresult
NS_GetAppInfoFromClearDataNotification(nsISupports *aSubject,
uint32_t *aAppID,

View File

@ -697,6 +697,18 @@ bool NS_GetOriginAttributes(nsIChannel *aChannel,
*/
bool NS_HasBeenCrossOrigin(nsIChannel* aChannel, bool aReport = false);
/**
* Returns true if the channel is a safe top-level navigation.
*/
bool NS_IsSafeTopLevelNav(nsIChannel* aChannel);
/**
* Returns true if the channel is a foreign with respect to the host-uri.
* For loads of TYPE_DOCUMENT, this function returns true if it's a
* cross origin navigation.
*/
bool NS_IsSameSiteForeign(nsIChannel* aChannel, nsIURI* aHostURI);
// Constants duplicated from nsIScriptSecurityManager so we avoid having necko
// know about script security manager.
#define NECKO_NO_APP_ID 0

View File

@ -140,8 +140,23 @@ CookieServiceParent::RecvGetCookieString(const URIParams& aHost,
return false;
}
mCookieService->GetCookieStringInternal(hostURI, aIsForeign, aFromHttp, attrs,
isPrivate, *aResult);
// To support same-site cookies, we need a similar dummy channel
// as in RecvSetCookieString (see below), and implement a similar
// "gross hack" for TenFourFox issue 499. Again, only isPrivate is
// relevant here.
nsCOMPtr<nsIChannel> dummyChannel;
CreateDummyChannel(hostURI, attrs, isPrivate, getter_AddRefs(dummyChannel));
// XXX: This is wrong, and should crash if enabled. The dummy
// channel can never be nsIContentPolicy::TYPE_DOCUMENT and is not
// an HTTP channel, so the headers cannot be inspected. Once the
// channel is hooked up to the actual HTTP channel, this should
// "just work" as it does in nsCookieService.
MOZ_CRASH("RecvGetCookieString NYI");
mCookieService->GetCookieStringInternal(hostURI, aIsForeign, aFromHttp,
NS_IsSafeTopLevelNav(dummyChannel),
NS_IsSameSiteForeign(dummyChannel, hostURI),
attrs, isPrivate, *aResult);
return true;
}

View File

@ -79,7 +79,8 @@ nsCookie::Create(const nsACString &aName,
int64_t aCreationTime,
bool aIsSession,
bool aIsSecure,
bool aIsHttpOnly)
bool aIsHttpOnly,
int32_t aSameSite)
{
// Ensure mValue contains a valid UTF-8 sequence. Otherwise XPConnect will
// truncate the string after the first invalid octet.
@ -108,10 +109,16 @@ nsCookie::Create(const nsACString &aName,
if (aCreationTime > gLastCreationTime)
gLastCreationTime = aCreationTime;
// If aSameSite is not a sensible value, assume strict
if (aSameSite < 0 || aSameSite > nsICookie2::SAMESITE_STRICT) {
aSameSite = nsICookie2::SAMESITE_STRICT;
}
// construct the cookie. placement new, oh yeah!
return new (place) nsCookie(name, value, host, path, end,
aExpiry, aLastAccessed, aCreationTime,
aIsSession, aIsSecure, aIsHttpOnly);
aIsSession, aIsSecure, aIsHttpOnly,
aSameSite);
}
size_t
@ -150,6 +157,7 @@ NS_IMETHODIMP nsCookie::GetStatus(nsCookieStatus *aStatus) { *aStatus = 0;
NS_IMETHODIMP nsCookie::GetPolicy(nsCookiePolicy *aPolicy) { *aPolicy = 0; return NS_OK; }
NS_IMETHODIMP nsCookie::GetCreationTime(int64_t *aCreation){ *aCreation = CreationTime(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetLastAccessed(int64_t *aTime) { *aTime = LastAccessed(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetSameSite(int32_t *aSameSite) { *aSameSite = SameSite(); return NS_OK; }
// compatibility method, for use with the legacy nsICookie interface.
// here, expires == 0 denotes a session cookie.

View File

@ -43,7 +43,8 @@ class nsCookie : public nsICookie2
int64_t aCreationTime,
bool aIsSession,
bool aIsSecure,
bool aIsHttpOnly)
bool aIsHttpOnly,
int32_t aSameSite)
: mName(aName)
, mValue(aValue)
, mHost(aHost)
@ -55,6 +56,7 @@ class nsCookie : public nsICookie2
, mIsSession(aIsSession != false)
, mIsSecure(aIsSecure != false)
, mIsHttpOnly(aIsHttpOnly != false)
, mSameSite(aSameSite)
{
}
@ -74,7 +76,8 @@ class nsCookie : public nsICookie2
int64_t aCreationTime,
bool aIsSession,
bool aIsSecure,
bool aIsHttpOnly);
bool aIsHttpOnly,
int32_t aSameSite);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
@ -91,6 +94,7 @@ class nsCookie : public nsICookie2
inline bool IsDomain() const { return *mHost == '.'; }
inline bool IsSecure() const { return mIsSecure; }
inline bool IsHttpOnly() const { return mIsHttpOnly; }
inline int32_t SameSite() const { return mSameSite; }
// setters
inline void SetExpiry(int64_t aExpiry) { mExpiry = aExpiry; }
@ -123,6 +127,7 @@ class nsCookie : public nsICookie2
bool mIsSession;
bool mIsSecure;
bool mIsHttpOnly;
int32_t mSameSite;
};
#endif // nsCookie_h__

View File

@ -69,13 +69,17 @@ using namespace mozilla::net;
******************************************************************************/
static 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 7
/* XXX: Our schema version diverges from mainline Firefox.
v8 here is for SameSite (TenFourFox issue 499), not v9.
We don't need nor implement OriginAttributes. */
#define COOKIES_SCHEMA_VERSION 8
// parameter indexes; see EnsureReadDomain, EnsureReadComplete and
// ReadCookieDBListener::HandleResult
@ -90,6 +94,7 @@ static nsCookieService *gCookieService;
#define IDX_HTTPONLY 8
#define IDX_BASE_DOMAIN 9
#define IDX_ORIGIN_ATTRIBUTES 10
#define IDX_SAME_SITE 11
static const int64_t kCookiePurgeAge =
int64_t(30 * 24 * 60 * 60) * PR_USEC_PER_SEC; // 30 days in microseconds
@ -134,6 +139,7 @@ struct nsCookieAttributes
bool isSession;
bool isSecure;
bool isHttpOnly;
int32_t sameSite;
};
// stores the nsCookieEntry entryclass and an index into the cookie array
@ -258,6 +264,7 @@ LogCookie(nsCookie *aCookie)
MOZ_LOG(gCookieLog, LogLevel::Debug,("is secure: %s\n", aCookie->IsSecure() ? "true" : "false"));
MOZ_LOG(gCookieLog, LogLevel::Debug,("is httpOnly: %s\n", aCookie->IsHttpOnly() ? "true" : "false"));
MOZ_LOG(gCookieLog, LogLevel::Debug,("sameSite: %d\n", aCookie->SameSite()));
}
}
@ -1317,6 +1324,16 @@ nsCookieService::TryInitDB(bool aRecreateDB)
("Upgraded database to schema version 7"));
}
case 7:
{
// Add the sameSite column to the table.
rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("ALTER TABLE moz_cookies ADD sameSite INTEGER DEFAULT 0;"));
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
COOKIE_LOGSTRING(LogLevel::Debug,
("Upgraded database to schema version 8"));
}
// No more upgrades. Update the schema version.
rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
@ -1361,7 +1378,8 @@ nsCookieService::TryInitDB(bool aRecreateDB)
"lastAccessed, "
"creationTime, "
"isSecure, "
"isHttpOnly "
"isHttpOnly, "
"sameSite "
"FROM moz_cookies"), getter_AddRefs(stmt));
if (NS_SUCCEEDED(rv))
break;
@ -1402,7 +1420,8 @@ nsCookieService::TryInitDB(bool aRecreateDB)
"lastAccessed, "
"creationTime, "
"isSecure, "
"isHttpOnly"
"isHttpOnly, "
"sameSite"
") VALUES ("
":baseDomain, "
":originAttributes, "
@ -1414,7 +1433,8 @@ nsCookieService::TryInitDB(bool aRecreateDB)
":lastAccessed, "
":creationTime, "
":isSecure, "
":isHttpOnly"
":isHttpOnly, "
":sameSite"
")"),
getter_AddRefs(mDefaultDBState->stmtInsert));
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
@ -1486,6 +1506,7 @@ nsCookieService::CreateTable()
"isHttpOnly INTEGER, "
"appId INTEGER DEFAULT 0, "
"inBrowserElement INTEGER DEFAULT 0, "
"sameSite INTEGER DEFAULT 0, "
"CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)"
")"));
if (NS_FAILED(rv)) return rv;
@ -1903,8 +1924,11 @@ nsCookieService::GetCookieStringCommon(nsIURI *aHostURI,
bool isPrivate = aChannel && NS_UsePrivateBrowsing(aChannel);
nsAutoCString result;
GetCookieStringInternal(aHostURI, isForeign, aHttpBound, attrs,
isPrivate, result);
bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, aHostURI);
GetCookieStringInternal(aHostURI, isForeign, aHttpBound,
isSafeTopLevelNav, isSameSiteForeign,
attrs, isPrivate, result);
*aCookie = result.IsEmpty() ? nullptr : ToNewCString(result);
return NS_OK;
}
@ -2262,7 +2286,8 @@ nsCookieService::Add(const nsACString &aHost,
bool aIsSecure,
bool aIsHttpOnly,
bool aIsSession,
int64_t aExpiry)
int64_t aExpiry,
int32_t aSameSite)
{
if (!mDBState) {
NS_WARNING("No DBState! Profile already closed?");
@ -2289,7 +2314,8 @@ nsCookieService::Add(const nsACString &aHost,
nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
aIsSession,
aIsSecure,
aIsHttpOnly);
aIsHttpOnly,
aSameSite);
if (!cookie) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -2386,7 +2412,8 @@ nsCookieService::Read()
"isSecure, "
"isHttpOnly, "
"baseDomain, "
"originAttributes "
"originAttributes, "
"sameSite "
"FROM moz_cookies "
"WHERE baseDomain NOTNULL"), getter_AddRefs(stmtRead));
NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
@ -2447,6 +2474,7 @@ nsCookieService::GetCookieFromRow(T &aRow)
int64_t creationTime = aRow->AsInt64(IDX_CREATION_TIME);
bool isSecure = 0 != aRow->AsInt32(IDX_SECURE);
bool isHttpOnly = 0 != aRow->AsInt32(IDX_HTTPONLY);
int32_t sameSite = aRow->AsInt32(IDX_SAME_SITE);
// Create a new nsCookie and assign the data.
return nsCookie::Create(name, value, host, path,
@ -2455,7 +2483,8 @@ nsCookieService::GetCookieFromRow(T &aRow)
creationTime,
false,
isSecure,
isHttpOnly);
isHttpOnly,
sameSite);
}
void
@ -2555,7 +2584,10 @@ nsCookieService::EnsureReadDomain(const nsCookieKey &aKey)
"lastAccessed, "
"creationTime, "
"isSecure, "
"isHttpOnly "
"isHttpOnly, "
"baseDomain, "
"originAttributes, "
"sameSite "
"FROM moz_cookies "
"WHERE baseDomain = :baseDomain "
" AND originAttributes = :originAttributes"),
@ -2648,7 +2680,8 @@ nsCookieService::EnsureReadComplete()
"isSecure, "
"isHttpOnly, "
"baseDomain, "
"originAttributes "
"originAttributes, "
"sameSite "
"FROM moz_cookies "
"WHERE baseDomain NOTNULL"), getter_AddRefs(stmt));
@ -2846,7 +2879,8 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
false,
Substring(buffer, secureIndex, expiresIndex - secureIndex - 1).EqualsLiteral(kTrue),
isHttpOnly);
isHttpOnly,
nsICookie2::SAMESITE_UNSET);
if (!newCookie) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -2922,6 +2956,8 @@ void
nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
bool aIsForeign,
bool aHttpBound,
bool aIsSafeTopLevelNav,
bool aIsSameSiteForeign,
const NeckoOriginAttributes aOriginAttrs,
bool aIsPrivate,
nsCString &aCookieString)
@ -3004,6 +3040,21 @@ nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
if (cookie->IsSecure() && !isSecure)
continue;
int32_t sameSiteAttr = 0;
cookie->GetSameSite(&sameSiteAttr);
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) {
continue;
}
}
// if the cookie is httpOnly and it's not going directly to the HTTP
// connection, don't send it
if (cookie->IsHttpOnly() && !aHttpBound)
@ -3220,10 +3271,30 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
cookieAttributes.isSession,
cookieAttributes.isSecure,
cookieAttributes.isHttpOnly);
cookieAttributes.isHttpOnly,
cookieAttributes.sameSite);
if (!cookie)
return newCookie;
// If the new cookie is same-site but in a cross site context,
// browser must ignore the cookie.
if (IsSameSiteEnabled() &&
cookieAttributes.sameSite != nsICookie2::SAMESITE_UNSET) {
bool isThirdParty = false;
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
do_GetService(THIRDPARTYUTIL_CONTRACTID);
if (thirdPartyUtil) {
thirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isThirdParty);
} else {
NS_WARNING("failed to get third party service with same-site cookie");
}
if (isThirdParty) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
"failed the samesite tests");
return newCookie;
}
}
// check permissions from site permission list, or ask the user,
// to determine if we can set the cookie
if (mPermissionService) {
@ -3439,6 +3510,8 @@ nsCookieService::AddInternal(const nsCookieKey &aKey,
6. Attribute "HttpOnly", not covered in the RFCs, is supported
(see bug 178993).
7. Attribute "sameSite" is supported (TenFourFox issue 499).
** Begin BNF:
token = 1*<any allowed-chars except separators>
value = 1*<any allowed-chars except value-sep>
@ -3460,6 +3533,7 @@ nsCookieService::AddInternal(const nsCookieKey &aKey,
NAME = token ; cookie name
VALUE = value ; cookie value
cookie-av = token ["=" value]
same-site-val = "lax" | "strict"
valid values for cookie-av (checked post-parsing) are:
cookie-av = "Path" "=" value
@ -3470,6 +3544,7 @@ nsCookieService::AddInternal(const nsCookieKey &aKey,
| "Version" "=" value
| "Secure"
| "HttpOnly"
| "SameSite" "=" same-site-val
******************************************************************************/
@ -3556,6 +3631,9 @@ nsCookieService::ParseAttributes(nsDependentCString &aCookieHeader,
static const char kMaxage[] = "max-age";
static const char kSecure[] = "secure";
static const char kHttpOnly[] = "httponly";
static const char kSameSite[] = "samesite";
static const char kSameSiteLax[] = "lax";
static const char kSameSiteStrict[] = "strict";
nsASingleFragmentCString::const_char_iterator tempBegin, tempEnd;
nsASingleFragmentCString::const_char_iterator cookieStart, cookieEnd;
@ -3564,6 +3642,7 @@ nsCookieService::ParseAttributes(nsDependentCString &aCookieHeader,
aCookieAttributes.isSecure = false;
aCookieAttributes.isHttpOnly = false;
aCookieAttributes.sameSite = nsICookie2::SAMESITE_UNSET;
nsDependentCSubstring tokenString(cookieStart, cookieStart);
nsDependentCSubstring tokenValue (cookieStart, cookieStart);
@ -3612,6 +3691,16 @@ nsCookieService::ParseAttributes(nsDependentCString &aCookieHeader,
// just set the boolean
else if (tokenString.LowerCaseEqualsLiteral(kHttpOnly))
aCookieAttributes.isHttpOnly = true;
else if (tokenString.LowerCaseEqualsLiteral(kSameSite)) {
if (tokenValue.LowerCaseEqualsLiteral(kSameSiteLax)) {
aCookieAttributes.sameSite = nsICookie2::SAMESITE_LAX;
} else if (tokenValue.LowerCaseEqualsLiteral(kSameSiteStrict)) {
aCookieAttributes.sameSite = nsICookie2::SAMESITE_STRICT;
} else {
NS_WARNING("unknown sameSite attribute on cookie, dropped");
}
}
}
// rebind aCookieHeader, in case we need to process another cookie
@ -3699,6 +3788,18 @@ nsCookieService::GetBaseDomainFromHost(const nsACString &aHost,
return rv;
}
bool
nsCookieService::IsSameSiteEnabled()
{
static bool prefInitialized = false;
if (!prefInitialized) {
Preferences::AddBoolVarCache(&sSameSiteEnabled,
"network.cookie.same-site.enabled", false);
prefInitialized = true;
}
return sSameSiteEnabled;
}
// Normalizes the given hostname, component by component. ASCII/ACE
// components are lower-cased, and UTF-8 components are normalized per
// RFC 3454 and converted to ACE.
@ -4501,6 +4602,10 @@ bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
aCookie->IsHttpOnly());
NS_ASSERT_SUCCESS(rv);
rv = params->BindInt32ByName(NS_LITERAL_CSTRING("sameSite"),
aCookie->SameSite());
NS_ASSERT_SUCCESS(rv);
// Bind the params to the array.
rv = aParamsArray->AddParams(params);
NS_ASSERT_SUCCESS(rv);

View File

@ -247,6 +247,8 @@ class nsCookieService final : public nsICookieService
private:
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
static bool sSameSiteEnabled; // cached pref
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
@ -288,11 +290,12 @@ class nsCookieService final : public nsICookieService
void CancelAsyncRead(bool aPurgeReadSet);
void EnsureReadDomain(const nsCookieKey &aKey);
void EnsureReadComplete();
bool IsSameSiteEnabled();
nsresult NormalizeHost(nsCString &aHost);
nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch);
nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
nsresult GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie);
void GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const NeckoOriginAttributes aOriginAttrs, bool aIsPrivate, nsCString &aCookie);
void GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, bool aIsSafeTopLevelNav, bool aIsTopLevelForeign, const NeckoOriginAttributes aOriginAttrs, bool aIsPrivate, nsCString &aCookie);
nsresult SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp);
void SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, const NeckoOriginAttributes &aOriginAttrs, bool aIsPrivate, nsIChannel* aChannel);
bool SetCookieInternal(nsIURI *aHostURI, const nsCookieKey& aKey, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp, nsIChannel* aChannel);

View File

@ -11,10 +11,16 @@
* access of cookie objects
*/
[scriptable, uuid(05c420e5-03d0-4c7b-a605-df7ebe5ca326)]
// Rev slightly different UUID for TenFourFox since we
// are, indeed, slightly different than Firefox (see
// TenFourFix issue 499).
[scriptable, uuid(be205daf-4f4c-11e6-80ba-ea5cd310c1a8)]
interface nsICookie2 : nsICookie
{
const uint32_t SAMESITE_UNSET = 0;
const uint32_t SAMESITE_LAX = 1;
const uint32_t SAMESITE_STRICT = 2;
/**
* the host (possibly fully qualified) of the cookie,
@ -60,4 +66,15 @@ interface nsICookie2 : nsICookie
*/
readonly attribute int64_t lastAccessed;
/**
* the sameSite attribute; this controls the cookie behavior for cross-site
* requests as per
* https://tools.ietf.org/html/draft-west-first-party-cookies-07
*
* This should be one of:
* - SAMESITE_UNSET - the SameSite attribute is not present
* - SAMESITE_LAX - the SameSite attribute is present, but not strict
* - SAMESITE_STRICT - the SameSite attribute is present and strict
*/
readonly attribute int32_t sameSite;
};

View File

@ -46,6 +46,9 @@ interface nsICookieManager2 : nsICookieManager
* expiration date, in seconds since midnight (00:00:00), January 1,
* 1970 UTC. note that expiry time will also be honored for session cookies;
* in this way, the more restrictive of the two will take effect.
* @param aSameSite
* the SameSite attribute. This attribute is optional to avoid breaking
* addons
*/
void add(in AUTF8String aHost,
in AUTF8String aPath,
@ -54,7 +57,8 @@ interface nsICookieManager2 : nsICookieManager
in boolean aIsSecure,
in boolean aIsHttpOnly,
in boolean aIsSession,
in int64_t aExpiry);
in int64_t aExpiry,
[optional] in int32_t aSameSite);
/**
* Find whether a given cookie already exists.

View File

@ -214,6 +214,9 @@ NS_INTERFACE_MAP_BEGIN(HttpBaseChannel)
NS_INTERFACE_MAP_ENTRY(nsIPrivateBrowsingChannel)
NS_INTERFACE_MAP_ENTRY(nsITimedChannel)
NS_INTERFACE_MAP_ENTRY(nsIConsoleReportCollector)
if (aIID.Equals(NS_GET_IID(HttpBaseChannel))) {
foundInterface = static_cast<nsIWritablePropertyBag*>(this);
} else
NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag)
//-----------------------------------------------------------------------------

View File

@ -47,6 +47,11 @@ class nsPerformance;
class nsISecurityConsoleMessage;
class nsIPrincipal;
#define HTTP_BASE_CHANNEL_IID \
{ 0x9d5cde03, 0xe6e9, 0x4612, \
{ 0xbf, 0xef, 0xbb, 0x66, 0xf3, 0xbb, 0x74, 0x46 } }
namespace mozilla {
class LogCollector;
@ -86,6 +91,8 @@ public:
NS_DECL_NSITRACEABLECHANNEL
NS_DECL_NSITIMEDCHANNEL
NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_BASE_CHANNEL_IID)
HttpBaseChannel();
virtual nsresult Init(nsIURI *aURI, uint32_t aCaps, nsProxyInfo *aProxyInfo,
@ -497,6 +504,8 @@ protected:
bool mForceMainDocumentChannel;
};
NS_DEFINE_STATIC_IID_ACCESSOR(HttpBaseChannel, HTTP_BASE_CHANNEL_IID)
// Share some code while working around C++'s absurd inability to handle casting
// of member functions between base/derived types.
// - We want to store member function pointer to call at resume time, but one