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_field) { | |
21 std::vector<std::string> values; | |
22 std::string value; | |
23 void* iter = NULL; | |
24 while (headers->EnumerateHeader(&iter, header_field, &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_header = ""; | |
32 | |
33 std::sort(values.begin(), values.end()); | |
34 for (size_t i = 0; i < values.size(); ++i) | |
35 aggregated_header += values[i] + ","; | |
36 return aggregated_header; | |
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); | |
46 } | |
47 | |
48 namespace data_reduction_proxy { | |
49 | |
50 bool CheckHeaderChromeProxy(const std::string f1, | |
bolian
2014/06/25 18:55:56
I think it is better to return true in case of pos
xingx
2014/06/25 20:55:43
Done.
| |
51 const net::HttpResponseHeaders* headers) { | |
52 // I call fingerprint from FW, received_fingerprint; the fingerprint | |
53 // generated from contents is called actual_fingerprint | |
54 std::string received_f1; | |
55 if (!base::Base64Decode(f1, &received_f1)) { | |
56 LOG(WARNING) << "Xing f1 base64 decode fails"; // remove later | |
57 return true; | |
58 } | |
59 | |
60 // need to get all the values of Chrome-Proxy, remove value fp=xxx, | |
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_f1 = GetMD5(ValuesToSortedString(values)); | |
73 | |
74 return received_f1.compare(actual_f1) == 0; | |
75 } | |
76 | |
77 bool CheckHeaderVia(const std::string f2, | |
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) { | |
93 // exist_chrome = true; | |
94 exist_hidden = (i < (int)(vias.size() - 1)); | |
95 break; | |
96 } | |
97 | |
98 return !exist_hidden; | |
99 } | |
100 | |
101 bool CheckHeaderOtherHeaders(const std::string f3, | |
102 const net::HttpResponseHeaders* headers) { | |
103 std::string received_f3; | |
104 | |
105 // f3 format: | |
106 // f3 = [base64fingerprint]:header_name1:header_namer2:... | |
107 net::HttpUtil::ValuesIterator it(f3.begin(), f3.end(), ':'); | |
108 if (!(it.GetNext() && base::Base64Decode( | |
109 std::string(it.value_begin(), it.value_end()), | |
110 &received_f3))) { | |
111 LOG(WARNING) << "Xing f3 base64 decode fails"; | |
112 return true; | |
113 } | |
114 | |
115 // get header value for each header specified in f3 | |
116 std::string header_values = ""; | |
117 while (it.GetNext()) { | |
118 std::vector<std::string> values = GetHeaderValues(headers, | |
119 std::string(it.value_begin(), | |
120 it.value_end())); | |
121 header_values += ValuesToSortedString(values) + ";"; | |
122 } | |
123 | |
124 // calculate actual_f3 | |
125 std::string actual_f3 = GetMD5(header_values); | |
126 | |
127 return received_f3.compare(actual_f3) == 0; | |
128 } | |
129 | |
130 bool CheckHeaderContentLength(const std::string f4, | |
131 const net::HttpResponseHeaders* headers, | |
132 std::string* mime_type) { | |
133 bool equal = true; | |
134 // if content_length from FW is not empty, check; | |
135 // otherwise, pass. | |
136 if (f4.size()) { | |
137 int received_content_length, actual_content_length; | |
138 if (!base::StringToInt(f4, &received_content_length)) | |
139 return true; | |
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)) | |
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 if (!equal) | |
152 headers->GetMimeType(mime_type); | |
153 LOG(WARNING) << "xing type "<<*mime_type; | |
154 return equal; | |
155 } | |
156 | |
157 void CheckResponseFingerprint(const net::HttpResponseHeaders* headers, | |
158 const bool is_secure_scheme) | |
159 { | |
160 // schemeIsSecure, we may need to change name, it means it's default | |
161 // FW on HTTPS or fallback FW on HTTP | |
162 | |
163 // put values of Chrome-Proxy into a vector, check if it has a "fp=" value | |
164 std::vector<std::string> values = GetHeaderValues(headers, "Chrome-Proxy"); | |
165 | |
166 // enumerate and check if we see "fp=" value | |
167 int fingerprint_index = -1; | |
168 for (size_t i=0; i<values.size(); ++i) { | |
169 if (values[i].find("fp=") == 0) { | |
170 fingerprint_index = i; | |
171 break; | |
172 } | |
173 } | |
174 | |
175 if (fingerprint_index == -1) | |
176 return; | |
177 | |
178 // delimiter "|", separate fp= string: fp=f1|f2|f3|f4 | |
179 net::HttpUtil::ValuesIterator it(values[fingerprint_index].begin() + 3, | |
180 values[fingerprint_index].end(), '|'); | |
181 | |
182 // we found "fp=" value, need to check fingerprint, first get carrier ID | |
183 unsigned mcc_mnc = 0; | |
184 base::StringToUint(net::android::GetTelephonyNetworkOperator(), &mcc_mnc); | |
185 | |
186 // log total number for tamper detect | |
187 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
188 is_secure_scheme ? | |
189 "DataReductionProxy.HTTPSHeaderTampereDetected" : | |
190 "DataReductionProxy.HTTPHeaderTampereDetected", | |
191 mcc_mnc); | |
192 | |
193 | |
194 // check fingerprint one by one | |
195 if (!it.GetNext()) return; | |
196 if (!CheckHeaderChromeProxy(std::string(it.value_begin(), it.value_end()), | |
197 headers)) { | |
198 // need to check if it's HTTP or HTTPS | |
199 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
200 is_secure_scheme ? | |
201 "DataReductionProxy.HTTPSHeaderTampered_ChromeProxy" : | |
202 "DataReductionProxy.HTTPHeaderTampered_ChromeProxy", | |
203 mcc_mnc); | |
204 | |
205 LOG(WARNING)<<"Xing f1 not equal"; | |
206 } | |
207 | |
208 if (!it.GetNext()) return; | |
209 if (!CheckHeaderVia(std::string(it.value_begin(), it.value_end()), | |
210 headers)) { | |
211 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
212 is_secure_scheme ? | |
213 "DataReductionProxy.HTTPSHeaderTampered_Via" : | |
214 "DataReductionProxy.HTTPHeaderTampered_Via", | |
215 mcc_mnc); | |
216 | |
217 LOG(WARNING)<<"Xing f2 not equal"; | |
218 } | |
219 | |
220 if (!it.GetNext()) return; | |
221 if (!CheckHeaderOtherHeaders(std::string(it.value_begin(), it.value_end()), | |
222 headers)) { | |
223 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
224 is_secure_scheme ? | |
225 "DataReductionProxy.HTTPSHeaderTampered_OtherHeaders" : | |
226 "DataReductionProxy.HTTPHeaderTampered_OtherHeaders", | |
227 mcc_mnc); | |
228 LOG(WARNING)<<"Xing f3 not equal"; | |
229 } | |
230 | |
231 std::string mime_type; | |
232 if (!it.GetNext()) return; | |
233 if (!CheckHeaderContentLength(std::string(it.value_begin(), it.value_end()), | |
234 headers, &mime_type)) { | |
235 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
236 is_secure_scheme ? | |
237 "DataReductionProxy.HTTPSHeaderTampered_ContentLength" : | |
238 "DataReductionProxy.HTTPHeaderTampered_ContentLength", | |
239 mcc_mnc); | |
240 | |
241 if (mime_type.compare("text/javascript") == 0 || | |
242 mime_type.compare("application/x-javascript") == 0 || | |
243 mime_type.compare("application/javascript") == 0) { | |
244 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
245 is_secure_scheme ? | |
246 "DataReductionProxy.HTTPSHeaderTampered_ContentLength_JS" : | |
247 "DataReductionProxy.HTTPHeaderTampered_ContentLength_JS", | |
248 mcc_mnc); | |
249 | |
250 LOG(WARNING) << "Xing mimetype JS"; | |
251 } | |
252 else if (mime_type.compare("text/css") == 0) { | |
253 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
254 is_secure_scheme ? | |
255 "DataReductionProxy.HTTPSHeaderTampered_ContentLength_CSS" : | |
256 "DataReductionProxy.HTTPHeaderTampered_ContentLength_CSS", | |
257 mcc_mnc); | |
258 | |
259 LOG(WARNING) << "Xing mimetype CSS"; | |
260 } | |
261 else if (mime_type.find("image") == 0) { | |
262 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
263 is_secure_scheme ? | |
264 "DataReductionProxy.HTTPSHeaderTampered_ContentLength_Image" : | |
265 "DataReductionProxy.HTTPHeaderTampered_ContentLength_Image", | |
266 mcc_mnc); | |
267 | |
268 LOG(WARNING) << "Xing mimetype Image"; | |
269 } | |
270 else { | |
271 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
272 is_secure_scheme ? | |
273 "DataReductionProxy.HTTPSHeaderTampered_ContentLength_Other" : | |
274 "DataReductionProxy.HTTPHeaderTampered_ContentLength_Other", | |
275 mcc_mnc); | |
276 | |
277 LOG(WARNING) << "Xing mimetype Other"; | |
278 } | |
279 LOG(WARNING)<<"Xing f4 not equal"; | |
280 } | |
281 } | |
282 | |
283 } // namespace data_reduction_proxy | |
OLD | NEW |