Bug 1452496: Discard same-site cookie in cross site context. r=valentin
authorChristoph Kerschbaumer <ckerschb@christophkerschbaumer.com>
Thu, 12 Apr 2018 12:52:51 +0200
changeset 412997 453b91e427d6cafe70ef97993be6444ab57cb22c
parent 412996 ae45e56c3c71ae23084e9ef549c3d58880add8a1
child 412998 532a1bb332ba3d10caaccecdc00c574df553ed20
push id33829
push userarchaeopteryx@coole-files.de
push dateThu, 12 Apr 2018 19:20:32 +0000
treeherdermozilla-central@17dda59473c3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvalentin
bugs1452496
milestone61.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 1452496: Discard same-site cookie in cross site context. r=valentin
netwerk/base/nsNetUtil.cpp
netwerk/base/nsNetUtil.h
netwerk/cookie/CookieServiceChild.cpp
netwerk/cookie/CookieServiceChild.h
netwerk/cookie/CookieServiceParent.cpp
netwerk/cookie/CookieServiceParent.h
netwerk/cookie/PCookieService.ipdl
netwerk/cookie/nsCookieService.cpp
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2130,38 +2130,41 @@ NS_IsSafeTopLevelNav(nsIChannel* aChanne
   }
   nsHttpRequestHead *requestHead = baseChan->GetRequestHead();
   if (!requestHead) {
     return false;
   }
   return requestHead->IsSafeMethod();
 }
 
-bool NS_IsTopLevelForeign(nsIChannel* aChannel)
+bool NS_IsSameSiteForeign(nsIChannel* aChannel, nsIURI* aHostURI)
 {
   if (!aChannel) {
     return false;
   }
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   if (!loadInfo) {
     return false;
   }
-  if (loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT) {
-    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;
   }
 
-  nsCOMPtr<nsIURI> uri;
-  loadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(uri));
-
   bool isForeign = false;
   thirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
   return isForeign;
 }
 
 bool
 NS_ShouldCheckAppCache(nsIPrincipal *aPrincipal)
 {
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -680,19 +680,21 @@ bool NS_GetOriginAttributes(nsIChannel *
 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 navigation.
+ * 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_IsTopLevelForeign(nsIChannel* aChannel);
+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
 #define NECKO_UNKNOWN_APP_ID UINT32_MAX
 
 // Unique first-party domain for separating the safebrowsing cookie.
 // Note if this value is changed, code in test_cookiejars_safebrowsing.js and
--- a/netwerk/cookie/CookieServiceChild.cpp
+++ b/netwerk/cookie/CookieServiceChild.cpp
@@ -128,19 +128,19 @@ CookieServiceChild::TrackCookieLoad(nsIC
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   mozilla::OriginAttributes attrs;
   if (loadInfo) {
     attrs = loadInfo->GetOriginAttributes();
   }
   URIParams uriParams;
   SerializeURI(uri, uriParams);
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
-  bool isTopLevelForeign = NS_IsTopLevelForeign(aChannel);
+  bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, uri);
   SendPrepareCookieList(uriParams, isForeign, isSafeTopLevelNav,
-                        isTopLevelForeign, attrs);
+                        isSameSiteForeign, attrs);
 }
 
 mozilla::ipc::IPCResult
 CookieServiceChild::RecvRemoveAll(){
   mCookiesMap.Clear();
   return IPC_OK();
 }
 
@@ -256,17 +256,17 @@ CookieServiceChild::PrefChanged(nsIPrefB
     NS_ASSERTION(mThirdPartyUtil, "require ThirdPartyUtil service");
   }
 }
 
 void
 CookieServiceChild::GetCookieStringFromCookieHashTable(nsIURI                 *aHostURI,
                                                        bool                   aIsForeign,
                                                        bool                   aIsSafeTopLevelNav,
-                                                       bool                   aIsTopLevelForeign,
+                                                       bool                   aIsSameSiteForeign,
                                                        const OriginAttributes &aOriginAttrs,
                                                        nsCString              &aCookieString)
 {
   nsCOMPtr<nsIEffectiveTLDService> TLDService =
     do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   NS_ASSERTION(TLDService, "Can't get TLDService");
   bool requireHostMatch;
   nsAutoCString baseDomain;
@@ -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 (aIsForeign || aIsTopLevelForeign) {
+    if (aIsSameSiteForeign) {
       // 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) {
@@ -350,24 +350,24 @@ CookieServiceChild::GetCookieStringFromC
     }
   }
 }
 
 void
 CookieServiceChild::GetCookieStringSyncIPC(nsIURI                 *aHostURI,
                                            bool                   aIsForeign,
                                            bool                   aIsSafeTopLevelNav,
-                                           bool                   aIsTopLevelForeign,
+                                           bool                   aIsSameSiteForeign,
                                            const OriginAttributes &aAttrs,
                                            nsAutoCString          &aCookieString)
 {
   URIParams uriParams;
   SerializeURI(aHostURI, uriParams);
 
-  SendGetCookieString(uriParams, aIsForeign, aIsSafeTopLevelNav, aIsTopLevelForeign, aAttrs, &aCookieString);
+  SendGetCookieString(uriParams, aIsForeign, aIsSafeTopLevelNav, aIsSameSiteForeign, aAttrs, &aCookieString);
 }
 
 uint32_t
 CookieServiceChild::CountCookiesFromHashTable(const nsCString &aBaseDomain,
                                               const OriginAttributes &aOriginAttrs)
 {
   CookiesList *cookiesList = nullptr;
 
@@ -482,28 +482,28 @@ CookieServiceChild::GetCookieStringInter
   }
 
   // Asynchronously call the parent.
   bool isForeign = true;
   if (RequireThirdPartyCheck())
     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
-  bool isTopLevelForeign = NS_IsTopLevelForeign(aChannel);
+  bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, aHostURI);
 
   nsAutoCString result;
   if (!mIPCSync) {
     GetCookieStringFromCookieHashTable(aHostURI, !!isForeign, isSafeTopLevelNav,
-                                       isTopLevelForeign, attrs, result);
+                                       isSameSiteForeign, attrs, result);
   } else {
     if (!mIPCOpen) {
       return NS_ERROR_NOT_AVAILABLE;
     }
     GetCookieStringSyncIPC(aHostURI, !!isForeign, isSafeTopLevelNav,
-                           isTopLevelForeign, attrs, result);
+                           isSameSiteForeign, attrs, result);
   }
 
   if (!result.IsEmpty())
     *aCookieString = ToNewCString(result);
 
   return NS_OK;
 }
 
@@ -529,30 +529,36 @@ CookieServiceChild::SetCookieStringInter
   if (RequireThirdPartyCheck())
     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   nsDependentCString cookieString(aCookieString);
   nsDependentCString stringServerTime;
   if (aServerTime)
     stringServerTime.Rebind(aServerTime);
 
-  URIParams uriParams;
-  SerializeURI(aHostURI, uriParams);
+  URIParams hostURIParams;
+  SerializeURI(aHostURI, hostURIParams);
+
+  nsCOMPtr<nsIURI> channelURI;
+  aChannel->GetURI(getter_AddRefs(channelURI));
+  URIParams channelURIParams;
+  SerializeURI(channelURI, channelURIParams);
 
   mozilla::OriginAttributes attrs;
   if (aChannel) {
     nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
     if (loadInfo) {
       attrs = loadInfo->GetOriginAttributes();
     }
   }
 
   // Asynchronously call the parent.
   if (mIPCOpen) {
-    SendSetCookieString(uriParams, !!isForeign, cookieString,
+    SendSetCookieString(hostURIParams, channelURIParams,
+                        !!isForeign, cookieString,
                         stringServerTime, attrs, aFromHttp);
   }
 
   if (mIPCSync) {
     return NS_OK;
   }
 
   bool requireHostMatch;
--- a/netwerk/cookie/CookieServiceChild.h
+++ b/netwerk/cookie/CookieServiceChild.h
@@ -57,25 +57,25 @@ protected:
 
   nsresult GetCookieStringInternal(nsIURI *aHostURI,
                                    nsIChannel *aChannel,
                                    char **aCookieString);
 
   void GetCookieStringFromCookieHashTable(nsIURI *aHostURI,
                                           bool aIsForeign,
                                           bool aIsSafeTopLevelNav,
-                                          bool aIsTopLevelForeign,
+                                          bool aIsSameSiteForeign,
                                           const OriginAttributes &aAttrs,
                                           nsCString &aCookieString);
 
   void
   GetCookieStringSyncIPC(nsIURI                 *aHostURI,
                          bool                    aIsForeign,
                          bool                    aIsSafeTopLevelNav,
-                         bool                    aIsTopLevelForeign,
+                         bool                    aIsSameSiteForeign,
                          const OriginAttributes &aAttrs,
                          nsAutoCString          &aCookieString);
 
   nsresult SetCookieStringInternal(nsIURI *aHostURI,
                                    nsIChannel *aChannel,
                                    const char *aCookieString,
                                    const char *aServerTime,
                                    bool aFromHttp);
--- a/netwerk/cookie/CookieServiceParent.cpp
+++ b/netwerk/cookie/CookieServiceParent.cpp
@@ -24,34 +24,29 @@ using mozilla::OriginAttributes;
 using mozilla::dom::PContentParent;
 using mozilla::net::NeckoParent;
 
 namespace {
 
 // Ignore failures from this function, as they only affect whether we do or
 // don't show a dialog box in private browsing mode if the user sets a pref.
 void
-CreateDummyChannel(nsIURI* aHostURI, OriginAttributes& aAttrs, nsIChannel** aChannel)
+CreateDummyChannel(nsIURI* aHostURI, nsIURI* aChannelURI,
+                   OriginAttributes& aAttrs, nsIChannel** aChannel)
 {
   nsCOMPtr<nsIPrincipal> principal =
     BasePrincipal::CreateCodebasePrincipal(aHostURI, aAttrs);
   if (!principal) {
     return;
   }
 
-  nsCOMPtr<nsIURI> dummyURI;
-  nsresult rv = NS_NewURI(getter_AddRefs(dummyURI), "about:blank");
-  if (NS_FAILED(rv)) {
-      return;
-  }
-
   // The following channel is never openend, so it does not matter what
   // securityFlags we pass; let's follow the principle of least privilege.
   nsCOMPtr<nsIChannel> dummyChannel;
-  NS_NewChannel(getter_AddRefs(dummyChannel), dummyURI, principal,
+  NS_NewChannel(getter_AddRefs(dummyChannel), aChannelURI, principal,
                 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
                 nsIContentPolicy::TYPE_INVALID);
   nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(dummyChannel);
   if (!pbChannel) {
     return;
   }
 
   pbChannel->SetPrivate(aAttrs.mPrivateBrowsingId > 0);
@@ -151,25 +146,25 @@ CookieServiceParent::TrackCookieLoad(nsI
   aChannel->GetURI(getter_AddRefs(uri));
 
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   mozilla::OriginAttributes attrs;
   if (loadInfo) {
     attrs = loadInfo->GetOriginAttributes();
   }
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
-  bool isTopLevelForeign = NS_IsTopLevelForeign(aChannel);
+  bool aIsSameSiteForeign = NS_IsSameSiteForeign(aChannel, uri);
 
   // Send matching cookies to Child.
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil;
   thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
   bool isForeign = true;
   thirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
   nsTArray<nsCookie*> foundCookieList;
-  mCookieService->GetCookiesForURI(uri, isForeign, isSafeTopLevelNav, isTopLevelForeign,
+  mCookieService->GetCookiesForURI(uri, isForeign, isSafeTopLevelNav, aIsSameSiteForeign,
                                    false, attrs, foundCookieList);
   nsTArray<CookieStruct> matchingCookiesList;
   SerialializeCookieList(foundCookieList, matchingCookiesList, uri);
   Unused << SendTrackCookiesLoad(matchingCookiesList, attrs);
 }
 
 void
 CookieServiceParent::SerialializeCookieList(const nsTArray<nsCookie*> &aFoundCookieList,
@@ -191,24 +186,24 @@ CookieServiceParent::SerialializeCookieL
     cookieStruct->sameSite() = cookie->SameSite();
   }
 }
 
 mozilla::ipc::IPCResult
 CookieServiceParent::RecvPrepareCookieList(const URIParams        &aHost,
                                            const bool             &aIsForeign,
                                            const bool             &aIsSafeTopLevelNav,
-                                           const bool             &aIsTopLevelForeign,
+                                           const bool             &aIsSameSiteForeign,
                                            const OriginAttributes &aAttrs)
 {
   nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
 
   // Send matching cookies to Child.
   nsTArray<nsCookie*> foundCookieList;
-  mCookieService->GetCookiesForURI(hostURI, aIsForeign, aIsSafeTopLevelNav, aIsTopLevelForeign,
+  mCookieService->GetCookiesForURI(hostURI, aIsForeign, aIsSafeTopLevelNav, aIsSameSiteForeign,
                                    false, aAttrs, foundCookieList);
   nsTArray<CookieStruct> matchingCookiesList;
   SerialializeCookieList(foundCookieList, matchingCookiesList, hostURI);
   Unused << SendTrackCookiesLoad(matchingCookiesList, aAttrs);
   return IPC_OK();
 }
 
 void
@@ -217,59 +212,65 @@ CookieServiceParent::ActorDestroy(ActorD
   // Nothing needed here. Called right before destructor since this is a
   // non-refcounted class.
 }
 
 mozilla::ipc::IPCResult
 CookieServiceParent::RecvGetCookieString(const URIParams& aHost,
                                          const bool& aIsForeign,
                                          const bool& aIsSafeTopLevelNav,
-                                         const bool& aIsToplevelForeign,
+                                         const bool& aIsSameSiteForeign,
                                          const OriginAttributes& aAttrs,
                                          nsCString* aResult)
 {
   if (!mCookieService)
     return IPC_OK();
 
   // Deserialize URI. Having a host URI is mandatory and should always be
   // provided by the child; thus we consider failure fatal.
   nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
   if (!hostURI)
     return IPC_FAIL_NO_REASON(this);
-  mCookieService->GetCookieStringInternal(hostURI, aIsForeign, aIsSafeTopLevelNav, aIsToplevelForeign,
+  mCookieService->GetCookieStringInternal(hostURI, aIsForeign, aIsSafeTopLevelNav, aIsSameSiteForeign,
                                           false, aAttrs, *aResult);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 CookieServiceParent::RecvSetCookieString(const URIParams& aHost,
+                                         const URIParams& aChannelURI,
                                          const bool& aIsForeign,
                                          const nsCString& aCookieString,
                                          const nsCString& aServerTime,
                                          const OriginAttributes& aAttrs,
                                          const bool& aFromHttp)
 {
   if (!mCookieService)
     return IPC_OK();
 
   // Deserialize URI. Having a host URI is mandatory and should always be
   // provided by the child; thus we consider failure fatal.
   nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
   if (!hostURI)
     return IPC_FAIL_NO_REASON(this);
 
+  nsCOMPtr<nsIURI> channelURI = DeserializeURI(aChannelURI);
+  if (!channelURI)
+    return IPC_FAIL_NO_REASON(this);
+
   // This is a gross hack. We've already computed everything we need to know
   // for whether to set this cookie or not, but we need to communicate all of
   // this information through to nsICookiePermission, which indirectly
   // computes the information from the channel. We only care about the
   // aIsPrivate argument as nsCookieService::SetCookieStringInternal deals
   // with aIsForeign before we have to worry about nsCookiePermission trying
   // to use the channel to inspect it.
   nsCOMPtr<nsIChannel> dummyChannel;
-  CreateDummyChannel(hostURI, const_cast<OriginAttributes&>(aAttrs),
+  CreateDummyChannel(hostURI, channelURI,
+                     const_cast<OriginAttributes&>(aAttrs),
                      getter_AddRefs(dummyChannel));
 
   // NB: dummyChannel could be null if something failed in CreateDummyChannel.
   nsDependentCString cookieString(aCookieString, 0);
   mCookieService->SetCookieStringInternal(hostURI, aIsForeign, cookieString,
                                           aServerTime, aFromHttp, true, aAttrs,
                                           dummyChannel);
   return IPC_OK();
--- a/netwerk/cookie/CookieServiceParent.h
+++ b/netwerk/cookie/CookieServiceParent.h
@@ -33,31 +33,32 @@ public:
   void AddCookie(nsICookie *aCookie);
 
 protected:
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual mozilla::ipc::IPCResult RecvGetCookieString(const URIParams& aHost,
                                                       const bool& aIsForeign,
                                                       const bool& aIsSafeTopLevelNav,
-                                                      const bool& aIsTopLevelForeign,
+                                                      const bool& aIsSameSiteForeign,
                                                       const OriginAttributes& aAttrs,
                                                       nsCString* aResult) override;
 
   virtual mozilla::ipc::IPCResult RecvSetCookieString(const URIParams& aHost,
+                                                      const URIParams& aChannelURI,
                                                       const bool& aIsForeign,
                                                       const nsCString& aCookieString,
                                                       const nsCString& aServerTime,
                                                       const OriginAttributes& aAttrs,
                                                       const bool& aFromHttp) override;
   virtual
   mozilla::ipc::IPCResult RecvPrepareCookieList(const URIParams &aHost,
                                                 const bool &aIsForeign,
                                                 const bool &aIsSafeTopLevelNav,
-                                                const bool &aIsTopLevelForeign,
+                                                const bool &aIsSameSiteForeign,
                                                 const OriginAttributes &aAttrs) override;
 
   void
   SerialializeCookieList(const nsTArray<nsCookie*> &aFoundCookieList,
                          nsTArray<CookieStruct> &aCookiesList,
                          nsIURI *aHostURI);
 
   RefPtr<nsCookieService> mCookieService;
--- a/netwerk/cookie/PCookieService.ipdl
+++ b/netwerk/cookie/PCookieService.ipdl
@@ -41,16 +41,20 @@ parent:
    * @param host
    *        Same as the 'aURI' argument to nsICookieService.getCookieString.
    * @param isForeign
    *        True if the the request is third party, for purposes of allowing
    *        access to cookies. This should be obtained from
    *        mozIThirdPartyUtil.isThirdPartyChannel. Third party requests may be
    *        rejected depending on user preferences; if those checks are
    *        disabled, this parameter is ignored.
+   * @param isSafeTopLevelNav
+   *        True for safe methods like e.g. GET.
+   * @param isSameSiteForeign
+   *        True if the same-site cookie originates from a cross-site context.
    * @param fromHttp
    *        Whether the result is for an HTTP request header. This should be
    *        true for nsICookieService.getCookieStringFromHttp calls, false
    *        otherwise.
    * @param attrs
    *        The origin attributes from the HTTP channel or document that the
    *        cookie is being set on.
    *
@@ -58,25 +62,27 @@ parent:
    * @see nsICookieService.getCookieStringFromHttp
    * @see mozIThirdPartyUtil.isThirdPartyChannel
    *
    * @return the resulting cookie string.
    */
   nested(inside_cpow) sync GetCookieString(URIParams host,
                                            bool isForeign,
                                            bool isSafeTopLevelNav,
-                                           bool isTopLevelForeign,
+                                           bool isSameSiteForeign,
                                            OriginAttributes attrs)
        returns (nsCString result);
 
   /*
    * Set a cookie string.
    *
    * @param host
    *        Same as the 'aURI' argument to nsICookieService.setCookieString.
+   * @param channelURI
+   *        The URI of the request.
    * @param isForeign
    *        True if the the request is third party, for purposes of allowing
    *        access to cookies. This should be obtained from
    *        mozIThirdPartyUtil.isThirdPartyChannel. Third party requests may be
    *        rejected depending on user preferences; if those checks are
    *        disabled, this parameter is ignored.
    * @param cookieString
    *        Same as the 'aCookie' argument to nsICookieService.setCookieString.
@@ -92,26 +98,27 @@ parent:
    *        The origin attributes from the HTTP channel or document that the
    *        cookie is being set on.
    *
    * @see nsICookieService.setCookieString
    * @see nsICookieService.setCookieStringFromHttp
    * @see mozIThirdPartyUtil.isThirdPartyChannel
    */
   nested(inside_cpow) async SetCookieString(URIParams host,
+                                            URIParams channelURI,
                                             bool isForeign,
                                             nsCString cookieString,
                                             nsCString serverTime,
                                             OriginAttributes attrs,
                                             bool aFromHttp);
 
   async PrepareCookieList(URIParams host,
                           bool isForeign,
                           bool isSafeTopLevelNav,
-                          bool isTopLevelForeign,
+                          bool isSameSiteForeign,
                           OriginAttributes attrs);
 
   async __delete__();
 
 child:
   async TrackCookiesLoad(CookieStruct[] cookiesList,
                          OriginAttributes attrs);
 
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -2036,19 +2036,19 @@ nsCookieService::GetCookieStringCommon(n
 
   // Get originAttributes.
   OriginAttributes attrs;
   if (aChannel) {
     NS_GetOriginAttributes(aChannel, attrs);
   }
 
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
-  bool isTopLevelForeign = NS_IsTopLevelForeign(aChannel);
+  bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, aHostURI);
   nsAutoCString result;
-  GetCookieStringInternal(aHostURI, isForeign, isSafeTopLevelNav, isTopLevelForeign,
+  GetCookieStringInternal(aHostURI, isForeign, isSafeTopLevelNav, isSameSiteForeign,
                           aHttpBound, attrs, result);
   *aCookie = result.IsEmpty() ? nullptr : ToNewCString(result);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCookieService::SetCookieString(nsIURI     *aHostURI,
                                  nsIPrompt  *aPrompt,
@@ -3107,17 +3107,17 @@ nsCookieService::PathMatches(nsCookie* a
   // the given path.
   return true;
 }
 
 void
 nsCookieService::GetCookiesForURI(nsIURI *aHostURI,
                                   bool aIsForeign,
                                   bool aIsSafeTopLevelNav,
-                                  bool aIsTopLevelForeign,
+                                  bool aIsSameSiteForeign,
                                   bool aHttpBound,
                                   const OriginAttributes& aOriginAttrs,
                                   nsTArray<nsCookie*>& aCookieList)
 {
   NS_ASSERTION(aHostURI, "null host!");
 
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
@@ -3199,17 +3199,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 (aIsForeign || aIsTopLevelForeign) {
+    if (aIsSameSiteForeign) {
       // 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) {
@@ -3280,23 +3280,23 @@ nsCookieService::GetCookiesForURI(nsIURI
   // then sort by creation time (see bug 236772).
   aCookieList.Sort(CompareCookiesForSending());
 }
 
 void
 nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
                                          bool aIsForeign,
                                          bool aIsSafeTopLevelNav,
-                                         bool aIsTopLevelForeign,
+                                         bool aIsSameSiteForeign,
                                          bool aHttpBound,
                                          const OriginAttributes& aOriginAttrs,
                                          nsCString &aCookieString)
 {
   AutoTArray<nsCookie*, 8> foundCookieList;
-  GetCookiesForURI(aHostURI, aIsForeign, aIsSafeTopLevelNav, aIsTopLevelForeign,
+  GetCookiesForURI(aHostURI, aIsForeign, aIsSafeTopLevelNav, aIsSameSiteForeign,
                    aHttpBound, aOriginAttrs, foundCookieList);
 
   nsCookie* cookie;
   for (uint32_t i = 0; i < foundCookieList.Length(); ++i) {
     cookie = foundCookieList.ElementAt(i);
 
     // check if we have anything to write
     if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
@@ -3464,16 +3464,27 @@ nsCookieService::CanSetCookie(nsIURI*   
   if (aLeaveSecureAlone && aCookieAttributes.isSecure && !isSecure) {
     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
       "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) {
+    bool isThirdParty = NS_IsSameSiteForeign(aChannel, aHostURI);
+    if (isThirdParty) {
+      COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
+                        "failed the samesite tests");
+      return newCookie;
+    }
+  }
+
   aSetCookie = true;
   return newCookie;
 }
 
 // processes a single cookie, and returns true if there are more cookies
 // to be processed
 bool
 nsCookieService::SetCookieInternal(nsIURI                        *aHostURI,