OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2011 Adam Barth. All Rights Reserved. | 2 * Copyright (C) 2011 Adam Barth. All Rights Reserved. |
3 * Copyright (C) 2011 Daniel Bates (dbates@intudata.com). | 3 * Copyright (C) 2011 Daniel Bates (dbates@intudata.com). |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 14 matching lines...) Expand all Loading... |
25 */ | 25 */ |
26 | 26 |
27 #include "core/html/parser/XSSAuditor.h" | 27 #include "core/html/parser/XSSAuditor.h" |
28 | 28 |
29 #include "core/HTMLNames.h" | 29 #include "core/HTMLNames.h" |
30 #include "core/SVGNames.h" | 30 #include "core/SVGNames.h" |
31 #include "core/XLinkNames.h" | 31 #include "core/XLinkNames.h" |
32 #include "core/dom/Document.h" | 32 #include "core/dom/Document.h" |
33 #include "core/frame/LocalFrame.h" | 33 #include "core/frame/LocalFrame.h" |
34 #include "core/frame/Settings.h" | 34 #include "core/frame/Settings.h" |
35 #include "core/frame/csp/ContentSecurityPolicy.h" | |
36 #include "core/html/HTMLParamElement.h" | 35 #include "core/html/HTMLParamElement.h" |
37 #include "core/html/LinkRelAttribute.h" | 36 #include "core/html/LinkRelAttribute.h" |
38 #include "core/html/parser/HTMLDocumentParser.h" | 37 #include "core/html/parser/HTMLDocumentParser.h" |
39 #include "core/html/parser/HTMLParserIdioms.h" | 38 #include "core/html/parser/HTMLParserIdioms.h" |
40 #include "core/html/parser/TextResourceDecoder.h" | 39 #include "core/html/parser/TextResourceDecoder.h" |
41 #include "core/html/parser/XSSAuditorDelegate.h" | 40 #include "core/html/parser/XSSAuditorDelegate.h" |
42 #include "core/inspector/ConsoleMessage.h" | 41 #include "core/inspector/ConsoleMessage.h" |
43 #include "core/loader/DocumentLoader.h" | 42 #include "core/loader/DocumentLoader.h" |
44 #include "core/loader/MixedContentChecker.h" | 43 #include "core/loader/MixedContentChecker.h" |
45 #include "platform/network/EncodedFormData.h" | 44 #include "platform/network/EncodedFormData.h" |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 (position = decodedSnippet.find(isNotHTMLSpace<UChar>, position + 1)) != | 262 (position = decodedSnippet.find(isNotHTMLSpace<UChar>, position + 1)) != |
264 kNotFound && | 263 kNotFound && |
265 (position = decodedSnippet.find( | 264 (position = decodedSnippet.find( |
266 isTerminatingCharacter, | 265 isTerminatingCharacter, |
267 isHTMLQuote(decodedSnippet[position]) ? position + 1 : position)) != | 266 isHTMLQuote(decodedSnippet[position]) ? position + 1 : position)) != |
268 kNotFound) { | 267 kNotFound) { |
269 decodedSnippet.truncate(position); | 268 decodedSnippet.truncate(position); |
270 } | 269 } |
271 } | 270 } |
272 | 271 |
273 static ReflectedXSSDisposition combineXSSProtectionHeaderAndCSP( | |
274 ReflectedXSSDisposition xssProtection, | |
275 ReflectedXSSDisposition reflectedXSS) { | |
276 ReflectedXSSDisposition result = std::max(xssProtection, reflectedXSS); | |
277 | |
278 if (result == ReflectedXSSInvalid || result == FilterReflectedXSS || | |
279 result == ReflectedXSSUnset) | |
280 return FilterReflectedXSS; | |
281 | |
282 return result; | |
283 } | |
284 | |
285 static bool isSemicolonSeparatedAttribute( | 272 static bool isSemicolonSeparatedAttribute( |
286 const HTMLToken::Attribute& attribute) { | 273 const HTMLToken::Attribute& attribute) { |
287 return threadSafeMatch(attribute.nameAsVector(), SVGNames::valuesAttr); | 274 return threadSafeMatch(attribute.nameAsVector(), SVGNames::valuesAttr); |
288 } | 275 } |
289 | 276 |
290 static String semicolonSeparatedValueContainingJavaScriptURL( | 277 static String semicolonSeparatedValueContainingJavaScriptURL( |
291 const String& value) { | 278 const String& value) { |
292 Vector<String> valueList; | 279 Vector<String> valueList; |
293 value.split(';', valueList); | 280 value.split(';', valueList); |
294 for (size_t i = 0; i < valueList.size(); ++i) { | 281 for (size_t i = 0; i < valueList.size(); ++i) { |
295 String stripped = stripLeadingAndTrailingHTMLSpaces(valueList[i]); | 282 String stripped = stripLeadingAndTrailingHTMLSpaces(valueList[i]); |
296 if (protocolIsJavaScript(stripped)) | 283 if (protocolIsJavaScript(stripped)) |
297 return stripped; | 284 return stripped; |
298 } | 285 } |
299 return emptyString(); | 286 return emptyString(); |
300 } | 287 } |
301 | 288 |
302 XSSAuditor::XSSAuditor() | 289 XSSAuditor::XSSAuditor() |
303 : m_isEnabled(false), | 290 : m_isEnabled(false), |
304 m_xssProtection(FilterReflectedXSS), | 291 m_xssProtection(FilterReflectedXSS), |
305 m_didSendValidCSPHeader(false), | |
306 m_didSendValidXSSProtectionHeader(false), | 292 m_didSendValidXSSProtectionHeader(false), |
307 m_state(Uninitialized), | 293 m_state(Uninitialized), |
308 m_scriptTagFoundInRequest(false), | 294 m_scriptTagFoundInRequest(false), |
309 m_scriptTagNestingLevel(0), | 295 m_scriptTagNestingLevel(0), |
310 m_encoding(UTF8Encoding()) { | 296 m_encoding(UTF8Encoding()) { |
311 // Although tempting to call init() at this point, the various objects | 297 // Although tempting to call init() at this point, the various objects |
312 // we want to reference might not all have been constructed yet. | 298 // we want to reference might not all have been constructed yet. |
313 } | 299 } |
314 | 300 |
315 void XSSAuditor::initForFragment() { | 301 void XSSAuditor::initForFragment() { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 | 345 |
360 if (DocumentLoader* documentLoader = | 346 if (DocumentLoader* documentLoader = |
361 document->frame()->loader().documentLoader()) { | 347 document->frame()->loader().documentLoader()) { |
362 const AtomicString& headerValue = | 348 const AtomicString& headerValue = |
363 documentLoader->response().httpHeaderField(HTTPNames::X_XSS_Protection); | 349 documentLoader->response().httpHeaderField(HTTPNames::X_XSS_Protection); |
364 String errorDetails; | 350 String errorDetails; |
365 unsigned errorPosition = 0; | 351 unsigned errorPosition = 0; |
366 String reportURL; | 352 String reportURL; |
367 KURL xssProtectionReportURL; | 353 KURL xssProtectionReportURL; |
368 | 354 |
369 // Process the X-XSS-Protection header, then mix in the CSP header's value. | |
370 ReflectedXSSDisposition xssProtectionHeader = parseXSSProtectionHeader( | 355 ReflectedXSSDisposition xssProtectionHeader = parseXSSProtectionHeader( |
371 headerValue, errorDetails, errorPosition, reportURL); | 356 headerValue, errorDetails, errorPosition, reportURL); |
372 | 357 |
373 if (xssProtectionHeader == AllowReflectedXSS) | 358 if (xssProtectionHeader == AllowReflectedXSS) |
374 UseCounter::count(*document, UseCounter::XSSAuditorDisabled); | 359 UseCounter::count(*document, UseCounter::XSSAuditorDisabled); |
375 else if (xssProtectionHeader == FilterReflectedXSS) | 360 else if (xssProtectionHeader == FilterReflectedXSS) |
376 UseCounter::count(*document, UseCounter::XSSAuditorEnabledFilter); | 361 UseCounter::count(*document, UseCounter::XSSAuditorEnabledFilter); |
377 else if (xssProtectionHeader == BlockReflectedXSS) | 362 else if (xssProtectionHeader == BlockReflectedXSS) |
378 UseCounter::count(*document, UseCounter::XSSAuditorEnabledBlock); | 363 UseCounter::count(*document, UseCounter::XSSAuditorEnabledBlock); |
379 else if (xssProtectionHeader == ReflectedXSSInvalid) | 364 else if (xssProtectionHeader == ReflectedXSSInvalid) |
380 UseCounter::count(*document, UseCounter::XSSAuditorInvalid); | 365 UseCounter::count(*document, UseCounter::XSSAuditorInvalid); |
381 | 366 |
382 m_didSendValidXSSProtectionHeader = | 367 m_didSendValidXSSProtectionHeader = |
383 xssProtectionHeader != ReflectedXSSUnset && | 368 xssProtectionHeader != ReflectedXSSUnset && |
384 xssProtectionHeader != ReflectedXSSInvalid; | 369 xssProtectionHeader != ReflectedXSSInvalid; |
385 if ((xssProtectionHeader == FilterReflectedXSS || | 370 if ((xssProtectionHeader == FilterReflectedXSS || |
386 xssProtectionHeader == BlockReflectedXSS) && | 371 xssProtectionHeader == BlockReflectedXSS) && |
387 !reportURL.isEmpty()) { | 372 !reportURL.isEmpty()) { |
388 xssProtectionReportURL = document->completeURL(reportURL); | 373 xssProtectionReportURL = document->completeURL(reportURL); |
389 if (MixedContentChecker::isMixedContent(document->getSecurityOrigin(), | 374 if (MixedContentChecker::isMixedContent(document->getSecurityOrigin(), |
390 xssProtectionReportURL)) { | 375 xssProtectionReportURL)) { |
391 errorDetails = "insecure reporting URL for secure page"; | 376 errorDetails = "insecure reporting URL for secure page"; |
392 xssProtectionHeader = ReflectedXSSInvalid; | 377 xssProtectionHeader = ReflectedXSSInvalid; |
393 xssProtectionReportURL = KURL(); | 378 xssProtectionReportURL = KURL(); |
394 } | 379 } |
395 } | 380 } |
396 if (xssProtectionHeader == ReflectedXSSInvalid) | 381 if (xssProtectionHeader == ReflectedXSSInvalid) { |
397 document->addConsoleMessage(ConsoleMessage::create( | 382 document->addConsoleMessage(ConsoleMessage::create( |
398 SecurityMessageSource, ErrorMessageLevel, | 383 SecurityMessageSource, ErrorMessageLevel, |
399 "Error parsing header X-XSS-Protection: " + headerValue + ": " + | 384 "Error parsing header X-XSS-Protection: " + headerValue + ": " + |
400 errorDetails + " at character position " + | 385 errorDetails + " at character position " + |
401 String::format("%u", errorPosition) + | 386 String::format("%u", errorPosition) + |
402 ". The default protections will be applied.")); | 387 ". The default protections will be applied.")); |
| 388 } |
403 | 389 |
404 ReflectedXSSDisposition cspHeader = | 390 m_xssProtection = xssProtectionHeader; |
405 document->contentSecurityPolicy()->getReflectedXSSDisposition(); | 391 if (m_xssProtection == ReflectedXSSInvalid || |
406 m_didSendValidCSPHeader = | 392 m_xssProtection == ReflectedXSSUnset) { |
407 cspHeader != ReflectedXSSUnset && cspHeader != ReflectedXSSInvalid; | 393 m_xssProtection = FilterReflectedXSS; |
| 394 } |
408 | 395 |
409 m_xssProtection = | |
410 combineXSSProtectionHeaderAndCSP(xssProtectionHeader, cspHeader); | |
411 // FIXME: Combine the two report URLs in some reasonable way. | |
412 if (auditorDelegate) | 396 if (auditorDelegate) |
413 auditorDelegate->setReportURL(xssProtectionReportURL.copy()); | 397 auditorDelegate->setReportURL(xssProtectionReportURL.copy()); |
414 | 398 |
415 EncodedFormData* httpBody = documentLoader->request().httpBody(); | 399 EncodedFormData* httpBody = documentLoader->request().httpBody(); |
416 if (httpBody && !httpBody->isEmpty()) | 400 if (httpBody && !httpBody->isEmpty()) |
417 m_httpBodyAsString = httpBody->flattenToString(); | 401 m_httpBodyAsString = httpBody->flattenToString(); |
418 } | 402 } |
419 | 403 |
420 setEncoding(m_encoding); | 404 setEncoding(m_encoding); |
421 } | 405 } |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
459 else if (m_scriptTagNestingLevel) { | 443 else if (m_scriptTagNestingLevel) { |
460 if (request.token.type() == HTMLToken::Character) | 444 if (request.token.type() == HTMLToken::Character) |
461 didBlockScript = filterCharacterToken(request); | 445 didBlockScript = filterCharacterToken(request); |
462 else if (request.token.type() == HTMLToken::EndTag) | 446 else if (request.token.type() == HTMLToken::EndTag) |
463 filterEndToken(request); | 447 filterEndToken(request); |
464 } | 448 } |
465 | 449 |
466 if (didBlockScript) { | 450 if (didBlockScript) { |
467 bool didBlockEntirePage = (m_xssProtection == BlockReflectedXSS); | 451 bool didBlockEntirePage = (m_xssProtection == BlockReflectedXSS); |
468 std::unique_ptr<XSSInfo> xssInfo = XSSInfo::create( | 452 std::unique_ptr<XSSInfo> xssInfo = XSSInfo::create( |
469 m_documentURL, didBlockEntirePage, m_didSendValidXSSProtectionHeader, | 453 m_documentURL, didBlockEntirePage, m_didSendValidXSSProtectionHeader); |
470 m_didSendValidCSPHeader); | |
471 return xssInfo; | 454 return xssInfo; |
472 } | 455 } |
473 return nullptr; | 456 return nullptr; |
474 } | 457 } |
475 | 458 |
476 bool XSSAuditor::filterStartToken(const FilterTokenRequest& request) { | 459 bool XSSAuditor::filterStartToken(const FilterTokenRequest& request) { |
477 m_state = FilteringTokens; | 460 m_state = FilteringTokens; |
478 bool didBlockScript = eraseDangerousAttributesIfInjected(request); | 461 bool didBlockScript = eraseDangerousAttributesIfInjected(request); |
479 | 462 |
480 if (hasName(request.token, scriptTag)) { | 463 if (hasName(request.token, scriptTag)) { |
(...skipping 436 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
917 } | 900 } |
918 | 901 |
919 bool XSSAuditor::isSafeToSendToAnotherThread() const { | 902 bool XSSAuditor::isSafeToSendToAnotherThread() const { |
920 return m_documentURL.isSafeToSendToAnotherThread() && | 903 return m_documentURL.isSafeToSendToAnotherThread() && |
921 m_decodedURL.isSafeToSendToAnotherThread() && | 904 m_decodedURL.isSafeToSendToAnotherThread() && |
922 m_decodedHTTPBody.isSafeToSendToAnotherThread() && | 905 m_decodedHTTPBody.isSafeToSendToAnotherThread() && |
923 m_httpBodyAsString.isSafeToSendToAnotherThread(); | 906 m_httpBodyAsString.isSafeToSendToAnotherThread(); |
924 } | 907 } |
925 | 908 |
926 } // namespace blink | 909 } // namespace blink |
OLD | NEW |