Commit f71582a1 authored by xingx@chromium.org's avatar xingx@chromium.org

Implement client side tamper detection logic.

The Chrome data reduction feature relies on HTTP headers to 
work correctly and efficiently with the data reduction 
proxy. This include both standard HTTP headers (like Via) 
and custom headers (like Chrome-Proxy). Tampering on these 
headers could lead to miserable user experience, taking 10s 
to load some pages, for example.

In the past, we have seen such headers being stripped by 
middle box proxies (for example, the WWW-Authenticate header 
was stripped by some carrier). It has been known that mobile 
carriers are doing HTTP traffic optimizations. We also want 
to know whether mobile carriers are trying to "optimize" the 
already optimized data reduction proxy response body, which 
might lead to higher cost to users.


We propose a mechanism in Chromium to enable us to learn the 
scale and the types of such tampers. In short, the mechanism 
will check whether a predefined set of HTTP response headers 
and the response body have been changed in a way that could 
affect the data reduction proxy. It will detect such changes 
by using pre-calculated header (and probably content) hashes 
sent by the server. Chromium will report through UMA the 
count of each tamper types has happened. This will only be 
enabled for a fraction of the data reduction proxy users.

BUG=381907

Review URL: https://codereview.chromium.org/338483002

Cr-Commit-Position: refs/heads/master@{#288492}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288492 0039d316-1c4b-4281-b951-d872f2087c98
parent 3571512c
...@@ -82,6 +82,7 @@ ...@@ -82,6 +82,7 @@
'data_reduction_proxy/browser/data_reduction_proxy_params_unittest.cc', 'data_reduction_proxy/browser/data_reduction_proxy_params_unittest.cc',
'data_reduction_proxy/browser/data_reduction_proxy_protocol_unittest.cc', 'data_reduction_proxy/browser/data_reduction_proxy_protocol_unittest.cc',
'data_reduction_proxy/browser/data_reduction_proxy_settings_unittest.cc', 'data_reduction_proxy/browser/data_reduction_proxy_settings_unittest.cc',
'data_reduction_proxy/browser/data_reduction_proxy_tamper_detection_unittest.cc',
'data_reduction_proxy/browser/data_reduction_proxy_usage_stats_unittest.cc', 'data_reduction_proxy/browser/data_reduction_proxy_usage_stats_unittest.cc',
'data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc', 'data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc',
'dom_distiller/core/article_entry_unittest.cc', 'dom_distiller/core/article_entry_unittest.cc',
......
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
'data_reduction_proxy/browser/data_reduction_proxy_protocol.h', 'data_reduction_proxy/browser/data_reduction_proxy_protocol.h',
'data_reduction_proxy/browser/data_reduction_proxy_settings.cc', 'data_reduction_proxy/browser/data_reduction_proxy_settings.cc',
'data_reduction_proxy/browser/data_reduction_proxy_settings.h', 'data_reduction_proxy/browser/data_reduction_proxy_settings.h',
'data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.cc',
'data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h',
'data_reduction_proxy/browser/data_reduction_proxy_usage_stats.cc', 'data_reduction_proxy/browser/data_reduction_proxy_usage_stats.cc',
'data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h', 'data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h',
], ],
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h"
#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
#include "net/base/load_flags.h" #include "net/base/load_flags.h"
#include "net/http/http_response_headers.h" #include "net/http/http_response_headers.h"
...@@ -58,6 +59,10 @@ bool MaybeBypassProxyAndPrepareToRetry( ...@@ -58,6 +59,10 @@ bool MaybeBypassProxyAndPrepareToRetry(
if (data_reduction_proxies.first.is_empty()) if (data_reduction_proxies.first.is_empty())
return false; return false;
DataReductionProxyTamperDetection::DetectAndReport(
original_response_headers,
data_reduction_proxies.first.SchemeIsSecure());
DataReductionProxyInfo data_reduction_proxy_info; DataReductionProxyInfo data_reduction_proxy_info;
net::ProxyService::DataReductionProxyBypassType bypass_type = net::ProxyService::DataReductionProxyBypassType bypass_type =
GetDataReductionProxyBypassType(original_response_headers, GetDataReductionProxyBypassType(original_response_headers,
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h"
#include <algorithm>
#include <cstring>
#include "base/base64.h"
#include "base/md5.h"
#include "base/metrics/histogram.h"
#include "base/metrics/sparse_histogram.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#if defined(OS_ANDROID)
#include "net/android/network_library.h"
#endif
// Macro for UMA reporting. HTTP response first reports to histogram events
// |http_histogram| by |carrier_id|; then reports the total counts to
// |http_histogram|_Total. HTTPS response reports to histograms
// |https_histogram| and |https_histogram|_Total similarly.
#define REPORT_TAMPER_DETECTION_UMA( \
scheme_is_https, https_histogram, http_histogram, carrier_id) \
do { \
if (scheme_is_https) { \
UMA_HISTOGRAM_SPARSE_SLOWLY(https_histogram, carrier_id); \
UMA_HISTOGRAM_COUNTS(https_histogram "_Total", 1); \
} else { \
UMA_HISTOGRAM_SPARSE_SLOWLY(http_histogram, carrier_id); \
UMA_HISTOGRAM_COUNTS(http_histogram "_Total", 1); \
}\
} while (0)
namespace data_reduction_proxy {
// static
bool DataReductionProxyTamperDetection::DetectAndReport(
const net::HttpResponseHeaders* headers,
const bool scheme_is_https) {
DCHECK(headers);
// Abort tamper detection, if the fingerprint of the Chrome-Proxy header is
// absent.
std::string chrome_proxy_fingerprint;
if (!GetDataReductionProxyActionFingerprintChromeProxy(
headers, &chrome_proxy_fingerprint)) {
return false;
}
// Get carrier ID.
unsigned carrier_id = 0;
#if defined(OS_ANDROID)
base::StringToUint(net::android::GetTelephonyNetworkOperator(), &carrier_id);
#endif
DataReductionProxyTamperDetection tamper_detection(
headers, scheme_is_https, carrier_id);
// Checks if the Chrome-Proxy header has been tampered with.
if (tamper_detection.ValidateChromeProxyHeader(chrome_proxy_fingerprint)) {
tamper_detection.ReportUMAforChromeProxyHeaderValidation();
return true;
}
// Chrome-Proxy header has not been tampered with, and thus other
// fingerprints are valid. Reports the number of responses that other
// fingerprints will be checked.
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https,
"DataReductionProxy.HeaderTamperDetectionHTTPS",
"DataReductionProxy.HeaderTamperDetectionHTTP",
carrier_id);
bool tampered = false;
std::string fingerprint;
if (GetDataReductionProxyActionFingerprintVia(headers, &fingerprint)) {
bool has_chrome_proxy_via_header;
if (tamper_detection.ValidateViaHeader(
fingerprint, &has_chrome_proxy_via_header)) {
tamper_detection.ReportUMAforViaHeaderValidation(
has_chrome_proxy_via_header);
tampered = true;
}
}
if (GetDataReductionProxyActionFingerprintOtherHeaders(
headers, &fingerprint)) {
if (tamper_detection.ValidateOtherHeaders(fingerprint)) {
tamper_detection.ReportUMAforOtherHeadersValidation();
tampered = true;
}
}
if (GetDataReductionProxyActionFingerprintContentLength(
headers, &fingerprint)) {
if (tamper_detection.ValidateContentLengthHeader(fingerprint)) {
tamper_detection.ReportUMAforContentLengthHeaderValidation();
tampered = true;
}
}
if (!tampered) {
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https,
"DataReductionProxy.HeaderTamperDetectionPassHTTPS",
"DataReductionProxy.HeaderTamperDetectionPassHTTP",
carrier_id);
}
return tampered;
}
// Constructor initializes the map of fingerprint names to codes.
DataReductionProxyTamperDetection::DataReductionProxyTamperDetection(
const net::HttpResponseHeaders* headers,
const bool is_secure,
const unsigned carrier_id)
: response_headers_(headers),
scheme_is_https_(is_secure),
carrier_id_(carrier_id) {
DCHECK(headers);
}
DataReductionProxyTamperDetection::~DataReductionProxyTamperDetection() {};
// |fingerprint| is Base64 encoded. Decodes it first. Then calculates the
// fingerprint of received Chrome-Proxy header, and compares the two to see
// whether they are equal or not.
bool DataReductionProxyTamperDetection::ValidateChromeProxyHeader(
const std::string& fingerprint) const {
std::string received_fingerprint;
if (!base::Base64Decode(fingerprint, &received_fingerprint))
return true;
// Gets the Chrome-Proxy header values with its fingerprint removed.
std::vector<std::string> chrome_proxy_header_values;
GetDataReductionProxyHeaderWithFingerprintRemoved(
response_headers_, &chrome_proxy_header_values);
// Calculates the MD5 hash value of Chrome-Proxy.
std::string actual_fingerprint;
GetMD5(ValuesToSortedString(&chrome_proxy_header_values),
&actual_fingerprint);
return received_fingerprint != actual_fingerprint;
}
void DataReductionProxyTamperDetection::
ReportUMAforChromeProxyHeaderValidation() const {
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https_,
"DataReductionProxy.HeaderTamperedHTTPS_ChromeProxy",
"DataReductionProxy.HeaderTamperedHTTP_ChromeProxy",
carrier_id_);
}
// Checks whether there are other proxies/middleboxes' named after the data
// reduction proxy's name in Via header. |has_chrome_proxy_via_header| marks
// that whether the data reduction proxy's Via header occurs or not.
bool DataReductionProxyTamperDetection::ValidateViaHeader(
const std::string& fingerprint,
bool* has_chrome_proxy_via_header) const {
bool has_intermediary;
*has_chrome_proxy_via_header = HasDataReductionProxyViaHeader(
response_headers_,
&has_intermediary);
if (*has_chrome_proxy_via_header)
return !has_intermediary;
return true;
}
void DataReductionProxyTamperDetection::ReportUMAforViaHeaderValidation(
bool has_chrome_proxy) const {
// The Via header of the data reduction proxy is missing.
if (!has_chrome_proxy) {
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https_,
"DataReductionProxy.HeaderTamperedHTTPS_Via_Missing",
"DataReductionProxy.HeaderTamperedHTTP_Via_Missing",
carrier_id_);
return;
}
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https_,
"DataReductionProxy.HeaderTamperedHTTPS_Via",
"DataReductionProxy.HeaderTamperedHTTP_Via",
carrier_id_);
}
// The data reduction proxy constructs a canonical representation of values of
// a list of headers. The fingerprint is constructed as follows:
// 1) for each header, gets the string representation of its values (same as
// ValuesToSortedString);
// 2) concatenates all header's string representations using ";" as a delimiter;
// 3) calculates the MD5 hash value of above concatenated string;
// 4) appends the header names to the fingerprint, with a delimiter "|".
// The constructed fingerprint looks like:
// [hashed_fingerprint]|header_name1|header_namer2:...
//
// To check whether such a fingerprint matches the response that the Chromium
// client receives, the client firstly extracts the header names. For
// each header, gets its string representation (by ValuesToSortedString),
// concatenates them and calculates the MD5 hash value. Compares the hash
// value to the fingerprint received from the data reduction proxy.
bool DataReductionProxyTamperDetection::ValidateOtherHeaders(
const std::string& fingerprint) const {
DCHECK(!fingerprint.empty());
// According to RFC 2616, "|" is not a valid character in a header name; and
// it is not a valid base64 encoding character, so there is no ambituity in
//using it as a delimiter.
net::HttpUtil::ValuesIterator it(
fingerprint.begin(), fingerprint.end(), '|');
// The first value is the base64 encoded fingerprint.
std::string received_fingerprint;
if (!it.GetNext() ||
!base::Base64Decode(it.value(), &received_fingerprint)) {
NOTREACHED();
return true;
}
std::string header_values;
// The following values are the header names included in the fingerprint
// calculation.
while (it.GetNext()) {
// Gets values of one header.
std::vector<std::string> response_header_values =
GetHeaderValues(response_headers_, it.value());
// Sorts the values and concatenate them, with delimiter ";". ";" can occur
// in a header value and thus two different sets of header values could map
// to the same string representation. This should be very rare.
// TODO(xingx): find an unambiguous representation.
header_values += ValuesToSortedString(&response_header_values) + ";";
}
// Calculates the MD5 hash of the concatenated string.
std::string actual_fingerprint;
GetMD5(header_values, &actual_fingerprint);
return received_fingerprint != actual_fingerprint;
}
void DataReductionProxyTamperDetection::
ReportUMAforOtherHeadersValidation() const {
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https_,
"DataReductionProxy.HeaderTamperedHTTPS_OtherHeaders",
"DataReductionProxy.HeaderTamperedHTTP_OtherHeaders",
carrier_id_);
}
// The Content-Length value will not be reported as different if at either side
// (the data reduction proxy side and the client side), the Content-Length is
// missing or it cannot be decoded as a valid integer.
bool DataReductionProxyTamperDetection::ValidateContentLengthHeader(
const std::string& fingerprint) const {
int received_content_length_fingerprint, actual_content_length;
// Abort, if Content-Length value from the data reduction proxy does not
// exist or it cannot be converted to an integer.
if (!base::StringToInt(fingerprint, &received_content_length_fingerprint))
return false;
std::string actual_content_length_string;
// Abort, if there is no Content-Length header received.
if (!response_headers_->GetNormalizedHeader("Content-Length",
&actual_content_length_string)) {
return false;
}
// Abort, if the Content-Length value cannot be converted to integer.
if (!base::StringToInt(actual_content_length_string,
&actual_content_length)) {
return false;
}
return received_content_length_fingerprint != actual_content_length;
}
void DataReductionProxyTamperDetection::
ReportUMAforContentLengthHeaderValidation() const {
// Gets MIME type of the response and reports to UMA histograms separately.
// Divides MIME types into 4 groups: JavaScript, CSS, Images, and others.
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https_,
"DataReductionProxy.HeaderTamperedHTTPS_ContentLength",
"DataReductionProxy.HeaderTamperedHTTP_ContentLength",
carrier_id_);
// Gets MIME type.
std::string mime_type;
response_headers_->GetMimeType(&mime_type);
std::string JS1 = "text/javascript";
std::string JS2 = "application/x-javascript";
std::string JS3 = "application/javascript";
std::string CSS = "text/css";
std::string IMAGE = "image/";
size_t mime_type_size = mime_type.size();
if ((mime_type_size >= JS1.size() && LowerCaseEqualsASCII(mime_type.begin(),
mime_type.begin() + JS1.size(), JS1.c_str())) ||
(mime_type_size >= JS2.size() && LowerCaseEqualsASCII(mime_type.begin(),
mime_type.begin() + JS2.size(), JS2.c_str())) ||
(mime_type_size >= JS3.size() && LowerCaseEqualsASCII(mime_type.begin(),
mime_type.begin() + JS3.size(), JS3.c_str()))) {
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https_,
"DataReductionProxy.HeaderTamperedHTTPS_ContentLength_JS",
"DataReductionProxy.HeaderTamperedHTTP_ContentLength_JS",
carrier_id_);
} else if (mime_type_size >= CSS.size() &&
LowerCaseEqualsASCII(mime_type.begin(),
mime_type.begin() + CSS.size(), CSS.c_str())) {
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https_,
"DataReductionProxy.HeaderTamperedHTTPS_ContentLength_CSS",
"DataReductionProxy.HeaderTamperedHTTP_ContentLength_CSS",
carrier_id_);
} else if (mime_type_size >= IMAGE.size() &&
LowerCaseEqualsASCII(mime_type.begin(),
mime_type.begin() + IMAGE.size(), IMAGE.c_str())) {
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https_,
"DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Image",
"DataReductionProxy.HeaderTamperedHTTP_ContentLength_Image",
carrier_id_);
} else {
REPORT_TAMPER_DETECTION_UMA(
scheme_is_https_,
"DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Other",
"DataReductionProxy.HeaderTamperedHTTP_ContentLength_Other",
carrier_id_);
}
}
// We construct a canonical representation of the header so that reordered
// header values will produce the same fingerprint. The fingerprint is
// constructed as follows:
// 1) sort the values;
// 2) concatenate sorted values with a "," delimiter.
std::string DataReductionProxyTamperDetection::ValuesToSortedString(
std::vector<std::string>* values) {
std::string concatenated_values;
DCHECK(values);
if (!values) return "";
std::sort(values->begin(), values->end());
for (size_t i = 0; i < values->size(); ++i) {
// Concatenates with delimiter ",".
concatenated_values += (*values)[i] + ",";
}
return concatenated_values;
}
void DataReductionProxyTamperDetection::GetMD5(
const std::string& input, std::string* output) {
base::MD5Digest digest;
base::MD5Sum(input.c_str(), input.size(), &digest);
*output = std::string(
reinterpret_cast<char*>(digest.a), ARRAYSIZE_UNSAFE(digest.a));
}
std::vector<std::string> DataReductionProxyTamperDetection::GetHeaderValues(
const net::HttpResponseHeaders* headers,
const std::string& header_name) {
std::vector<std::string> values;
std::string value;
void* iter = NULL;
while (headers->EnumerateHeader(&iter, header_name, &value)) {
values.push_back(value);
}
return values;
}
} // namespace data_reduction_proxy
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file implements the tamper detection logic, which detects whether
// there are middleboxes and whether they are tampering with the response
// which may break correct communication and data transfer between the Chromium
// client and the data reduction proxy.
//
// At a high level, the tamper detection process works in two steps:
// 1. The data reduction proxy selects a fraction of responses to analyze,
// generates a series of fingerprints for each, and appends them to the
// Chrome-Proxy response headers;
// 2. The client re-generate the fingerprints using the same method as the
// proxy, compares them to the fingerprints in the response, and generates
// UMA. A response is considered to have been tampered with if the
// fingerprints do not match.
//
// Four fingerprints are generated by the data reduction proxy:
// 1. Fingerprint of the Chrome-Proxy header, which is designed to check
// whether the Chrome-Proxy header has been modified or not;
// 2. Fingerprint of the Via header, which is designed to check whether there
// are middleboxes between the Chromium client and the data reduction proxy;
// 3. Fingerprint of a list of headers, which is designed to check whether the
// values of a list of headers (list is defined by the data reduction proxy)
// have been modified or deleted;
// 4. Fingerprint of the Content-Length header, which is designed to check
// whether the response body has been modified or not (the code assumes that
// different Content-Length values indicate different response bodies).
//
// On the client side, the fingerprint of the Chrome-Proxy header will be
// checked first. If the fingerprint indicates that the Chrome-Proxy header has
// not been modified, then the other fingerprints will be considered to be
// reliable and will be checked next; if not, then it's possible that the other
// fingerprints have been tampered with and thus they will not be checked.
// If middlebox removes all the fingerprints then such tampering will not be
// detected.
//
// Detected tampering information will be reported to UMA. In general, for each
// fingerprint, the client reports the number of responses that have been
// tampered with for different carriers. For the fingerprint of the
// Content-Length header, which indicates whether the response body has been
// modified or not, the reports of tampering are separated by MIME type of the
// response body.
#ifndef COMPONENTS_DATA_REDUCTION_PROXY_BROWSER_DATA_REDUCTION_PROXY_TAMPER_DETECTION_H_
#define COMPONENTS_DATA_REDUCTION_PROXY_BROWSER_DATA_REDUCTION_PROXY_TAMPER_DETECTION_H_
#include <map>
#include <string>
#include <vector>
#include "net/proxy/proxy_service.h"
namespace net {
class HttpResponseHeaders;
}
namespace data_reduction_proxy {
// Detects if the response sent by the data reduction proxy has been modified
// by intermediaries on the Web.
class DataReductionProxyTamperDetection {
public:
// Checks if the response contains tamper detection fingerprints added by the
// data reduction proxy, and determines if the response had been tampered
// with if so. Results are reported to UMA. HTTP and HTTPS traffic are
// reported separately, specified by |scheme_is_https|. Returns true if
// the response has been tampered with.
static bool DetectAndReport(const net::HttpResponseHeaders* headers,
bool scheme_is_https);
// Tamper detection checks |response_headers|. Histogram events are reported
// by |carrier_id|; |scheme_is_https| determines which histogram to report
// (HTTP and HTTPS are reported separately).
DataReductionProxyTamperDetection(
const net::HttpResponseHeaders* response_headers,
bool scheme_is_https,
unsigned carrier_id);
virtual ~DataReductionProxyTamperDetection();
private:
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
TestFingerprintCommon);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
ChromeProxy);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
Via);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
OtherHeaders);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
ContentLength);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
HeaderRemoving);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
ValuesToSortedString);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
GetHeaderValues);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest,
DetectAndReport);
// Returns the result of validating Chrome-Proxy header.
bool ValidateChromeProxyHeader(const std::string& fingerprint) const;
// Reports UMA for tampering of the Chrome-Proxy header.
void ReportUMAforChromeProxyHeaderValidation() const;
// Returns the result of validating the Via header. |has_chrome_proxy|
// indicates that the data reduction proxy's Via header occurs or not.
bool ValidateViaHeader(const std::string& fingerprint,
bool* has_chrome_proxy_via_header) const;
// Reports UMA for tampering of the Via header.
void ReportUMAforViaHeaderValidation(bool has_chrome_proxy_via_header) const;
// Returns the result of validating a list of headers.
bool ValidateOtherHeaders(const std::string& fingerprint) const;
// Reports UMA for tampering of values of the list of headers.
void ReportUMAforOtherHeadersValidation() const;
// Returns the result of validating the Content-Length header.
bool ValidateContentLengthHeader(const std::string& fingerprint) const;
// Reports UMA for tampering of the Content-Length header.
void ReportUMAforContentLengthHeaderValidation() const;
// Returns a string representation of |values|.
static std::string ValuesToSortedString(std::vector<std::string>* values);
// Returns raw MD5 hash value for a given string |input|. It is different to
// base::MD5String which is base16 encoded.
static void GetMD5(const std::string& input, std::string* output);
// Returns all the values of |header_name| of the response |headers| as a
// vector. This function is used for values that need to be sorted later.
static std::vector<std::string> GetHeaderValues(
const net::HttpResponseHeaders* headers,
const std::string& header_name);
// Pointer to response headers.
const net::HttpResponseHeaders* response_headers_;
// If true, the connection to the data reduction proxy is over HTTPS;
const bool scheme_is_https_;
// Carrier ID: the numeric name of the current registered operator.
const unsigned carrier_id_;
DISALLOW_COPY_AND_ASSIGN(DataReductionProxyTamperDetection);
};
} // namespace data_reduction_proxy
#endif // COMPONENTS_DATA_REDUCTION_PROXY_BROWSER_DATA_REDUCTION_PROXY_TAMPER_DETECTION_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h"
#include <string.h>
#include <algorithm>
#include <map>
#include <vector>
#include "base/base64.h"
#include "base/md5.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
#include "net/android/network_library.h"
#endif
namespace {
void HeadersToRaw(std::string* headers) {
std::replace(headers->begin(), headers->end(), '\n', '\0');
if (!headers->empty())
*headers += '\0';
}
// Calcuates MD5 hash value for a string and then base64 encode it. Testcases
// contain expected fingerprint in plain text, which needs to be encoded before
// comparison.
std::string GetEncoded(const std::string& input) {
base::MD5Digest digest;
base::MD5Sum(input.c_str(), input.size(), &digest);
std::string base64encoded;
base::Base64Encode(std::string((char*)digest.a,
ARRAYSIZE_UNSAFE(digest.a)), &base64encoded);
return base64encoded;
}
// Replaces all contents within "[]" by corresponding base64 encoded MD5 value.
// It can handle nested case like: [[abc]def]. This helper function transforms
// fingerprint in plain text to actual encoded fingerprint.
void ReplaceWithEncodedString(std::string* input) {
size_t start, end, temp;
while (true) {
start = input->find("[");
if (start == std::string::npos) break;
while (true) {
temp = input->find("[", start + 1);
end = input->find("]", start + 1);
if (end != std::string::npos && end < temp)
break;
start = temp;
}
std::string need_to_encode = input->substr(start + 1, end - start - 1);
*input = input->substr(0, start) + GetEncoded(need_to_encode) +
input->substr(end + 1);
}
}
// Returns a vector contains all the values from a comma-separated string.
// Some testcases contain string representation of a vector, this helper
// function generates a vector from a input string.
std::vector<std::string> StringsToVector(const std::string& values) {
std::vector<std::string> ret;
if (values.empty())
return ret;
size_t now = 0;
size_t next;
while ((next = values.find(",", now)) != std::string::npos) {
ret.push_back(values.substr(now, next - now));
now = next + 1;
}
return ret;
}
void InitEnv() {
#if defined(OS_ANDROID)
JNIEnv* env = base::android::AttachCurrentThread();
static bool inited = false;
if (!inited) {
net::android::RegisterNetworkLibrary(env);
inited = true;
}
#endif
}
} // namespace
namespace data_reduction_proxy {
class DataReductionProxyTamperDetectionTest : public testing::Test {
};
// Tests function ValidateChromeProxyHeader.
TEST_F(DataReductionProxyTamperDetectionTest, ChromeProxy) {
// |received_fingerprint| is not the actual fingerprint from data reduction
// proxy, instead, the base64 encoded field is in plain text (within "[]")
// and needs to be encoded first.
struct {
std::string label;
std::string raw_header;
std::string received_fingerprint;
bool expected_tampered_with;
} test[] = {
{
"Checks sorting.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: c,b,a,3,2,1,fcp=f\n",
"[1,2,3,a,b,c,]",
false,
},
{
"Checks Chrome-Proxy's fingerprint removing.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: a,b,c,d,e,3,2,1,fcp=f\n",
"[1,2,3,a,b,c,d,e,]",
false,
},
{
"Checks no Chrome-Proxy header case (should not happen).",
"HTTP/1.1 200 OK\n",
"[]",
false,
},
{
"Checks empty Chrome-Proxy header case (should not happen).",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: \n",
"[,]",
false,
},
{
"Checks Chrome-Proxy header with its fingerprint only case.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: fcp=f\n",
"[]",
false,
},
{
"Checks empty Chrome-Proxy header case, with extra ',' and ' '",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: fcp=f , \n",
"[]",
false,
},
{
"Changed no value to empty value.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: fcp=f\n",
"[,]",
true,
},
{
"Changed header values.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: a,b=2,c,d=1,fcp=f\n",
"[a,b=3,c,d=1,]",
true,
},
{
"Changed order of header values.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: c,b,a,fcp=1\n",
"[c,b,a,]",
true,
},
{
"Checks Chrome-Proxy header with extra ' '.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: a , b , c, d, fcp=f\n",
"[a,b,c,d,]",
false
},
{
"Check Chrome-Proxy header with multiple lines and ' '.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: a , c , d, fcp=f \n"
"Chrome-Proxy: b \n",
"[a,b,c,d,]",
false
},
{
"Checks Chrome-Proxy header with multiple lines, at different positions",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: a \n"
"Chrome-Proxy: c \n"
"Content-Type: 1\n"
"Cache-Control: 2\n"
"ETag: 3\n"
"Chrome-Proxy: b \n"
"Connection: 4\n"
"Expires: 5\n"
"Chrome-Proxy: fcp=f \n"
"Via: \n"
"Content-Length: 12345\n",
"[a,b,c,]",
false
},
{
"Checks Chrome-Proxy header with multiple same values.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: a \n"
"Chrome-Proxy: b\n"
"Chrome-Proxy: c\n"
"Chrome-Proxy: d, fcp=f \n"
"Chrome-Proxy: a \n",
"[a,a,b,c,d,]",
false
},
{
"Changed Chrome-Proxy header with multiple lines..",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: a\n"
"Chrome-Proxy: a\n"
"Chrome-Proxy: b\n"
"Chrome-Proxy: c,fcp=f\n",
"[a,b,c,]",
true,
},
{
"Checks case whose received fingerprint is empty.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: a,b,c,fcp=1\n",
"[]",
true,
},
{
"Checks case whose received fingerprint cannot be base64 decoded.",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: a,b,c,fcp=1\n",
"not_base64_encoded",
true,
},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
ReplaceWithEncodedString(&test[i].received_fingerprint);
std::string raw_headers(test[i].raw_header);
HeadersToRaw(&raw_headers);
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(raw_headers));
DataReductionProxyTamperDetection tamper_detection(headers, true, 0);
bool tampered = tamper_detection.ValidateChromeProxyHeader(
test[i].received_fingerprint);
EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
}
}
// Tests function ValidateViaHeader.
TEST_F(DataReductionProxyTamperDetectionTest, Via) {
struct {
std::string label;
std::string raw_header;
std::string received_fingerprint;
bool expected_tampered_with;
bool expected_has_chrome_proxy_via_header;
} test[] = {
{
"Checks the case that Chrome-Compression-Proxy occurs at the last.",
"HTTP/1.1 200 OK\n"
"Via: a, b, c, 1.1 Chrome-Compression-Proxy\n",
"",
false,
true,
},
{
"Checks when there is intermediary.",
"HTTP/1.1 200 OK\n"
"Via: a, b, c, 1.1 Chrome-Compression-Proxy, xyz\n",
"",
true,
true,
},
{
"Checks the case of empty Via header.",
"HTTP/1.1 200 OK\n"
"Via: \n",
"",
true,
false,
},
{
"Checks the case that only the data reduction proxy's Via header occurs.",
"HTTP/1.1 200 OK\n"
"Via: 1.1 Chrome-Compression-Proxy \n",
"",
false,
true,
},
{
"Checks the case that there are ' ', i.e., empty value after the data"
" reduction proxy's Via header.",
"HTTP/1.1 200 OK\n"
"Via: 1.1 Chrome-Compression-Proxy , , \n",
"",
false,
true,
},
{
"Checks the case when there is no Via header",
"HTTP/1.1 200 OK\n",
"",
true,
false,
},
// Same to above test cases, but with deprecated data reduciton proxy Via
// header.
{
"Checks the case that Chrome Compression Proxy occurs at the last.",
"HTTP/1.1 200 OK\n"
"Via: a, b, c, 1.1 Chrome Compression Proxy\n",
"",
false,
true,
},
{
"Checks when there is intermediary.",
"HTTP/1.1 200 OK\n"
"Via: a, b, c, 1.1 Chrome Compression Proxy, xyz\n",
"",
true,
true,
},
{
"Checks the case of empty Via header.",
"HTTP/1.1 200 OK\n"
"Via: \n",
"",
true,
false,
},
{
"Checks the case that only the data reduction proxy's Via header occurs.",
"HTTP/1.1 200 OK\n"
"Via: 1.1 Chrome Compression Proxy \n",
"",
false,
true,
},
{
"Checks the case that there are ' ', i.e., empty value after the data"
"reduction proxy's Via header.",
"HTTP/1.1 200 OK\n"
"Via: 1.1 Chrome Compression Proxy , , \n",
"",
false,
true,
},
{
"Checks the case when there is no Via header",
"HTTP/1.1 200 OK\n",
"",
true,
false,
},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
std::string raw_headers(test[i].raw_header);
HeadersToRaw(&raw_headers);
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(raw_headers));
DataReductionProxyTamperDetection tamper_detection(headers, true, 0);
bool has_chrome_proxy_via_header;
bool tampered = tamper_detection.ValidateViaHeader(
test[i].received_fingerprint, &has_chrome_proxy_via_header);
EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
EXPECT_EQ(test[i].expected_has_chrome_proxy_via_header,
has_chrome_proxy_via_header) << test[i].label;
}
}
// Tests function ValidateOtherHeaders.
TEST_F(DataReductionProxyTamperDetectionTest, OtherHeaders) {
// For following testcases, |received_fingerprint| is not the actual
// fingerprint from data reduction proxy, instead, the base64 encoded field
// is in plain text (within "[]") and needs to be encoded first. For example,
// "[12345;]|content-length" needs to be encoded to
// "Base64Encoded(MD5(12345;))|content-length" before calling the checking
// function.
struct {
std::string label;
std::string raw_header;
std::string received_fingerprint;
bool expected_tampered_with;
} test[] = {
{
"Checks the case that only one header is requested.",
"HTTP/1.1 200 OK\n"
"Content-Length: 12345\n",
"[12345,;]|content-length",
false
},
{
"Checks the case that there is only one requested header and it does not"
"exist.",
"HTTP/1.1 200 OK\n",
"[;]|non_exist_header",
false
},
{
"Checks the case of multiple headers are requested.",
"HTTP/1.1 200 OK\n"
"Content-Type: 1\n"
"Cache-Control: 2\n"
"ETag: 3\n"
"Connection: 4\n"
"Expires: 5\n",
"[1,;2,;3,;4,;5,;]|content-type|cache-control|etag|connection|expires",
false
},
{
"Checks the case that one header has multiple values.",
"HTTP/1.1 200 OK\n"
"Content-Type: aaa1, bbb1, ccc1\n"
"Cache-Control: aaa2\n",
"[aaa1,bbb1,ccc1,;aaa2,;]|content-type|cache-control",
false
},
{
"Checks the case that one header has multiple lines.",
"HTTP/1.1 200 OK\n"
"Content-Type: aaa1, ccc1\n"
"Content-Type: xxx1, bbb1, ccc1\n"
"Cache-Control: aaa2\n",
"[aaa1,bbb1,ccc1,ccc1,xxx1,;aaa2,;]|content-type|cache-control",
false
},
{
"Checks the case that more than one headers have multiple values.",
"HTTP/1.1 200 OK\n"
"Content-Type: aaa1, ccc1\n"
"Cache-Control: ccc2 , bbb2\n"
"Content-Type: bbb1, ccc1\n"
"Cache-Control: aaa2 \n",
"[aaa1,bbb1,ccc1,ccc1,;aaa2,bbb2,ccc2,;]|content-type|cache-control",
false
},
{
"Checks the case that one of the requested headers is missing (Expires).",
"HTTP/1.1 200 OK\n"
"Content-Type: aaa1, ccc1\n",
"[aaa1,ccc1,;;]|content-type|expires",
false
},
{
"Checks the case that some of the requested headers have empty value.",
"HTTP/1.1 200 OK\n"
"Content-Type: \n"
"Cache-Control: \n",
"[,;,;]|content-type|cache-control",
false
},
{
"Checks the case that all the requested headers are missing.",
"HTTP/1.1 200 OK\n",
"[;;]|content-type|expires",
false
},
{
"Checks the case that some headers are missing, some of them are empty.",
"HTTP/1.1 200 OK\n"
"Cache-Control: \n",
"[;,;]|content-type|cache-control",
false
},
{
"Checks the case there is no requested header (header list is empty).",
"HTTP/1.1 200 OK\n"
"Chrome-Proxy: aut=aauutthh,bbbypas=0,aaxxx=xxx,bbbloc=1\n"
"Content-Type: 1\n"
"Cache-Control: 2\n",
"[]",
false
},
{
"Checks tampered requested header values.",
"HTTP/1.1 200 OK\n"
"Content-Type: aaa1, ccc1\n"
"Cache-Control: ccc2 , bbb2\n",
"[aaa1,bbb1,;bbb2,ccc2,;]|content-type|cache-control",
true
},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
ReplaceWithEncodedString(&test[i].received_fingerprint);
std::string raw_headers(test[i].raw_header);
HeadersToRaw(&raw_headers);
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(raw_headers));
DataReductionProxyTamperDetection tamper_detection(headers, true, 0);
bool tampered = tamper_detection.ValidateOtherHeaders(
test[i].received_fingerprint);
EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
}
}
// Tests function ValidateContentLengthHeader.
TEST_F(DataReductionProxyTamperDetectionTest, ContentLength) {
struct {
std::string label;
std::string raw_header;
std::string received_fingerprint;
bool expected_tampered_with;
} test[] = {
{
"Checks the case fingerprint matches received response.",
"HTTP/1.1 200 OK\n"
"Content-Length: 12345\n",
"12345",
false,
},
{
"Checks case that response got modified.",
"HTTP/1.1 200 OK\n"
"Content-Length: 12345\n",
"125",
true,
},
{
"Checks the case that the data reduction proxy has not sent"
"Content-Length header.",
"HTTP/1.1 200 OK\n"
"Content-Length: 12345\n",
"",
false,
},
{
"Checks the case that the data reduction proxy sends invalid"
"Content-Length header.",
"HTTP/1.1 200 OK\n"
"Content-Length: 12345\n",
"aaa",
false,
},
{
"Checks the case that the data reduction proxy sends invalid"
"Content-Length header.",
"HTTP/1.1 200 OK\n"
"Content-Length: aaa\n",
"aaa",
false,
},
{
"Checks the case that Content-Length header is missing at the Chromium"
"client side.",
"HTTP/1.1 200 OK\n",
"123",
false,
},
{
"Checks the case that Content-Length header are missing at both end.",
"HTTP/1.1 200 OK\n",
"",
false,
},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
std::string raw_headers(test[i].raw_header);
HeadersToRaw(&raw_headers);
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(raw_headers));
DataReductionProxyTamperDetection tamper_detection(headers, true, 0);
bool tampered = tamper_detection.ValidateContentLengthHeader(
test[i].received_fingerprint);
EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
}
}
// Tests ValuesToSortedString function.
TEST_F(DataReductionProxyTamperDetectionTest, ValuesToSortedString) {
struct {
std::string label;
std::string input_values;
std::string expected_output_string;
} test[] = {
{
"Checks the correctness of sorting.",
"3,2,1,",
"1,2,3,",
},
{
"Checks the case that there is an empty input vector.",
"",
"",
},
{
"Checks the case that there is an empty string in the input vector.",
",",
",",
},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
std::vector<std::string> input_values =
StringsToVector(test[i].input_values);
std::string output_string =
DataReductionProxyTamperDetection::ValuesToSortedString(&input_values);
EXPECT_EQ(output_string, test[i].expected_output_string) << test[i].label;
}
}
// Tests GetHeaderValues function.
TEST_F(DataReductionProxyTamperDetectionTest, GetHeaderValues) {
struct {
std::string label;
std::string raw_header;
std::string header_name;
std::string expected_output_values;
} test[] = {
{
"Checks the correctness of getting single line header.",
"HTTP/1.1 200 OK\n"
"test: 1, 2, 3\n",
"test",
"1,2,3,",
},
{
"Checks the correctness of getting multiple lines header.",
"HTTP/1.1 200 OK\n"
"test: 1, 2, 3\n"
"test: 4, 5, 6\n"
"test: 7, 8, 9\n",
"test",
"1,2,3,4,5,6,7,8,9,",
},
{
"Checks the correctness of getting missing header.",
"HTTP/1.1 200 OK\n",
"test",
"",
},
{
"Checks the correctness of getting empty header.",
"HTTP/1.1 200 OK\n"
"test: \n",
"test",
",",
},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
std::string raw_headers(test[i].raw_header);
HeadersToRaw(&raw_headers);
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(raw_headers));
std::vector<std::string> expected_output_values =
StringsToVector(test[i].expected_output_values);
std::vector<std::string> output_values =
DataReductionProxyTamperDetection::GetHeaderValues(
headers, test[i].header_name);
EXPECT_EQ(expected_output_values, output_values) << test[i].label;
}
}
// Tests main function DetectAndReport.
TEST_F(DataReductionProxyTamperDetectionTest, DetectAndReport) {
struct {
std::string label;
std::string raw_header;
bool expected_tampered_with;
} test[] = {
{
"Check no fingerprint added case.",
"HTTP/1.1 200 OK\n"
"Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
"Content-Length: 12345\n"
"Chrome-Proxy: bypass=0\n",
false,
},
{
"Check the case Chrome-Proxy fingerprint doesn't match.",
"HTTP/1.1 200 OK\n"
"Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
"Content-Length: 12345\n"
"header1: header_1\n"
"header2: header_2\n"
"header3: header_3\n"
"Chrome-Proxy: fcl=12345, "
"foh=[header_1,;header_2,;header_3,;]|header1|header2|header3,fvia=0,"
"fcp=abc\n",
true,
},
{
"Check the case response matches the fingerprint completely.",
"HTTP/1.1 200 OK\n"
"Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
"Content-Length: 12345\n"
"header1: header_1\n"
"header2: header_2\n"
"header3: header_3\n"
"Chrome-Proxy: fcl=12345, "
"foh=[header_1,;header_2,;header_3,;]|header1|header2|header3,"
"fvia=0, fcp=[fcl=12345,foh=[header_1,;header_2,;header_3,;]"
"|header1|header2|header3,fvia=0,]\n",
false,
},
{
"Check the case that Content-Length doesn't match.",
"HTTP/1.1 200 OK\n"
"Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
"Content-Length: 0\n"
"header1: header_1\n"
"header2: header_2\n"
"header3: header_3\n"
"Chrome-Proxy: fcl=12345, "
"foh=[header_1,;header_2,;header_3,;]|header1|header2|header3, fvia=0, "
"fcp=[fcl=12345,foh=[header_1,;header_2,;header_3,;]|"
"header1|header2|header3,fvia=0,]\n",
true,
},
};
InitEnv();
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
std::string raw_headers(test[i].raw_header);
ReplaceWithEncodedString(&raw_headers);
HeadersToRaw(&raw_headers);
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(raw_headers));
EXPECT_EQ(test[i].expected_tampered_with,
DataReductionProxyTamperDetection::DetectAndReport(headers, true))
<< test[i].label;
}
}
} // namespace
...@@ -3426,6 +3426,78 @@ Therefore, the affected-histogram name has to have at least one dot in it. ...@@ -3426,6 +3426,78 @@ Therefore, the affected-histogram name has to have at least one dot in it.
</summary> </summary>
</histogram> </histogram>
<histogram name="DataReductionProxy.HeaderTamperDetectionHTTP">
<owner>xingx@chromium.org</owner>
<owner>bolian@chromium.org</owner>
<owner>bengr@chromium.org</owner>
<summary>
For each carrier, the total number of HTTP responses that have been checked
for tampering. This assumes the data reduction proxy injected fingerprints
have not been tampered with. Only the data reduction proxy responses with
200 OK response code are checked.
</summary>
</histogram>
<histogram name="DataReductionProxy.HeaderTamperDetectionHTTPS">
<owner>xingx@chromium.org</owner>
<owner>bolian@chromium.org</owner>
<owner>bengr@chromium.org</owner>
<summary>
For each carrier, the total number of HTTPS responses that have been checked
for tampering. This assumes the data reduction proxy injected fingerprints
have not been tampered with. Only the data reduction proxy responses with
200 OK response code are checked.
</summary>
</histogram>
<histogram name="DataReductionProxy.HeaderTamperDetectionPassHTTP">
<owner>xingx@chromium.org</owner>
<owner>bolian@chromium.org</owner>
<owner>bengr@chromium.org</owner>
<summary>
For each carrier, the total number of HTTP responses that passed the tamper
detection. This assumes the data reduction proxy injected fingerprints have
not been tampered with. Only the data reduction proxy responses with 200 OK
response code are checked.
</summary>
</histogram>
<histogram name="DataReductionProxy.HeaderTamperDetectionPassHTTPS">
<owner>xingx@chromium.org</owner>
<owner>bolian@chromium.org</owner>
<owner>bengr@chromium.org</owner>
<summary>
For each carrier, the total number of HTTPs responses that passed the tamper
detection. This assumes the data reduction proxy injected fingerprints have
not been tampered with. Only the data reduction proxy responses with 200 OK
response code are checked.
</summary>
</histogram>
<histogram name="DataReductionProxy.HeaderTamperedHTTP">
<owner>xingx@chromium.org</owner>
<owner>bolian@chromium.org</owner>
<owner>bengr@chromium.org</owner>
<summary>
The total number of HTTP responses that some part (specified by suffix name)
have been tampered with. This assumes the data reduction proxy injected
fingerprints have not been tampered with. Only the data reduction proxy
responses with 200 OK response code are checked.
</summary>
</histogram>
<histogram name="DataReductionProxy.HeaderTamperedHTTPS">
<owner>xingx@chromium.org</owner>
<owner>bolian@chromium.org</owner>
<owner>bengr@chromium.org</owner>
<summary>
The total number of HTTPS responses that some part (specified by suffix
name) have been tampered with. This assumes the data reduction proxy
injected fingerprints have not been tampered with. Only the data reduction
proxy responses with 200 OK response code are checked.
</summary>
</histogram>
<histogram name="DataReductionProxy.NetworkChangeEvents" <histogram name="DataReductionProxy.NetworkChangeEvents"
enum="DataReductionProxyNetworkChangeEvent"> enum="DataReductionProxyNetworkChangeEvent">
<owner>bengr@chromium.org</owner> <owner>bengr@chromium.org</owner>
...@@ -50208,6 +50280,78 @@ Therefore, the affected-histogram name has to have at least one dot in it. ...@@ -50208,6 +50280,78 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<affected-histogram name="PLT.PT_StartToFinish"/> <affected-histogram name="PLT.PT_StartToFinish"/>
</histogram_suffixes> </histogram_suffixes>
<histogram_suffixes name="DataReductionProxy_TamperingFingerprints"
separator="_">
<suffix name="ChromeProxy"
label="for each carrier, number of tamperings detected on Chrome-Proxy
header"/>
<suffix name="ContentLength"
label="for each carrier, total number of responses whose Content-Length
header has been tampered with"/>
<suffix name="ContentLength_CSS"
label="for each carrier, number of CSS responses whose Content-Length
header has been tampered with"/>
<suffix name="ContentLength_Image"
label="for each carrier, number of image responses whose Content-Length
header has been tampered with"/>
<suffix name="ContentLength_JS"
label="for each carrier, number of JavaScript responses whose
Content-Length header has been tampered with"/>
<suffix name="ContentLength_Other"
label="for each carrier, number of other type responses whose
Content-Length header has been tampered with"/>
<suffix name="OtherHeaders"
label="for each carrier, number of tamperings detected on a list of
headers"/>
<suffix name="Via"
label="for each carrier, number of tamperings detected on Via header"/>
<suffix name="Via_Missing"
label="for each carrier, number of responses whose data reduction
proxy's Via header is missing"/>
<affected-histogram name="DataReductionProxy.HeaderTamperedHTTP"/>
<affected-histogram name="DataReductionProxy.HeaderTamperedHTTPS"/>
</histogram_suffixes>
<histogram_suffixes name="DataReductionProxy_TamperingTotal" separator="_">
<suffix name="Total" label="total number of tamperings detected"/>
<affected-histogram name="DataReductionProxy.HeaderTamperDetectionHTTP"/>
<affected-histogram name="DataReductionProxy.HeaderTamperDetectionHTTPS"/>
<affected-histogram name="DataReductionProxy.HeaderTamperDetectionPassHTTP"/>
<affected-histogram name="DataReductionProxy.HeaderTamperDetectionPassHTTPS"/>
<affected-histogram name="DataReductionProxy.HeaderTamperedHTTP_ChromeProxy"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTP_ContentLength"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTP_ContentLength_CSS"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTP_ContentLength_Image"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTP_ContentLength_JS"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTP_ContentLength_Other"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTP_OtherHeaders"/>
<affected-histogram name="DataReductionProxy.HeaderTamperedHTTP_Via"/>
<affected-histogram name="DataReductionProxy.HeaderTamperedHTTP_Via_Missing"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTPS_ChromeProxy"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTPS_ContentLength"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTPS_ContentLength_CSS"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Image"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTPS_ContentLength_JS"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Other"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTPS_OtherHeaders"/>
<affected-histogram name="DataReductionProxy.HeaderTamperedHTTPS_Via"/>
<affected-histogram
name="DataReductionProxy.HeaderTamperedHTTPS_Via_Missing"/>
</histogram_suffixes>
<histogram_suffixes name="DataReductionProxyBypassedBytes" separator="."> <histogram_suffixes name="DataReductionProxyBypassedBytes" separator=".">
<suffix name="SSL" label="Bypass due to SSL"/> <suffix name="SSL" label="Bypass due to SSL"/>
<suffix name="LocalBypassRules" <suffix name="LocalBypassRules"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment