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

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..be5abaf0f53e97189658443721dae78475e988e7 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -32,6 +32,7 @@
#include "net/base/net_errors.h"
#include "net/base/upload_data_stream.h"
#include "net/base/url_util.h"
+#include "net/filter/source_stream.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_handler.h"
#include "net/http/http_auth_handler_factory.h"
@@ -81,6 +82,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,
+ const std::string& encoding) {
+ size_t cursor = accept_encoding.find(encoding);
+ char c;
+
+ // Encoding is not mentioned.
+ if (cursor == std::string::npos)
+ return false;
Randy Smith (Not in Mondays) 2017/03/16 17:50:44 Put a note in here about explicitly not worrying a
eustas 2017/03/20 13:05:18 This code is gone.
+
+ // Check that match position is not in the middle of another entry.
+ if (cursor != 0) {
+ c = accept_encoding[cursor - 1];
+ if ((c != ' ') && (c != ','))
Randy Smith (Not in Mondays) 2017/03/16 17:50:44 Do we need to check other whitespace? (Tab specif
eustas 2017/03/20 13:05:18 In new code whitespace is trimmed around all token
+ 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;
Randy Smith (Not in Mondays) 2017/03/16 17:50:44 Is the thought that q=1 will never happen? I'd so
eustas 2017/03/20 13:05:18 Ooops. Fixed it,... and then rewritten it to a mor
+
+ 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
Randy Smith (Not in Mondays) 2017/03/16 17:50:44 Why not make this a DCHECK and remove the comments
eustas 2017/03/20 13:05:18 Acknowledged.
+ // 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 +667,14 @@ void HttpNetworkTransaction::OnNeedsProxyAuth(
establishing_tunnel_ = true;
response_.headers = proxy_response.headers;
response_.auth_challenge = proxy_response.auth_challenge;
+
+ if (response_.headers.get() && !ContentEncodingsValid()) {
+ UMA_HISTOGRAM_ENUMERATION("Net.ContentDecodingFailed2.FilterType",
+ SourceStream::TYPE_UNKNOWN,
+ SourceStream::TYPE_MAX);
+ return ERR_CONTENT_DECODING_FAILED;
+ }
+
headers_valid_ = true;
server_ssl_config_ = used_ssl_config;
proxy_info_ = used_proxy_info;
@@ -1246,6 +1376,13 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
DCHECK(response_.headers.get());
+ if (response_.headers.get() && !ContentEncodingsValid()) {
+ UMA_HISTOGRAM_ENUMERATION("Net.ContentDecodingFailed2.FilterType",
+ SourceStream::TYPE_UNKNOWN,
+ SourceStream::TYPE_MAX);
+ return ERR_CONTENT_DECODING_FAILED;
+ }
+
// 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 +1848,38 @@ 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/16 17:50:44 Suggestion: Move this comment down to above the "r
eustas 2017/03/20 13:05:18 Replaced with set-matching code, so now it is more
+ std::string accept_encoding;
+ request_headers_.GetHeader(HttpRequestHeaders::kAcceptEncoding,
+ &accept_encoding);
+
+ std::string encoding;
+ size_t iter = 0;
+ while (headers->EnumerateHeader(&iter, "Content-Encoding", &encoding)) {
+ if (accept_encoding.empty())
+ return false;
+
+ 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";
Randy Smith (Not in Mondays) 2017/03/16 17:50:44 Do we need to worry about these cases in the Accep
eustas 2017/03/20 13:05:18 Made it symmetric in new parser.
+
+ // Reject malformed encodings.
+ if (encoding.find_first_of("=;, ") != std::string::npos)
+ return false;
+
+ if (!IsAcceptableEncoding(accept_encoding, encoding))
+ return false;
+ }
+ return true;
+}
+
} // namespace net

Powered by Google App Engine
This is Rietveld 408576698