Bug 1548349 - Make sure the image cache for third-party tracking subresources is keyed to the top-level document's eTLD+1; r=baku,aosmond

Differential Revision: https://phabricator.services.mozilla.com/D29546

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Ehsan Akhgari 2019-05-02 12:27:07 +00:00
Родитель 50b2b648b6
Коммит 27938631aa
2 изменённых файлов: 43 добавлений и 8 удалений

Просмотреть файл

@ -13,10 +13,12 @@
#include "nsLayoutUtils.h" #include "nsLayoutUtils.h"
#include "nsString.h" #include "nsString.h"
#include "mozilla/AntiTrackingCommon.h" #include "mozilla/AntiTrackingCommon.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/dom/BlobURLProtocolHandler.h" #include "mozilla/dom/BlobURLProtocolHandler.h"
#include "mozilla/dom/File.h" #include "mozilla/dom/File.h"
#include "mozilla/dom/ServiceWorkerManager.h" #include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/dom/Document.h" #include "mozilla/dom/Document.h"
#include "nsHashKeys.h"
#include "nsPrintfCString.h" #include "nsPrintfCString.h"
namespace mozilla { namespace mozilla {
@ -43,6 +45,7 @@ ImageCacheKey::ImageCacheKey(nsIURI* aURI, const OriginAttributes& aAttrs,
: mURI(aURI), : mURI(aURI),
mOriginAttributes(aAttrs), mOriginAttributes(aAttrs),
mControlledDocument(GetSpecialCaseDocumentToken(aDocument, aURI)), mControlledDocument(GetSpecialCaseDocumentToken(aDocument, aURI)),
mTopLevelBaseDomain(GetTopLevelBaseDomain(aDocument, aURI)),
mIsChrome(false) { mIsChrome(false) {
if (SchemeIs("blob")) { if (SchemeIs("blob")) {
mBlobSerial = BlobSerial(mURI); mBlobSerial = BlobSerial(mURI);
@ -57,6 +60,7 @@ ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
mBlobRef(aOther.mBlobRef), mBlobRef(aOther.mBlobRef),
mOriginAttributes(aOther.mOriginAttributes), mOriginAttributes(aOther.mOriginAttributes),
mControlledDocument(aOther.mControlledDocument), mControlledDocument(aOther.mControlledDocument),
mTopLevelBaseDomain(aOther.mTopLevelBaseDomain),
mHash(aOther.mHash), mHash(aOther.mHash),
mIsChrome(aOther.mIsChrome) {} mIsChrome(aOther.mIsChrome) {}
@ -66,6 +70,7 @@ ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
mBlobRef(std::move(aOther.mBlobRef)), mBlobRef(std::move(aOther.mBlobRef)),
mOriginAttributes(aOther.mOriginAttributes), mOriginAttributes(aOther.mOriginAttributes),
mControlledDocument(aOther.mControlledDocument), mControlledDocument(aOther.mControlledDocument),
mTopLevelBaseDomain(aOther.mTopLevelBaseDomain),
mHash(aOther.mHash), mHash(aOther.mHash),
mIsChrome(aOther.mIsChrome) {} mIsChrome(aOther.mIsChrome) {}
@ -75,6 +80,12 @@ bool ImageCacheKey::operator==(const ImageCacheKey& aOther) const {
if (mControlledDocument != aOther.mControlledDocument) { if (mControlledDocument != aOther.mControlledDocument) {
return false; return false;
} }
// Don't share the image cache between two top-level documents of different
// base domains.
if (!mTopLevelBaseDomain.Equals(aOther.mTopLevelBaseDomain,
nsCaseInsensitiveCStringComparator())) {
return false;
}
// The origin attributes always have to match. // The origin attributes always have to match.
if (mOriginAttributes != aOther.mOriginAttributes) { if (mOriginAttributes != aOther.mOriginAttributes) {
return false; return false;
@ -127,7 +138,8 @@ void ImageCacheKey::EnsureHash() const {
hash = HashString(spec); hash = HashString(spec);
} }
hash = AddToHash(hash, HashString(suffix), HashString(ptr)); hash = AddToHash(hash, HashString(suffix), HashString(mTopLevelBaseDomain),
HashString(ptr));
mHash.emplace(hash); mHash.emplace(hash);
} }
@ -153,27 +165,44 @@ void* ImageCacheKey::GetSpecialCaseDocumentToken(Document* aDocument,
return aDocument; return aDocument;
} }
return nullptr;
}
/* static */
nsCString ImageCacheKey::GetTopLevelBaseDomain(Document* aDocument,
nsIURI* aURI) {
if (!aDocument || !aDocument->GetInnerWindow()) {
return EmptyCString();
}
// If the window is 3rd party resource, let's see if first-party storage // If the window is 3rd party resource, let's see if first-party storage
// access is granted for this image. // access is granted for this image.
if (nsContentUtils::IsThirdPartyTrackingResourceWindow( if (nsContentUtils::IsThirdPartyTrackingResourceWindow(
aDocument->GetInnerWindow())) { aDocument->GetInnerWindow())) {
return nsContentUtils::StorageDisabledByAntiTracking(aDocument, aURI) return nsContentUtils::StorageDisabledByAntiTracking(aDocument, aURI)
? aDocument ? aDocument->GetBaseDomain()
: nullptr; : EmptyCString();
} }
// Another scenario is if this image is a 3rd party resource loaded by a // Another scenario is if this image is a 3rd party resource loaded by a
// first party context. In this case, we should check if the nsIChannel has // first party context. In this case, we should check if the nsIChannel has
// been marked as tracking resource, but we don't have the channel yet at // been marked as tracking resource, but we don't have the channel yet at
// this point. The best approach here is to be conservative: if we are sure // this point. The best approach here is to be conservative: if we are sure
// that the permission is granted, let's return a nullptr. Otherwise, let's // that the permission is granted, let's return 0. Otherwise, let's make a
// make a unique image cache. // unique image cache per the top-level document eTLD+1.
if (!AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor( if (!AntiTrackingCommon::MaybeIsFirstPartyStorageAccessGrantedFor(
aDocument->GetInnerWindow(), aURI)) { aDocument->GetInnerWindow(), aURI)) {
return aDocument; nsPIDOMWindowOuter* top = aDocument->GetInnerWindow()->GetScriptableTop();
nsPIDOMWindowInner* topInner = top->GetCurrentInnerWindow();
if (!topInner) {
return aDocument
->GetBaseDomain(); // because we don't have anything better!
}
return topInner->GetExtantDoc() ? topInner->GetExtantDoc()->GetBaseDomain()
: EmptyCString();
} }
return nullptr; return EmptyCString();
} }
} // namespace image } // namespace image

Просмотреть файл

@ -61,11 +61,16 @@ class ImageCacheKey final {
private: private:
bool SchemeIs(const char* aScheme); bool SchemeIs(const char* aScheme);
// For ServiceWorker and for anti-tracking we need to use the document as // For ServiceWorker we need to use the document as
// token for the key. All those exceptions are handled by this method. // token for the key. All those exceptions are handled by this method.
static void* GetSpecialCaseDocumentToken(dom::Document* aDocument, static void* GetSpecialCaseDocumentToken(dom::Document* aDocument,
nsIURI* aURI); nsIURI* aURI);
// For anti-tracking we need to use the top-level document's base domain for
// the key. This is handled by this method.
static nsCString GetTopLevelBaseDomain(dom::Document* aDocument,
nsIURI* aURI);
void EnsureHash() const; void EnsureHash() const;
void EnsureBlobRef() const; void EnsureBlobRef() const;
@ -74,6 +79,7 @@ class ImageCacheKey final {
mutable nsCString mBlobRef; mutable nsCString mBlobRef;
OriginAttributes mOriginAttributes; OriginAttributes mOriginAttributes;
void* mControlledDocument; void* mControlledDocument;
nsCString mTopLevelBaseDomain;
mutable Maybe<PLDHashNumber> mHash; mutable Maybe<PLDHashNumber> mHash;
bool mIsChrome; bool mIsChrome;
}; };