summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortsepez@chromium.org <tsepez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-13 16:45:12 +0000
committertsepez@chromium.org <tsepez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-13 16:45:12 +0000
commit2fb95db2941727bb10a6eedaee3a1bef0af00a1c (patch)
treedb6ada547274d0ccb5a571ced56f6283d97f5f05
parent6a8f51186bb732bbeb40ef39eb87fb2ba7d882bb (diff)
downloadchromium_src-2fb95db2941727bb10a6eedaee3a1bef0af00a1c.zip
chromium_src-2fb95db2941727bb10a6eedaee3a1bef0af00a1c.tar.gz
chromium_src-2fb95db2941727bb10a6eedaee3a1bef0af00a1c.tar.bz2
Block HTTP basic auth from cross-orgin third-party content.
BUG=81251 TEST=browser_tests Review URL: http://codereview.chromium.org/6918001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@85281 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/ui/login/login_prompt_browsertest.cc68
-rw-r--r--chrome/common/chrome_switches.cc4
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/test/data/login/load_img_from_b.html14
-rw-r--r--content/browser/renderer_host/resource_dispatcher_host.cc44
5 files changed, 131 insertions, 0 deletions
diff --git a/chrome/browser/ui/login/login_prompt_browsertest.cc b/chrome/browser/ui/login/login_prompt_browsertest.cc
index d8b29d3..ba2bd8c 100644
--- a/chrome/browser/ui/login/login_prompt_browsertest.cc
+++ b/chrome/browser/ui/login/login_prompt_browsertest.cc
@@ -17,6 +17,7 @@
#include "content/browser/renderer_host/resource_dispatcher_host.h"
#include "content/common/notification_service.h"
#include "net/base/auth.h"
+#include "net/base/mock_host_resolver.h"
namespace {
@@ -474,4 +475,71 @@ IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, NoLoginPromptForFavicon) {
EXPECT_TRUE(test_server()->Stop());
}
+// Block crossdomain subresource login prompting as a phishing defense.
+IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, BlockCrossdomainPrompt) {
+ const char* kTestPage = "files/login/load_img_from_b.html";
+
+ host_resolver()->AddRule("www.a.com", "127.0.0.1");
+ host_resolver()->AddRule("www.b.com", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+
+ TabContentsWrapper* contents = browser()->GetSelectedTabContentsWrapper();
+ ASSERT_TRUE(contents);
+
+ NavigationController* controller = &contents->controller();
+ LoginPromptBrowserTestObserver observer;
+ observer.Register(Source<NavigationController>(controller));
+
+ // Load a page that has a cross-domain sub-resource authentication.
+ // There should be no login prompt.
+ {
+ GURL test_page = test_server()->GetURL(kTestPage);
+ ASSERT_EQ("127.0.0.1", test_page.host());
+
+ // Change the host from 127.0.0.1 to www.a.com so that when the
+ // page tries to load from b, it will be cross-origin.
+ std::string new_host("www.a.com");
+ GURL::Replacements replacements;
+ replacements.SetHostStr(new_host);
+ test_page = test_page.ReplaceComponents(replacements);
+
+ WindowedLoadStopObserver load_stop_waiter(controller);
+ browser()->OpenURL(test_page, GURL(), CURRENT_TAB, PageTransition::TYPED);
+ load_stop_waiter.Wait();
+ }
+
+ EXPECT_EQ(0, observer.auth_needed_count_);
+
+ // Now request the same page, but from the same origin.
+ // There should be one login prompt.
+ {
+ GURL test_page = test_server()->GetURL(kTestPage);
+ ASSERT_EQ("127.0.0.1", test_page.host());
+
+ // Change the host from 127.0.0.1 to www.b.com so that when the
+ // page tries to load from b, it will be same-origin.
+ std::string new_host("www.b.com");
+ GURL::Replacements replacements;
+ replacements.SetHostStr(new_host);
+ test_page = test_page.ReplaceComponents(replacements);
+
+ WindowedAuthNeededObserver auth_needed_waiter(controller);
+ browser()->OpenURL(test_page, GURL(), CURRENT_TAB, PageTransition::TYPED);
+ auth_needed_waiter.Wait();
+ ASSERT_EQ(1u, observer.handlers_.size());
+
+ while (!observer.handlers_.empty()) {
+ WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
+ LoginHandler* handler = *observer.handlers_.begin();
+
+ ASSERT_TRUE(handler);
+ handler->CancelAuth();
+ auth_cancelled_waiter.Wait();
+ }
+ }
+
+ EXPECT_EQ(1, observer.auth_needed_count_);
+ EXPECT_TRUE(test_server()->Stop());
+}
+
} // namespace
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 253dd26..1f58615 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -18,6 +18,10 @@ namespace switches {
// is launched on the command line (e.g. by Selenium). Only needed on Mac.
const char kActivateOnLaunch[] = "activate-on-launch";
+// Allow third party content included on a page to prompt for a HTTP
+// basic auth username/password pair.
+const char kAllowCrossOriginAuthPrompt[] = "allow-cross-origin-auth-prompt";
+
// On ChromeOS, file:// access is disabled except for certain whitelisted
// directories. This switch re-enables file:// for testing.
const char kAllowFileAccess[] = "allow-file-access";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index e7e4721..c4876d7 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -24,6 +24,7 @@ namespace switches {
// All switches in alphabetical order. The switches should be documented
// alongside the definition of their values in the .cc file.
extern const char kActivateOnLaunch[];
+extern const char kAllowCrossOriginAuthPrompt[];
extern const char kAllowFileAccess[];
extern const char kAllowOutdatedPlugins[];
extern const char kAllowHTTPBackgroundPage[];
diff --git a/chrome/test/data/login/load_img_from_b.html b/chrome/test/data/login/load_img_from_b.html
new file mode 100644
index 0000000..96b93fc
--- /dev/null
+++ b/chrome/test/data/login/load_img_from_b.html
@@ -0,0 +1,14 @@
+<html>
+<body>
+This page will attempt to load an image requiring basic auth from the
+domain www.b.com using the same port that it loaded (since we assume www.b.com
+is an alias for this same server).
+
+<script>
+var img = document.createElement("img");
+img.src = "http://www.b.com:" + location.port + "/auth-basic/foo.jpg";
+document.body.appendChild(img);
+</script>
+</body>
+</html>
+
diff --git a/content/browser/renderer_host/resource_dispatcher_host.cc b/content/browser/renderer_host/resource_dispatcher_host.cc
index fac9a705..aeee6e6 100644
--- a/content/browser/renderer_host/resource_dispatcher_host.cc
+++ b/content/browser/renderer_host/resource_dispatcher_host.cc
@@ -68,6 +68,7 @@
#include "net/base/load_flags.h"
#include "net/base/mime_util.h"
#include "net/base/net_errors.h"
+#include "net/base/registry_controlled_domain.h"
#include "net/base/request_priority.h"
#include "net/base/ssl_cert_request_info.h"
#include "net/base/upload_data.h"
@@ -236,6 +237,32 @@ void RemoveDownloadFileFromChildSecurityPolicy(int child_id,
#pragma warning(default: 4748)
#endif
+// Relationship of resource being authenticated with the top level page.
+enum HttpAuthResourceType {
+ HTTP_AUTH_RESOURCE_TOP, // Top-level page itself
+ HTTP_AUTH_RESOURCE_SAME_DOMAIN, // Sub-content from same domain
+ HTTP_AUTH_RESOURCE_BLOCKED_CROSS, // Blocked Sub-content from cross domain
+ HTTP_AUTH_RESOURCE_ALLOWED_CROSS, // Allowed Sub-content per command line
+ HTTP_AUTH_RESOURCE_LAST
+};
+
+HttpAuthResourceType HttpAuthResourceTypeOf(net::URLRequest* request) {
+ // Use the same critera as for cookies to determine the sub-resource type
+ // that is requesting to be authenticated.
+ if (!request->first_party_for_cookies().is_valid())
+ return HTTP_AUTH_RESOURCE_TOP;
+
+ if (net::RegistryControlledDomainService::SameDomainOrHost(
+ request->first_party_for_cookies(), request->url()))
+ return HTTP_AUTH_RESOURCE_SAME_DOMAIN;
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAllowCrossOriginAuthPrompt))
+ return HTTP_AUTH_RESOURCE_ALLOWED_CROSS;
+
+ return HTTP_AUTH_RESOURCE_BLOCKED_CROSS;
+}
+
} // namespace
ResourceDispatcherHost::ResourceDispatcherHost(
@@ -1089,6 +1116,23 @@ void ResourceDispatcherHost::OnAuthRequired(
request->CancelAuth();
return;
}
+
+ // Prevent third-party content from prompting for login, unless it is
+ // a proxy that is trying to authenticate. This is often the foundation
+ // of a scam to extract credentials for another domain from the user.
+ if (!auth_info->is_proxy) {
+ HttpAuthResourceType resource_type = HttpAuthResourceTypeOf(request);
+ UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthResource",
+ resource_type,
+ HTTP_AUTH_RESOURCE_LAST);
+
+ if (resource_type == HTTP_AUTH_RESOURCE_BLOCKED_CROSS) {
+ request->CancelAuth();
+ return;
+ }
+ }
+
+
// Create a login dialog on the UI thread to get authentication data,
// or pull from cache and continue on the IO thread.
// TODO(mpcomplete): We should block the parent tab while waiting for