| 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 |