OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <string.h> | |
6 #include <algorithm> | |
7 #include <vector> | |
8 | |
9 #include "base/base64.h" | |
10 #include "base/md5.h" | |
11 #include "base/metrics/sparse_histogram.h" | |
12 #include "base/strings/string_number_conversions.h" | |
13 #include "components/data_reduction_proxy/browser/data_reduction_proxy_tamper_de tect.h" | |
14 | |
15 #include "net/android/network_library.h" | |
16 #include "net/http/http_request_headers.h" | |
17 #include "net/http/http_util.h" | |
18 | |
19 std::vector<std::string> GetHeaderValues( | |
20 const net::HttpResponseHeaders* headers, const std::string header_name) { | |
21 std::vector<std::string> values; | |
22 std::string value; | |
23 void* iter = NULL; | |
24 while (headers->EnumerateHeader(&iter, header_name, &value)) { | |
25 values.push_back(value); | |
26 } | |
27 return values; | |
28 } | |
29 | |
30 std::string ValuesToSortedString(std::vector<std::string> &values) { | |
31 std::string aggregated_values = ""; | |
32 | |
33 std::sort(values.begin(), values.end()); | |
34 for (size_t i = 0; i < values.size(); ++i) | |
35 aggregated_values += values[i] + ","; | |
36 return aggregated_values; | |
37 } | |
38 | |
39 std::string GetMD5(const std::string input) { | |
40 base::MD5Context context; | |
41 base::MD5Init(&context); | |
42 base::MD5Update(&context, input); | |
43 base::MD5Digest new_digest; | |
44 base::MD5Final(&new_digest, &context); | |
45 return std::string((char*)new_digest.a, 16); | |
bolian
2014/06/27 00:49:02
Is this constant defined somewhere?
| |
46 } | |
47 | |
48 namespace data_reduction_proxy { | |
49 | |
50 bool CheckHeaderChromeProxy(const std::string fingerprint, | |
51 const net::HttpResponseHeaders* headers) { | |
52 // I call fingerprint from FW, received_fingerprint; the fingerprint | |
bolian
2014/06/27 00:49:02
Don't mention FW. I think readers want to see more
| |
53 // generated from contents is called actual_fingerprint | |
54 std::string received_fingerprint; | |
55 if (!base::Base64Decode(fingerprint, &received_fingerprint)) { | |
56 LOG(WARNING) << "Xing f1 base64 decode fails"; // remove later | |
bolian
2014/06/27 00:49:02
It is time to remove now. :)
| |
57 return false; | |
58 } | |
59 | |
60 // need to get all the values of Chrome-Proxy, remove value fp=xxx, | |
bolian
2014/06/27 00:49:02
Capitalize the first letter and add period at the
| |
61 // and calculate hash value | |
62 std::vector<std::string> values = GetHeaderValues(headers, "Chrome-Proxy"); | |
63 | |
64 for (size_t i = 0; i < values.size(); ++i) | |
65 if (values[i].find("fp=") == 0) { | |
66 values.erase(values.begin() + i); | |
67 break; | |
68 } | |
69 | |
70 // from vector of values of "Chrome-Proxy", forma a string and | |
71 // calculate the MD5 value on the string | |
72 std::string actual_fingerprint = GetMD5(ValuesToSortedString(values)); | |
73 | |
74 return received_fingerprint.compare(actual_fingerprint) != 0; | |
bolian
2014/06/27 00:49:02
return received_fingerprint != actual_fingerprint;
xingx
2014/06/27 16:34:37
Done.
| |
75 } | |
76 | |
77 bool CheckHeaderVia(const std::string fingerprint, | |
78 const net::HttpResponseHeaders* headers) { | |
79 | |
80 // right now we get f2 value from FW, we may remove this later | |
81 // since for FW's f2 value, it should always be 0 | |
82 | |
83 std::vector<std::string> vias = GetHeaderValues(headers, "via"); | |
84 | |
85 // exist_chrome represents whether there is Chrome proxy | |
86 // in Via header; | |
87 // exist_hidden represents whether there is middlebox between | |
88 // FW and phone. | |
89 //bool exist_chrome = false; | |
90 bool exist_hidden = false; | |
91 for (int i = vias.size() - 1; i >= 0; --i) | |
92 if (vias[i].find("Chrome") != std::string::npos) { | |
bolian
2014/06/27 02:16:58
Use the real header value. Expose that from compon
| |
93 // exist_chrome = true; | |
bolian
2014/06/27 00:49:02
Use HasDataReductionProxyViaHeader in components/d
| |
94 exist_hidden = (i < (int)(vias.size() - 1)); | |
95 break; | |
96 } | |
97 | |
98 return exist_hidden; | |
99 } | |
100 | |
101 bool CheckHeaderOtherHeaders(const std::string fingerprint, | |
102 const net::HttpResponseHeaders* headers) { | |
103 std::string received_fingerprint; | |
104 | |
105 // f3 format: | |
106 // f3 = [base64fingerprint]:header_name1:header_namer2:... | |
107 net::HttpUtil::ValuesIterator it(fingerprint.begin(), | |
108 fingerprint.end(), ':'); | |
109 if (!(it.GetNext() && base::Base64Decode( | |
110 std::string(it.value_begin(), it.value_end()), | |
bolian
2014/06/27 00:49:02
it.value()?
xingx
2014/06/27 16:34:37
Done.
| |
111 &received_fingerprint))) { | |
112 LOG(WARNING) << "Xing f3 base64 decode fails"; | |
113 return false; | |
114 } | |
115 | |
116 // get header value for each header specified in f3 | |
117 std::string header_values = ""; | |
bolian
2014/06/27 00:49:02
Remove the empty string literal. That is the defau
xingx
2014/06/27 16:34:37
Done.
| |
118 while (it.GetNext()) { | |
119 std::vector<std::string> values = GetHeaderValues(headers, | |
120 std::string(it.value_begin(), | |
121 it.value_end())); | |
122 header_values += ValuesToSortedString(values) + ";"; | |
123 } | |
124 | |
125 // calculate actual_f3 | |
126 std::string actual_fingerprint = GetMD5(header_values); | |
127 | |
128 return received_fingerprint.compare(actual_fingerprint) != 0; | |
129 } | |
130 | |
131 bool CheckHeaderContentLength(const std::string fingerprint, | |
132 const net::HttpResponseHeaders* headers) { | |
133 bool equal = true; | |
134 // if content_length from FW is not empty, check; | |
135 // otherwise, pass. | |
136 if (fingerprint.size()) { | |
137 int received_content_length, actual_content_length; | |
138 if (!base::StringToInt(fingerprint, &received_content_length)) | |
139 return false; | |
140 | |
141 std::string actual_content_length_; | |
142 if (headers->GetNormalizedHeader("Content-Length", | |
143 &actual_content_length_)) { | |
144 if (!base::StringToInt(actual_content_length_, &actual_content_length)) | |
bolian
2014/06/27 00:49:02
You can just compare the strings.
| |
145 return true; | |
146 // equal marks whether received length == length sent by FW | |
147 equal = (received_content_length == actual_content_length); | |
148 } | |
149 } | |
150 | |
151 return !equal; | |
152 } | |
153 | |
154 void CheckResponseFingerprint(const net::HttpResponseHeaders* headers, | |
155 const bool is_secure_scheme) | |
156 { | |
157 // schemeIsSecure, we may need to change name, it means it's default | |
158 // FW on HTTPS or fallback FW on HTTP | |
159 | |
160 // put values of Chrome-Proxy into a vector, check if it has a "fp=" value | |
161 std::vector<std::string> values = GetHeaderValues(headers, "Chrome-Proxy"); | |
bolian
2014/06/27 00:49:02
I think we should have a class that wraps these in
| |
162 | |
163 // enumerate and check if we see "fp=" value | |
164 int fingerprint_index = -1; | |
165 for (size_t i=0; i<values.size(); ++i) { | |
166 if (values[i].find("fp=") == 0) { | |
bolian
2014/06/27 00:49:02
define fp= and other keywords as constants.
xingx
2014/06/27 16:34:37
Done.
| |
167 fingerprint_index = i; | |
168 break; | |
169 } | |
170 } | |
171 | |
172 if (fingerprint_index == -1) | |
173 return; | |
174 | |
175 // delimiter "|", separate fp= string: fp=f1|f2|f3|f4 | |
bolian
2014/06/27 00:49:02
I think it might be better to encode the whole val
bolian
2014/06/27 00:49:02
What is the assumption here? All four values must
| |
176 net::HttpUtil::ValuesIterator it(values[fingerprint_index].begin() + 3, | |
bolian
2014/06/27 00:49:02
compute 3 from the keyword "fp=" you defined.
xingx
2014/06/27 16:34:37
Done.
| |
177 values[fingerprint_index].end(), '|'); | |
178 | |
179 // we found "fp=" value, need to check fingerprint, first get carrier ID | |
180 unsigned mcc_mnc = 0; | |
181 base::StringToUint(net::android::GetTelephonyNetworkOperator(), &mcc_mnc); | |
182 | |
183 // log total number for tamper detect | |
184 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
185 is_secure_scheme ? | |
186 "DataReductionProxy.HTTPSHeaderTampereDetected" : | |
bolian
2014/06/27 00:49:02
The name sounds like tamper happened. How about ..
xingx
2014/06/27 16:34:37
Done.
| |
187 "DataReductionProxy.HTTPHeaderTampereDetected", | |
188 mcc_mnc); | |
189 | |
190 | |
191 // check fingerprint one by one | |
192 if (!it.GetNext()) return; | |
193 if (CheckHeaderChromeProxy(std::string(it.value_begin(), it.value_end()), | |
194 headers)) { | |
195 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
196 is_secure_scheme ? | |
197 "DataReductionProxy.HTTPSHeaderTampered_ChromeProxy" : | |
198 "DataReductionProxy.HTTPHeaderTampered_ChromeProxy", | |
199 mcc_mnc); | |
200 | |
201 LOG(WARNING)<<"Xing f1 not equal"; | |
bolian
2014/06/27 00:49:02
Time to remove all these.
| |
202 } | |
203 | |
204 if (!it.GetNext()) return; | |
205 if (CheckHeaderVia(std::string(it.value_begin(), it.value_end()), | |
206 headers)) { | |
207 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
208 is_secure_scheme ? | |
209 "DataReductionProxy.HTTPSHeaderTampered_Via" : | |
210 "DataReductionProxy.HTTPHeaderTampered_Via", | |
211 mcc_mnc); | |
212 | |
213 LOG(WARNING)<<"Xing f2 not equal"; | |
214 } | |
215 | |
216 if (!it.GetNext()) return; | |
217 if (CheckHeaderOtherHeaders(std::string(it.value_begin(), it.value_end()), | |
218 headers)) { | |
219 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
220 is_secure_scheme ? | |
221 "DataReductionProxy.HTTPSHeaderTampered_OtherHeaders" : | |
222 "DataReductionProxy.HTTPHeaderTampered_OtherHeaders", | |
223 mcc_mnc); | |
224 LOG(WARNING)<<"Xing f3 not equal"; | |
225 } | |
226 | |
227 std::string mime_type; | |
228 if (!it.GetNext()) return; | |
229 if (CheckHeaderContentLength(std::string(it.value_begin(), it.value_end()), | |
230 headers)) { | |
231 headers->GetMimeType(&mime_type); | |
232 LOG(WARNING) << "xing type "<<mime_type; | |
233 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
234 is_secure_scheme ? | |
235 "DataReductionProxy.HTTPSHeaderTampered_ContentLength" : | |
236 "DataReductionProxy.HTTPHeaderTampered_ContentLength", | |
237 mcc_mnc); | |
238 | |
239 if (mime_type.compare("text/javascript") == 0 || | |
240 mime_type.compare("application/x-javascript") == 0 || | |
241 mime_type.compare("application/javascript") == 0) { | |
242 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
243 is_secure_scheme ? | |
244 "DataReductionProxy.HTTPSHeaderTampered_ContentLength_JS" : | |
245 "DataReductionProxy.HTTPHeaderTampered_ContentLength_JS", | |
246 mcc_mnc); | |
247 | |
248 LOG(WARNING) << "Xing mimetype JS"; | |
249 } | |
250 else if (mime_type.compare("text/css") == 0) { | |
251 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
252 is_secure_scheme ? | |
253 "DataReductionProxy.HTTPSHeaderTampered_ContentLength_CSS" : | |
254 "DataReductionProxy.HTTPHeaderTampered_ContentLength_CSS", | |
255 mcc_mnc); | |
256 | |
257 LOG(WARNING) << "Xing mimetype CSS"; | |
258 } | |
259 else if (mime_type.find("image") == 0) { | |
260 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
261 is_secure_scheme ? | |
262 "DataReductionProxy.HTTPSHeaderTampered_ContentLength_Image" : | |
263 "DataReductionProxy.HTTPHeaderTampered_ContentLength_Image", | |
264 mcc_mnc); | |
265 | |
266 LOG(WARNING) << "Xing mimetype Image"; | |
267 } | |
268 else { | |
269 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
270 is_secure_scheme ? | |
271 "DataReductionProxy.HTTPSHeaderTampered_ContentLength_Other" : | |
272 "DataReductionProxy.HTTPHeaderTampered_ContentLength_Other", | |
273 mcc_mnc); | |
274 | |
275 LOG(WARNING) << "Xing mimetype Other"; | |
276 } | |
277 LOG(WARNING)<<"Xing f4 not equal"; | |
278 } | |
279 } | |
280 | |
281 } // namespace data_reduction_proxy | |
OLD | NEW |