diff options
author | jchaffraix@chromium.org <jchaffraix@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-07 23:39:41 +0000 |
---|---|---|
committer | jchaffraix@chromium.org <jchaffraix@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-07 23:39:41 +0000 |
commit | 4c06905d140438b2b06a96c2b07867ac981e19e5 (patch) | |
tree | 11ea6e191df1fe0605ef3a0103a18e40b9487114 /net | |
parent | 4c616541cf6e73dfebaf3cd68128c1e49e7d111b (diff) | |
download | chromium_src-4c06905d140438b2b06a96c2b07867ac981e19e5.zip chromium_src-4c06905d140438b2b06a96c2b07867ac981e19e5.tar.gz chromium_src-4c06905d140438b2b06a96c2b07867ac981e19e5.tar.bz2 |
Revert 120836 - Don't use IDENT_SRC_URL for HttpAuth challenges. IE hasn't supported it for years, and at worst it represents a session fixation attack.
BUG=94578
Review URL: https://chromiumcodereview.appspot.com/9307093
TBR=tsepez@chromium.org
Review URL: https://chromiumcodereview.appspot.com/9365001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@120857 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_auth_controller.cc | 18 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 153 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 31 |
3 files changed, 192 insertions, 10 deletions
diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc index 07565a1..8a23969 100644 --- a/net/http/http_auth_controller.cc +++ b/net/http/http_auth_controller.cc @@ -450,17 +450,21 @@ bool HttpAuthController::SelectNextAuthIdentityToTry() { DCHECK(handler_.get()); DCHECK(identity_.invalid); - // Do not try to use the username:password encoded into the URL. At worst, - // this represents a session fixation attack against basic auth, and as it - // turns out, IE hasn't supported this for years. If a caller really wants - // to use embedded identities, the can add an URLRequest::Delegate that - // inspects the URL and supplies the username/password at OnAuthRequired() - // time. Past data shows this is used extremely infrequently in web pages, - // but continue to collect this data. + // Try to use the username:password encoded into the URL first. if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() && !embedded_identity_used_) { + identity_.source = HttpAuth::IDENT_SRC_URL; + identity_.invalid = false; + // Extract the username:password from the URL. + string16 username; + string16 password; + GetIdentityFromURL(auth_url_, &username, &password); + identity_.credentials.Set(username, password); embedded_identity_used_ = true; + // TODO(eroman): If the password is blank, should we also try combining + // with a password from the cache? UMA_HISTOGRAM_BOOLEAN("net.HttpIdentSrcURL", true); + return true; } // Check the auth cache for a realm entry. diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index b961fdd..75b6a98 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -3718,12 +3718,12 @@ TEST_F(HttpNetworkTransactionTest, ResendRequestOnWriteBodyError) { // Test the request-challenge-retry sequence for basic auth when there is // an identity in the URL. The request should be sent as normal, but when -// it fails the identity from the URL is no longer used. -TEST_F(HttpNetworkTransactionTest, IgnoreAuthIdentityInURL) { +// it fails the identity from the URL is used to answer the challenge. +TEST_F(HttpNetworkTransactionTest, AuthIdentityInURL) { HttpRequestInfo request; request.method = "GET"; + // Note: the URL has a username:password in it. request.url = GURL("http://foo:b@r@www.google.com/"); - request.load_flags = LOAD_NORMAL; SessionDependencies session_deps; scoped_ptr<HttpTransaction> trans( @@ -3733,6 +3733,8 @@ TEST_F(HttpNetworkTransactionTest, IgnoreAuthIdentityInURL) { // will need to be unescaped by HttpNetworkTransaction. EXPECT_EQ("b%40r", request.url.password()); + request.load_flags = LOAD_NORMAL; + MockWrite data_writes1[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -3746,17 +3748,162 @@ TEST_F(HttpNetworkTransactionTest, IgnoreAuthIdentityInURL) { MockRead(false, ERR_FAILED), }; + // After the challenge above, the transaction will be restarted using the + // identity from the url (foo, b@r) to answer the challenge. + MockWrite data_writes2[] = { + MockWrite("GET / HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJAcg==\r\n\r\n"), + }; + + MockRead data_reads2[] = { + MockRead("HTTP/1.0 200 OK\r\n"), + MockRead("Content-Length: 100\r\n\r\n"), + MockRead(false, OK), + }; + StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), data_writes1, arraysize(data_writes1)); + StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), + data_writes2, arraysize(data_writes2)); session_deps.socket_factory.AddSocketDataProvider(&data1); + session_deps.socket_factory.AddSocketDataProvider(&data2); TestCompletionCallback callback1; + int rv = trans->Start(&request, callback1.callback(), BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + + EXPECT_TRUE(trans->IsReadyToRestartForAuth()); + TestCompletionCallback callback2; + rv = trans->RestartWithAuth(AuthCredentials(), callback2.callback()); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback2.WaitForResult(); + EXPECT_EQ(OK, rv); EXPECT_FALSE(trans->IsReadyToRestartForAuth()); + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + + // There is no challenge info, since the identity in URL worked. + EXPECT_TRUE(response->auth_challenge.get() == NULL); + + EXPECT_EQ(100, response->headers->GetContentLength()); + + // Empty the current queue. + MessageLoop::current()->RunAllPending(); +} + +// Test the request-challenge-retry sequence for basic auth when there is +// an incorrect identity in the URL. The identity from the URL should be used +// only once. +TEST_F(HttpNetworkTransactionTest, WrongAuthIdentityInURL) { + HttpRequestInfo request; + request.method = "GET"; + // Note: the URL has a username:password in it. The password "baz" is + // wrong (should be "bar"). + request.url = GURL("http://foo:baz@www.google.com/"); + + request.load_flags = LOAD_NORMAL; + + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + + MockWrite data_writes1[] = { + MockWrite("GET / HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Connection: keep-alive\r\n\r\n"), + }; + + MockRead data_reads1[] = { + MockRead("HTTP/1.0 401 Unauthorized\r\n"), + MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"), + MockRead("Content-Length: 10\r\n\r\n"), + MockRead(false, ERR_FAILED), + }; + + // After the challenge above, the transaction will be restarted using the + // identity from the url (foo, baz) to answer the challenge. + MockWrite data_writes2[] = { + MockWrite("GET / HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJheg==\r\n\r\n"), + }; + + MockRead data_reads2[] = { + MockRead("HTTP/1.0 401 Unauthorized\r\n"), + MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"), + MockRead("Content-Length: 10\r\n\r\n"), + MockRead(false, ERR_FAILED), + }; + + // After the challenge above, the transaction will be restarted using the + // identity supplied by the user (foo, bar) to answer the challenge. + MockWrite data_writes3[] = { + MockWrite("GET / HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Connection: keep-alive\r\n" + "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + }; + + MockRead data_reads3[] = { + MockRead("HTTP/1.0 200 OK\r\n"), + MockRead("Content-Length: 100\r\n\r\n"), + MockRead(false, OK), + }; + + StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), + data_writes1, arraysize(data_writes1)); + StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), + data_writes2, arraysize(data_writes2)); + StaticSocketDataProvider data3(data_reads3, arraysize(data_reads3), + data_writes3, arraysize(data_writes3)); + session_deps.socket_factory.AddSocketDataProvider(&data1); + session_deps.socket_factory.AddSocketDataProvider(&data2); + session_deps.socket_factory.AddSocketDataProvider(&data3); + + TestCompletionCallback callback1; + + int rv = trans->Start(&request, callback1.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback1.WaitForResult(); + EXPECT_EQ(OK, rv); + + EXPECT_TRUE(trans->IsReadyToRestartForAuth()); + TestCompletionCallback callback2; + rv = trans->RestartWithAuth(AuthCredentials(), callback2.callback()); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback2.WaitForResult(); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(trans->IsReadyToRestartForAuth()); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get())); + + TestCompletionCallback callback3; + rv = trans->RestartWithAuth( + AuthCredentials(kFoo, kBar), callback3.callback()); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback3.WaitForResult(); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(trans->IsReadyToRestartForAuth()); + + response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + + // There is no challenge info, since the identity worked. + EXPECT_TRUE(response->auth_challenge.get() == NULL); + + EXPECT_EQ(100, response->headers->GetContentLength()); + // Empty the current queue. MessageLoop::current()->RunAllPending(); } diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 287e21b..bafbd6f 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -2465,6 +2465,37 @@ TEST_F(URLRequestTestHTTP, BasicAuthWithCookies) { EXPECT_TRUE(d.data_received().find("Cookie: got_challenged=true") != std::string::npos); } + + // Same test as above, except this time the restart is initiated earlier + // (without user intervention since identity is embedded in the URL). + { + TestNetworkDelegate network_delegate; // must outlive URLRequest + scoped_refptr<TestURLRequestContext> context( + new TestURLRequestContext(true)); + context->set_network_delegate(&network_delegate); + context->Init(); + + TestDelegate d; + + GURL::Replacements replacements; + std::string username("user2"); + std::string password("secret"); + replacements.SetUsernameStr(username); + replacements.SetPasswordStr(password); + GURL url_with_identity = url_requiring_auth.ReplaceComponents(replacements); + + URLRequest r(url_with_identity, &d); + r.set_context(context); + r.Start(); + + MessageLoop::current()->Run(); + + EXPECT_TRUE(d.data_received().find("user2/secret") != std::string::npos); + + // Make sure we sent the cookie in the restarted transaction. + EXPECT_TRUE(d.data_received().find("Cookie: got_challenged=true") + != std::string::npos); + } } TEST_F(URLRequestTest, DelayedCookieCallback) { |