Bug 1529836 - Part 1: Add support for capping the maximum life-time of client-side cookies; r=baku
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 26 Feb 2019 18:34:37 +0000
changeset 519118 4a2df9ca56e1b5384ea8af8029b641bbd9c48b3f
parent 519117 c6e08ed3909cfd0227f444df63d0de4064b263e5
child 519119 db866e98d64e31f40dc49815125412afdb11c59d
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1529836
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1529836 - Part 1: Add support for capping the maximum life-time of client-side cookies; r=baku Differential Revision: https://phabricator.services.mozilla.com/D20780
modules/libpref/init/StaticPrefList.h
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsCookieService.h
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1936,16 +1936,23 @@ VARCACHE_PREF(
 
 // Anti-tracking user-interaction document interval
 VARCACHE_PREF(
   "privacy.userInteraction.document.interval",
    privacy_userInteraction_document_interval,
   uint32_t, 1800 // 30 minutes (in seconds)
 )
 
+// Maximum client-side cookie life-time cap
+VARCACHE_PREF(
+  "privacy.documentCookies.maxage",
+   privacy_documentCookies_maxage,
+  uint32_t, 0 // Disabled (in seconds, set to 0 to disable)
+)
+
 // Anti-fingerprinting, disabled by default
 VARCACHE_PREF(
   "privacy.resistFingerprinting",
    privacy_resistFingerprinting,
   RelaxedAtomicBool, false
 )
 
 VARCACHE_PREF(
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -3270,18 +3270,19 @@ bool nsCookieService::CanSetCookie(nsIUR
                                 (isHTTPS ? 0x02 : 0x00) |
                                 (aCookieAttributes.isSecure ? 0x01 : 0x00));
     }
   }
 
   int64_t currentTimeInUsec = PR_Now();
 
   // calculate expiry time of cookie.
-  aCookieAttributes.isSession = GetExpiry(aCookieAttributes, aServerTime,
-                                          currentTimeInUsec / PR_USEC_PER_SEC);
+  aCookieAttributes.isSession =
+      GetExpiry(aCookieAttributes, aServerTime,
+                currentTimeInUsec / PR_USEC_PER_SEC, aFromHttp);
   if (aStatus == STATUS_ACCEPT_SESSION) {
     // force lifetime to session. note that the expiration time, if set above,
     // will still apply.
     aCookieAttributes.isSession = true;
   }
 
   // reject cookie if it's over the size limit, per RFC2109
   if ((aCookieAttributes.name.Length() + aCookieAttributes.value.Length()) >
@@ -4230,17 +4231,23 @@ bool nsCookieService::CheckPrefixes(nsCo
       return false;
     }
   }
 
   return true;
 }
 
 bool nsCookieService::GetExpiry(nsCookieAttributes &aCookieAttributes,
-                                int64_t aServerTime, int64_t aCurrentTime) {
+                                int64_t aServerTime, int64_t aCurrentTime,
+                                bool aFromHttp) {
+  // maxageCap is in seconds.
+  // Disabled for HTTP cookies.
+  int64_t maxageCap =
+      aFromHttp ? 0 : StaticPrefs::privacy_documentCookies_maxage();
+
   /* Determine when the cookie should expire. This is done by taking the
    * difference between the server time and the time the server wants the cookie
    * to expire, and adding that difference to the client time. This localizes
    * the client time regardless of whether or not the TZ environment variable
    * was set on the client.
    *
    * Note: We need to consider accounting for network lag here, per RFC.
    */
@@ -4253,36 +4260,47 @@ bool nsCookieService::GetExpiry(nsCookie
 
     // default to session cookie if the conversion failed
     if (numInts != 1) {
       return true;
     }
 
     // if this addition overflows, expiryTime will be less than currentTime
     // and the cookie will be expired - that's okay.
-    aCookieAttributes.expiryTime = aCurrentTime + maxage;
+    if (maxageCap) {
+      aCookieAttributes.expiryTime = aCurrentTime + std::min(maxage, maxageCap);
+    } else {
+      aCookieAttributes.expiryTime = aCurrentTime + maxage;
+    }
 
     // check for expires attribute
   } else if (!aCookieAttributes.expires.IsEmpty()) {
     PRTime expires;
 
     // parse expiry time
     if (PR_ParseTimeString(aCookieAttributes.expires.get(), true, &expires) !=
         PR_SUCCESS) {
       return true;
     }
 
     // If set-cookie used absolute time to set expiration, and it can't use
     // client time to set expiration.
     // Because if current time be set in the future, but the cookie expire
     // time be set less than current time and more than server time.
     // The cookie item have to be used to the expired cookie.
-    aCookieAttributes.expiryTime = expires / int64_t(PR_USEC_PER_SEC);
-
-    // default to session cookie if no attributes found
+    if (maxageCap) {
+      aCookieAttributes.expiryTime = std::min(
+          expires / int64_t(PR_USEC_PER_SEC), aCurrentTime + maxageCap);
+    } else {
+      aCookieAttributes.expiryTime = expires / int64_t(PR_USEC_PER_SEC);
+    }
+
+    // default to session cookie if no attributes found.  Here we don't need to
+    // enforce the maxage cap, because session cookies are short-lived by
+    // definition.
   } else {
     return true;
   }
 
   return false;
 }
 
 /******************************************************************************
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -339,17 +339,17 @@ class nsCookieService final : public nsI
   static bool ParseAttributes(nsDependentCString &aCookieHeader,
                               nsCookieAttributes &aCookie);
   bool RequireThirdPartyCheck();
   static bool CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI,
                           const nsCString &aBaseDomain, bool aRequireHostMatch);
   static bool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
   static bool CheckPrefixes(nsCookieAttributes &aCookie, bool aSecureRequest);
   static bool GetExpiry(nsCookieAttributes &aCookie, int64_t aServerTime,
-                        int64_t aCurrentTime);
+                        int64_t aCurrentTime, bool aFromHttp);
   void RemoveAllFromMemory();
   already_AddRefed<nsIArray> PurgeCookies(int64_t aCurrentTimeInUsec);
   bool FindCookie(const nsCookieKey &aKey, const nsCString &aHost,
                   const nsCString &aName, const nsCString &aPath,
                   nsListIter &aIter);
   bool FindSecureCookie(const nsCookieKey &aKey, nsCookie *aCookie);
   void FindStaleCookies(nsCookieEntry *aEntry, int64_t aCurrentTime,
                         bool aIsSecure, nsTArray<nsListIter> &aOutput,