summaryrefslogtreecommitdiffstats
path: root/net/http
diff options
context:
space:
mode:
authordarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-20 08:14:39 +0000
committerdarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-20 08:14:39 +0000
commitcd5b9a73feb4a4178973ccd571b277fcdd83e590 (patch)
tree56dee2f3bc2065f4f44b39d3f8dbc25c568a2dde /net/http
parent5103a768ef9ba423590b7417baa3f1bda6daa829 (diff)
downloadchromium_src-cd5b9a73feb4a4178973ccd571b277fcdd83e590.zip
chromium_src-cd5b9a73feb4a4178973ccd571b277fcdd83e590.tar.gz
chromium_src-cd5b9a73feb4a4178973ccd571b277fcdd83e590.tar.bz2
Add a flags to further control response header persistence. We use this to
filter out Set-Cookie and Set-Cookie2 response headers from being forwarded to the renderer. This serves to prevent the renderer from having any access to HttpOnly cookies, and it also prevents XMLHttpRequest consumers from being able to read cookies in the HTTP response headers. This is consistent with changes made to Firefox and WebKit. Patch by marius.schilder@gmail.com R=deanm,darin Review URL: http://codereview.chromium.org/11264 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5767 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r--net/http/http_cache.cc13
-rw-r--r--net/http/http_response_headers.cc109
-rw-r--r--net/http/http_response_headers.h34
-rw-r--r--net/http/http_response_headers_unittest.cc51
4 files changed, 148 insertions, 59 deletions
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index e3aa533..0cb4337 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -1075,7 +1075,18 @@ bool HttpCache::WriteResponseInfo(disk_cache::Entry* disk_entry,
pickle.WriteInt64(response_info->request_time.ToInternalValue());
pickle.WriteInt64(response_info->response_time.ToInternalValue());
- response_info->headers->Persist(&pickle, skip_transient_headers);
+ net::HttpResponseHeaders::PersistOptions persist_options =
+ net::HttpResponseHeaders::PERSIST_RAW;
+
+ if (skip_transient_headers) {
+ persist_options =
+ net::HttpResponseHeaders::PERSIST_SANS_COOKIES |
+ net::HttpResponseHeaders::PERSIST_SANS_CHALLENGES |
+ net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP |
+ net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE;
+ }
+
+ response_info->headers->Persist(&pickle, persist_options);
if (response_info->ssl_info.cert) {
response_info->ssl_info.cert->Persist(&pickle);
diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc
index 8c80c49..47d2f85 100644
--- a/net/http/http_response_headers.cc
+++ b/net/http/http_response_headers.cc
@@ -27,22 +27,32 @@ namespace net {
namespace {
-// These response headers are not persisted in a cached representation of the
-// response headers. (This list comes from Mozilla's nsHttpResponseHead.cpp)
-const char* const kTransientHeaders[] = {
+// These headers are RFC 2616 hop-by-hop headers;
+// not to be stored by caches.
+const char* const kHopByHopResponseHeaders[] = {
"connection",
"proxy-connection",
"keep-alive",
- "www-authenticate",
- "proxy-authenticate",
"trailer",
"transfer-encoding",
- "upgrade",
+ "upgrade"
+};
+
+// These headers are challenge response headers;
+// not to be stored by caches.
+const char* const kChallengeResponseHeaders[] = {
+ "www-authenticate",
+ "proxy-authenticate"
+};
+
+// These headers are cookie setting headers;
+// not to be stored by caches or disclosed otherwise.
+const char* const kCookieResponseHeaders[] = {
"set-cookie",
"set-cookie2"
};
-// These respones headers are not copied from a 304/206 response to the cached
+// These response headers are not copied from a 304/206 response to the cached
// response headers. This list is based on Mozilla's nsHttpResponseHead.cpp.
const char* const kNonUpdatedHeaders[] = {
"connection",
@@ -90,41 +100,56 @@ HttpResponseHeaders::HttpResponseHeaders(const Pickle& pickle, void** iter)
Parse(raw_input);
}
-void HttpResponseHeaders::Persist(Pickle* pickle, bool for_cache) {
- if (for_cache) {
- HeaderSet transient_headers;
- GetTransientHeaders(&transient_headers);
+void HttpResponseHeaders::Persist(Pickle* pickle, PersistOptions options) {
+ if (options == PERSIST_RAW) {
+ pickle->WriteString(raw_headers_);
+ return; // Done.
+ }
- std::string blob;
- blob.reserve(raw_headers_.size());
+ HeaderSet filter_headers;
- // this just copies the status line w/ terminator
- blob.assign(raw_headers_.c_str(), strlen(raw_headers_.c_str()) + 1);
+ // Construct set of headers to filter out based on options.
+ if ((options & PERSIST_SANS_NON_CACHEABLE) == PERSIST_SANS_NON_CACHEABLE)
+ AddNonCacheableHeaders(&filter_headers);
- for (size_t i = 0; i < parsed_.size(); ++i) {
- DCHECK(!parsed_[i].is_continuation());
+ if ((options & PERSIST_SANS_COOKIES) == PERSIST_SANS_COOKIES)
+ AddCookieHeaders(&filter_headers);
- // locate the start of the next header
- size_t k = i;
- while (++k < parsed_.size() && parsed_[k].is_continuation());
- --k;
+ if ((options & PERSIST_SANS_CHALLENGES) == PERSIST_SANS_CHALLENGES)
+ AddChallengeHeaders(&filter_headers);
- std::string header_name(parsed_[i].name_begin, parsed_[i].name_end);
- StringToLowerASCII(&header_name);
+ if ((options & PERSIST_SANS_HOP_BY_HOP) == PERSIST_SANS_HOP_BY_HOP)
+ AddHopByHopHeaders(&filter_headers);
- if (transient_headers.find(header_name) == transient_headers.end()) {
- // includes terminator
- blob.append(parsed_[i].name_begin, parsed_[k].value_end + 1);
- }
+ std::string blob;
+ blob.reserve(raw_headers_.size());
+
+ // This copies the status line w/ terminator null.
+ // Note raw_headers_ has embedded nulls instead of \n,
+ // so this just copies the first header line.
+ blob.assign(raw_headers_.c_str(), strlen(raw_headers_.c_str()) + 1);
+
+ for (size_t i = 0; i < parsed_.size(); ++i) {
+ DCHECK(!parsed_[i].is_continuation());
- i = k;
+ // Locate the start of the next header.
+ size_t k = i;
+ while (++k < parsed_.size() && parsed_[k].is_continuation());
+ --k;
+
+ std::string header_name(parsed_[i].name_begin, parsed_[i].name_end);
+ StringToLowerASCII(&header_name);
+
+ if (filter_headers.find(header_name) == filter_headers.end()) {
+ // Includes terminator null due to the + 1.
+ blob.append(parsed_[i].name_begin, parsed_[k].value_end + 1);
}
- blob.push_back('\0');
- pickle->WriteString(blob);
- } else {
- pickle->WriteString(raw_headers_);
+ i = k;
}
+ blob.push_back('\0');
+
+ pickle->WriteString(blob);
}
void HttpResponseHeaders::Update(const HttpResponseHeaders& new_headers) {
@@ -557,7 +582,7 @@ void HttpResponseHeaders::AddToParsed(std::string::const_iterator name_begin,
parsed_.push_back(header);
}
-void HttpResponseHeaders::GetTransientHeaders(HeaderSet* result) const {
+void HttpResponseHeaders::AddNonCacheableHeaders(HeaderSet* result) const {
// Add server specified transients. Any 'cache-control: no-cache="foo,bar"'
// headers present in the response specify additional headers that we should
// not store in the cache.
@@ -602,11 +627,21 @@ void HttpResponseHeaders::GetTransientHeaders(HeaderSet* result) const {
}
}
}
+}
+
+void HttpResponseHeaders::AddHopByHopHeaders(HeaderSet* result) {
+ for (size_t i = 0; i < arraysize(kHopByHopResponseHeaders); ++i)
+ result->insert(std::string(kHopByHopResponseHeaders[i]));
+}
+
+void HttpResponseHeaders::AddCookieHeaders(HeaderSet* result) {
+ for (size_t i = 0; i < arraysize(kCookieResponseHeaders); ++i)
+ result->insert(std::string(kCookieResponseHeaders[i]));
+}
- // Add standard transient headers. Perhaps we should move this to a
- // statically cached hash_set to avoid duplicated work?
- for (size_t i = 0; i < arraysize(kTransientHeaders); ++i)
- result->insert(std::string(kTransientHeaders[i]));
+void HttpResponseHeaders::AddChallengeHeaders(HeaderSet* result) {
+ for (size_t i = 0; i < arraysize(kChallengeResponseHeaders); ++i)
+ result->insert(std::string(kChallengeResponseHeaders[i]));
}
void HttpResponseHeaders::GetMimeTypeAndCharset(std::string* mime_type,
diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h
index 3edb070..ddd23ac 100644
--- a/net/http/http_response_headers.h
+++ b/net/http/http_response_headers.h
@@ -44,10 +44,18 @@ class HttpResponseHeaders :
// be passed to the pickle's various Read* methods.
HttpResponseHeaders(const Pickle& pickle, void** pickle_iter);
- // Appends a representation of this object to the given pickle. If the
- // for_cache argument is true, then non-cacheable headers will be pruned from
- // the persisted version of the response headers.
- void Persist(Pickle* pickle, bool for_cache);
+ // Persist options.
+ typedef int PersistOptions;
+ static const PersistOptions PERSIST_RAW = -1; // Raw, unparsed headers.
+ static const PersistOptions PERSIST_ALL = 0; // Parsed headers.
+ static const PersistOptions PERSIST_SANS_COOKIES = 1 << 0;
+ static const PersistOptions PERSIST_SANS_CHALLENGES = 1 << 1;
+ static const PersistOptions PERSIST_SANS_HOP_BY_HOP = 1 << 2;
+ static const PersistOptions PERSIST_SANS_NON_CACHEABLE = 1 << 3;
+
+ // Appends a representation of this object to the given pickle.
+ // The options argument can be a combination of PersistOptions.
+ void Persist(Pickle* pickle, PersistOptions options);
// Performs header merging as described in 13.5.3 of RFC 2616.
void Update(const HttpResponseHeaders& new_headers);
@@ -242,10 +250,20 @@ class HttpResponseHeaders :
typedef base::hash_set<std::string> HeaderSet;
- // Returns the values from any 'cache-control: no-cache="foo,bar"' headers as
- // well as other known-to-be-transient header names. The header names are
- // all lowercase to support fast lookup.
- void GetTransientHeaders(HeaderSet* header_names) const;
+ // Adds the values from any 'cache-control: no-cache="foo,bar"' headers.
+ void AddNonCacheableHeaders(HeaderSet* header_names) const;
+
+ // Adds the set of header names that contain cookie values.
+ static void AddSensitiveHeaders(HeaderSet* header_names);
+
+ // Adds the set of rfc2616 hop-by-hop response headers.
+ static void AddHopByHopHeaders(HeaderSet* header_names);
+
+ // Adds the set of challenge response headers.
+ static void AddChallengeHeaders(HeaderSet* header_names);
+
+ // Adds the set of cookie response headers.
+ static void AddCookieHeaders(HeaderSet* header_names);
// The members of this structure point into raw_headers_.
struct ParsedHeader {
diff --git a/net/http/http_response_headers_unittest.cc b/net/http/http_response_headers_unittest.cc
index fa5cbaa..79064b6 100644
--- a/net/http/http_response_headers_unittest.cc
+++ b/net/http/http_response_headers_unittest.cc
@@ -290,24 +290,29 @@ TEST(HttpResponseHeadersTest, GetNormalizedHeader) {
TEST(HttpResponseHeadersTest, Persist) {
const struct {
+ net::HttpResponseHeaders::PersistOptions options;
const char* raw_headers;
const char* expected_headers;
} tests[] = {
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_ALL,
+ "HTTP/1.1 200 OK\n"
"Cache-control:private\n"
"cache-Control:no-store\n",
"HTTP/1.1 200 OK\n"
"Cache-control: private, no-store\n"
},
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
+ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
"server: blah\n",
"HTTP/1.1 200 OK\n"
"server: blah\n"
},
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
+ net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
+ "HTTP/1.1 200 OK\n"
"fOo: 1\n"
"Foo: 2\n"
"Transfer-Encoding: chunked\n"
@@ -317,7 +322,8 @@ TEST(HttpResponseHeadersTest, Persist) {
"HTTP/1.1 200 OK\n"
"cache-control: private, no-cache=\"foo\"\n"
},
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
+ "HTTP/1.1 200 OK\n"
"Foo: 2\n"
"Cache-Control: private,no-cache=\"foo, bar\"\n"
"bar",
@@ -326,7 +332,8 @@ TEST(HttpResponseHeadersTest, Persist) {
"Cache-Control: private,no-cache=\"foo, bar\"\n"
},
// ignore bogus no-cache value
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
+ "HTTP/1.1 200 OK\n"
"Foo: 2\n"
"Cache-Control: private,no-cache=foo\n",
@@ -335,7 +342,8 @@ TEST(HttpResponseHeadersTest, Persist) {
"Cache-Control: private,no-cache=foo\n"
},
// ignore bogus no-cache value
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
+ "HTTP/1.1 200 OK\n"
"Foo: 2\n"
"Cache-Control: private, no-cache=\n",
@@ -344,7 +352,8 @@ TEST(HttpResponseHeadersTest, Persist) {
"Cache-Control: private, no-cache=\n"
},
// ignore empty no-cache value
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
+ "HTTP/1.1 200 OK\n"
"Foo: 2\n"
"Cache-Control: private, no-cache=\"\"\n",
@@ -353,7 +362,8 @@ TEST(HttpResponseHeadersTest, Persist) {
"Cache-Control: private, no-cache=\"\"\n"
},
// ignore wrong quotes no-cache value
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
+ "HTTP/1.1 200 OK\n"
"Foo: 2\n"
"Cache-Control: private, no-cache=\'foo\'\n",
@@ -362,7 +372,8 @@ TEST(HttpResponseHeadersTest, Persist) {
"Cache-Control: private, no-cache=\'foo\'\n"
},
// ignore unterminated quotes no-cache value
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
+ "HTTP/1.1 200 OK\n"
"Foo: 2\n"
"Cache-Control: private, no-cache=\"foo\n",
@@ -371,7 +382,8 @@ TEST(HttpResponseHeadersTest, Persist) {
"Cache-Control: private, no-cache=\"foo\n"
},
// accept sloppy LWS
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
+ "HTTP/1.1 200 OK\n"
"Foo: 2\n"
"Cache-Control: private, no-cache=\" foo\t, bar\"\n",
@@ -379,7 +391,8 @@ TEST(HttpResponseHeadersTest, Persist) {
"Cache-Control: private, no-cache=\" foo\t, bar\"\n"
},
// header name appears twice, separated by another header
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_ALL,
+ "HTTP/1.1 200 OK\n"
"Foo: 1\n"
"Bar: 2\n"
"Foo: 3\n",
@@ -389,7 +402,8 @@ TEST(HttpResponseHeadersTest, Persist) {
"Bar: 2\n"
},
// header name appears twice, separated by another header (type 2)
- { "HTTP/1.1 200 OK\n"
+ { net::HttpResponseHeaders::PERSIST_ALL,
+ "HTTP/1.1 200 OK\n"
"Foo: 1, 3\n"
"Bar: 2\n"
"Foo: 4\n",
@@ -398,6 +412,17 @@ TEST(HttpResponseHeadersTest, Persist) {
"Foo: 1, 3, 4\n"
"Bar: 2\n"
},
+ // Test filtering of cookie headers.
+ { net::HttpResponseHeaders::PERSIST_SANS_COOKIES,
+ "HTTP/1.1 200 OK\n"
+ "Set-Cookie: foo=bar; httponly\n"
+ "Set-Cookie: bar=foo\n"
+ "Bar: 1\n"
+ "Set-Cookie2: bar2=foo2\n",
+
+ "HTTP/1.1 200 OK\n"
+ "Bar: 1\n"
+ },
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
@@ -407,7 +432,7 @@ TEST(HttpResponseHeadersTest, Persist) {
new HttpResponseHeaders(headers);
Pickle pickle;
- parsed1->Persist(&pickle, true);
+ parsed1->Persist(&pickle, tests[i].options);
void* iter = NULL;
scoped_refptr<HttpResponseHeaders> parsed2 =