commit b6be541aaa22422ecf9e70680282aa9c7dc05f06 Author: Kathy Brade brade@pearlcrescent.com Date: Wed Feb 4 10:38:44 2015 -0500
Bug 13900: Remove 3rd party HTTP auth tokens.
Prevent user tracking via HTTP Basic Authentication by removing Authorization headers from third party requests.
This is a port of a piece of the Stanford SafeCache code that previously was included in Torbutton. --- content/base/src/ThirdPartyUtil.cpp | 21 ++++++--- netwerk/base/public/mozIThirdPartyUtil.idl | 22 ++++++++- netwerk/protocol/http/nsHttpChannel.cpp | 70 ++++++++++++++++++++++++++++ netwerk/protocol/http/nsHttpChannel.h | 1 + 4 files changed, 107 insertions(+), 7 deletions(-)
diff --git a/content/base/src/ThirdPartyUtil.cpp b/content/base/src/ThirdPartyUtil.cpp index e2287eb..695392f 100644 --- a/content/base/src/ThirdPartyUtil.cpp +++ b/content/base/src/ThirdPartyUtil.cpp @@ -412,20 +412,28 @@ ThirdPartyUtil::GetBaseDomain(nsIURI* aHostURI, return NS_OK; }
-// Returns true if First Party Isolation is currently active for the given nsIChannel. -// Depends on Preference setting and possibly the state of Private Browsing mode. -bool ThirdPartyUtil::IsFirstPartyIsolationActive(nsIChannel *aChannel, nsIDocument *aDoc) +// Determine if First Party Isolation is currently active for the given +// nsIChannel or nsIDocument. Depends on preference setting and +// possibly the state of Private Browsing mode. +NS_IMETHODIMP +ThirdPartyUtil::IsFirstPartyIsolationActive(nsIChannel *aChannel, + nsIDocument *aDoc, + bool* aResult) { + NS_ASSERTION(aResult, "null outparam pointer"); + int32_t isolationState = mozilla::Preferences::GetInt("privacy.thirdparty.isolate"); if (isolationState == 1) { if (!aChannel && aDoc) { // No channel passed directly. Can we get a channel from aDoc? aChannel = aDoc->GetChannel(); } - return aChannel && NS_UsePrivateBrowsing(aChannel); + *aResult = aChannel && NS_UsePrivateBrowsing(aChannel); } else { // (isolationState == 0) || (isolationState == 2) - return (isolationState == 2); + *aResult = (isolationState == 2); } + + return NS_OK; }
// Produces a URI that uniquely identifies the first party to which @@ -435,7 +443,8 @@ bool ThirdPartyUtil::IsFirstPartyIsolationActive(nsIChannel *aChannel, nsIDocume NS_IMETHODIMP ThirdPartyUtil::GetFirstPartyIsolationURI(nsIChannel *aChannel, nsIDocument *aDoc, nsIURI **aOutput) { - bool isolationActive = IsFirstPartyIsolationActive(aChannel, aDoc); + bool isolationActive = false; + (void)IsFirstPartyIsolationActive(aChannel, aDoc, &isolationActive); if (isolationActive) { return GetFirstPartyURI(aChannel, aDoc, aOutput); } else { diff --git a/netwerk/base/public/mozIThirdPartyUtil.idl b/netwerk/base/public/mozIThirdPartyUtil.idl index 87fb630..0bb632b 100644 --- a/netwerk/base/public/mozIThirdPartyUtil.idl +++ b/netwerk/base/public/mozIThirdPartyUtil.idl @@ -13,7 +13,7 @@ interface nsIDocument; * Utility functions for determining whether a given URI, channel, or window * hierarchy is third party with respect to a known URI. */ -[scriptable, uuid(d994fd1d-d2fe-4372-9ae7-88b08b7d9d90)] +[scriptable, uuid(b538074d-5e00-40b3-b5c4-e060ae010b83)] interface mozIThirdPartyUtil : nsISupports { /** @@ -165,6 +165,26 @@ interface mozIThirdPartyUtil : nsISupports in nsIDocument aDoc);
/** + * isFirstPartyIsolationActive + * + * Determine if First Party Isolation is currently active for the given + * nsIChannel or nsIDocument. Depends on preference setting and + * possibly the state of Private Browsing mode. + * + * @param aChannel + * An arbitrary channel for some content element of a first party + * load. Can be null. + * + * @param aDoc + * An arbitrary third party document. Can be null. + * + * @return true if first party isolation is active. + */ + + [noscript] bool isFirstPartyIsolationActive(in nsIChannel aChannel, + in nsIDocument aDoc); + + /** * getFirstPartyIsolationURI * * If first-party isolation is active, then diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index b37b60b..59ba71b 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -4600,6 +4600,8 @@ nsHttpChannel::BeginConnect() // notify "http-on-modify-request" observers CallOnModifyRequestObservers();
+ RemoveAuthorizationHeaderIfAppropriate(); + // Check to see if we should redirect this channel elsewhere by // nsIHttpChannel.redirectTo API request if (mAPIRedirectToURI) { @@ -5750,6 +5752,72 @@ nsHttpChannel::ResumeAt(uint64_t aStartPos, return NS_OK; }
+// Remove the Authorization header if first party isolation is active and +// this channel is processing a third party request. This prevents user +// tracking via HTTP Basic Authentication. +// Note that this approach disables authentication for 3rd party domains. It +// would be better if we could isolate the authentication while still allowing +// it to be transmitted... but HTTP authentication is rarely used anyway. +void +nsHttpChannel::RemoveAuthorizationHeaderIfAppropriate() +{ + if (!mRequestHead.PeekHeader(nsHttp::Authorization)) { + return; // No Authorization header is present. + } + + nsCOMPtr<mozIThirdPartyUtil> thirdPartySvc + = do_GetService(THIRDPARTYUTIL_CONTRACTID); + bool isolationActive = true; + (void)thirdPartySvc->IsFirstPartyIsolationActive(this, nullptr, + &isolationActive); + if (!isolationActive) + return; // First party isolation is disabled for this channel. + + bool isAuthAllowed = false; + nsCOMPtr<nsIURI> firstPartyURI; + nsresult rv = thirdPartySvc->GetFirstPartyURIFromChannel(this, false, + getter_AddRefs(firstPartyURI)); + if (NS_SUCCEEDED(rv) && firstPartyURI) { + isAuthAllowed = (mURI == firstPartyURI) + || HostPartIsTheSame(firstPartyURI); + } else { + // We failed to get the first party URI. Check the document URI so + // that we can allow authentication if the request originates from the + // the browser chrome, e.g., some favicon requests. If there is no + // document URI associated with this request, it cannot be associated + // with a content document, so it must be a special request (e.g., + // favicon fetch or OSCP), for which we also allow authenication. + nsCOMPtr<nsIURI> docURI; + rv = GetDocumentURI(getter_AddRefs(docURI)); + if (NS_FAILED(rv) || !docURI) { + isAuthAllowed = true; + } else { + nsAutoCString docURISpec; + docURI->GetAsciiSpec(docURISpec); + if (docURISpec == "chrome://browser/content/browser.xul") + isAuthAllowed = true; + } + } + + if (!isAuthAllowed) { + mRequestHead.ClearHeader(nsHttp::Authorization); + mRequestHead.ClearHeader(nsHttp::Cache_Control); + mRequestHead.ClearHeader(nsHttp::Pragma); + +#ifdef PR_LOGGING + nsAutoCString requestURIStr, firstPartyURIStr; + mURI->GetAsciiSpec(requestURIStr); + if (firstPartyURI) + firstPartyURI->GetAsciiSpec(firstPartyURIStr); + else + firstPartyURIStr = "--N/A--"; + LOG(("Removed Authorization header from third party request" + " [Request URI=%s, First Party URI=%s]\n", + requestURIStr.get(), firstPartyURIStr.get())); +#endif + } +} + nsresult nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn) { @@ -5771,6 +5839,8 @@ nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn) // notify "http-on-modify-request" observers CallOnModifyRequestObservers();
+ RemoveAuthorizationHeaderIfAppropriate(); + mIsPending = true;
// get rid of the old response headers diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 8d3d23d..a76ed8e 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -282,6 +282,7 @@ private: nsresult ProcessPartialContent(); nsresult OnDoneReadingPartialCacheEntry(bool *streamDone);
+ void RemoveAuthorizationHeaderIfAppropriate(); nsresult DoAuthRetry(nsAHttpConnection *);
void HandleAsyncRedirectChannelToHttps();