Bug 1452496: Discard same-site cookie in cross site context. r=valentin a=jcristau
authorChristoph Kerschbaumer <ckerschb@christophkerschbaumer.com>
Thu, 12 Apr 2018 12:52:51 +0200
changeset 463279 84c492af301f887e9281cbe10d89d85e874921b0
parent 463278 eafece5d7f8c745759548a982f25ebd37455f863
child 463280 7aa5a506eaa06fb5c5a8f5e699d986fd47676c11
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvalentin, jcristau
bugs1452496
milestone60.0
Bug 1452496: Discard same-site cookie in cross site context. r=valentin a=jcristau
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
@@ -2147,38 +2147,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
@@ -685,19 +685,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,