Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1756)

Unified Diff: net/http/http_network_transaction.cc

Issue 2753453003: Reject unadvertised encodings (Closed)
Patch Set: Reject unadvertised encodings. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/http/http_network_transaction.cc
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index a5395d758bcc4c9d26ef115ca43da28ce9424a0c..aaeb7ab0d278a588de323e2b7df42ec9bbc6829d 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -81,6 +81,127 @@ std::unique_ptr<base::Value> NetLogSSLCipherFallbackCallback(
return std::move(dict);
}
+enum QualityParsingState {
+ BEFORE_QUALIFIER, // waiting for ";"
+ QUALIFIER, // waiting for "q"
+ EQUALS, // waiting for "="
+ ZERO_OR_ONE, // waiting for "0" or "1"
+ PERIOD, // waiting for "."
+ DECIMALS,
+ DONE
+};
+
+// Tests is specified encoding is allowed by the given "Accept-Encoding" header.
+// It is expected that header value is somewhat well-formatted (according to the
+// RFC2616), and no "content-coding" is a prefix of another "content-coding".
+bool IsAcceptableEncoding(const std::string& accept_encoding,
Randy Smith (Not in Mondays) 2017/03/15 16:35:31 Confirming: This is here because there's no existi
eustas 2017/03/16 12:08:16 Yup, hasn't found parsers for "Accept-xxx" header.
Randy Smith (Not in Mondays) 2017/03/16 17:50:44 Stick in a TODO to that effect? (To move it somew
eustas 2017/03/20 13:05:18 Reworked and moved to HttpUtil
+ const std::string& encoding) {
+ size_t cursor = accept_encoding.find(encoding);
+ char c;
+
+ // Encoding is not mentioned.
+ if (cursor == std::string::npos)
+ return false;
+
+ // Check that match position is not in the middle of another entry.
+ if (cursor != 0) {
+ c = accept_encoding[cursor - 1];
+ if ((c != ' ') && (c != ','))
+ return false;
+ }
+ cursor += encoding.size();
+
+ QualityParsingState state = BEFORE_QUALIFIER;
+ int ones = 0;
+ int sum = 0;
+ int digits = 0;
+
+ for (; cursor < accept_encoding.size(); ++cursor) {
+ c = accept_encoding[cursor];
+ switch (state) {
+ case BEFORE_QUALIFIER:
+ if (c == ' ')
+ break;
+ if (c == ',')
+ return true;
+ if (c == ';') {
+ state = QUALIFIER;
+ break;
+ }
+ return false;
+
+ case QUALIFIER:
+ if (c == ' ')
+ break;
+ if (c == 'q' || c == 'Q') {
+ state = EQUALS;
+ break;
+ }
+ return false;
+
+ case EQUALS:
+ if (c == '=') {
+ state = ZERO_OR_ONE;
+ break;
+ }
+ return false;
+
+ case ZERO_OR_ONE:
+ if (c == '0' || c == '1') {
+ ones = c - '0';
+ state = PERIOD;
+ break;
+ }
+ return false;
+
+ case PERIOD:
+ if (c == '.') {
+ state = DECIMALS;
+ break;
+ }
+ return false;
+
+ case DECIMALS:
+ if (c == ',' || c == ' ') {
+ state = DONE;
+ break;
+ }
+ if (c >= '0' && c <= '9') {
+ sum += c - '0';
+ digits++;
+ break;
+ }
+ return false;
+
+ case DONE:
+ // assert(0)
+ break;
+ }
+ if (state == DONE)
+ break;
+ }
+
+ // Before "q".
+ if (state == BEFORE_QUALIFIER)
+ return true;
+
+ // Before value.
+ if (state == QUALIFIER || state == EQUALS || state == ZERO_OR_ONE)
+ return false;
+
+ // assert(state == PERIOD || state == DECIMALS || state == DONE
+ // Some value is read.
+
+ if (digits > 3)
+ return false;
+
+ if (ones == 0)
+ return (sum != 0);
+
+ // Reject values like "1.x", where x != 0
+ return (sum == 0);
+}
+
} // namespace
//-----------------------------------------------------------------------------
@@ -545,6 +666,7 @@ void HttpNetworkTransaction::OnNeedsProxyAuth(
establishing_tunnel_ = true;
response_.headers = proxy_response.headers;
response_.auth_challenge = proxy_response.auth_challenge;
+ // TODO(eustas): Should content encoding be checked here?
Randy Smith (Not in Mondays) 2017/03/15 16:35:32 Hmmm. I'm inclined to think yes. I'll ask Matt f
eustas 2017/03/16 12:08:16 Added similar check here.
headers_valid_ = true;
server_ssl_config_ = used_ssl_config;
proxy_info_ = used_proxy_info;
@@ -574,6 +696,7 @@ void HttpNetworkTransaction::OnHttpsProxyTunnelResponse(
CopyConnectionAttemptsFromStreamRequest();
+ // TODO(eustas): Should content encoding be checked here?
headers_valid_ = true;
response_ = response_info;
server_ssl_config_ = used_ssl_config;
@@ -1246,6 +1369,12 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
DCHECK(response_.headers.get());
+ if (response_.headers.get() && !ContentEncodingsValid()) {
+ // TODO(eustas): report in UMA?
Randy Smith (Not in Mondays) 2017/03/15 16:35:32 See comment about which error.
eustas 2017/03/16 12:08:16 Done.
+ // TODO(eustas): ignore, based on Finch flag?
Randy Smith (Not in Mondays) 2017/03/15 16:35:32 I'm inclined to think we can just watch the metric
eustas 2017/03/16 12:08:16 Done.
+ return ERR_INVALID_CONTENT_ENCODING;
+ }
+
// On a 408 response from the server ("Request Timeout") on a stale socket,
// retry the request.
// Headers can be NULL because of http://crbug.com/384554.
@@ -1711,4 +1840,44 @@ void HttpNetworkTransaction::CopyConnectionAttemptsFromStreamRequest() {
connection_attempts_.push_back(attempt);
}
+bool HttpNetworkTransaction::ContentEncodingsValid() const {
+ HttpResponseHeaders* headers = GetResponseHeaders();
+ DCHECK(headers);
+
+ // It is OK, if both "Accept-Encoding" and "Content-Encoding" headers are
+ // missing. It is a widely used setup in unit-tests.
Randy Smith (Not in Mondays) 2017/03/15 16:35:32 If this is specifically for unit tests, it's not v
eustas 2017/03/16 12:08:16 Pulled GetHeader out of loop.
+
+ bool accept_encoding_acquired = false;
+ std::string accept_encoding;
+ std::string encoding;
+ size_t iter = 0;
+ while (headers->EnumerateHeader(&iter, "Content-Encoding", &encoding)) {
+ encoding = base::ToLowerASCII(encoding);
+ // RFC2616 Section 3.5 recommendation: applications SHOULD consider "x-gzip"
+ // and "x-compress" to be equivalent to "gzip" and "compress" respectively.
+ if (encoding.compare("x-gzip") == 0)
+ encoding = "gzip";
+ if (encoding.compare("x-compress") == 0)
+ encoding = "compress";
+
+ // Reject malformed encodings.
+ if (encoding.find_first_of("=;, ") != std::string::npos)
+ return false;
+
+ if (!accept_encoding_acquired) {
+ if (!request_headers_.GetHeader(HttpRequestHeaders::kAcceptEncoding,
+ &accept_encoding)) {
+ // URLRequestHttpJob sets the header if no value is provided.
+ // Other clients shoot themselves in a foot, if they don't.
+ return false;
+ }
+ accept_encoding_acquired = true;
+ }
+
+ if (!IsAcceptableEncoding(accept_encoding, encoding))
+ return false;
+ }
+ return true;
+}
+
} // namespace net

Powered by Google App Engine
This is Rietveld 408576698