diff options
Diffstat (limited to 'WebCore/page/XSSAuditor.cpp')
-rw-r--r-- | WebCore/page/XSSAuditor.cpp | 46 |
1 files changed, 34 insertions, 12 deletions
diff --git a/WebCore/page/XSSAuditor.cpp b/WebCore/page/XSSAuditor.cpp index 92ed896..890c3fa 100644 --- a/WebCore/page/XSSAuditor.cpp +++ b/WebCore/page/XSSAuditor.cpp @@ -65,20 +65,23 @@ static bool isIllegalURICharacter(UChar c) // in a valid URI: ', ", <, > // // If the request does not contain these characters then we can assume that no inline scripts have been injected - // into response page, because it is impossible to write an inline script of the form <script>...</script> + // into the response page, because it is impossible to write an inline script of the form <script>...</script> // without "<", ">". return (c == '\'' || c == '"' || c == '<' || c == '>'); } -String XSSAuditor::CachingURLCanonicalizer::canonicalizeURL(const String& url, const TextEncoding& encoding, bool decodeEntities) +String XSSAuditor::CachingURLCanonicalizer::canonicalizeURL(const String& url, const TextEncoding& encoding, bool decodeEntities, + bool decodeURLEscapeSequencesTwice) { - if (decodeEntities == m_decodeEntities && encoding == m_encoding && url == m_inputURL) + if (decodeEntities == m_decodeEntities && decodeURLEscapeSequencesTwice == m_decodeURLEscapeSequencesTwice + && encoding == m_encoding && url == m_inputURL) return m_cachedCanonicalizedURL; - m_cachedCanonicalizedURL = canonicalize(decodeURL(url, encoding, decodeEntities)); + m_cachedCanonicalizedURL = canonicalize(decodeURL(url, encoding, decodeEntities, decodeURLEscapeSequencesTwice)); m_inputURL = url; m_encoding = encoding; m_decodeEntities = decodeEntities; + m_decodeURLEscapeSequencesTwice = decodeURLEscapeSequencesTwice; return m_cachedCanonicalizedURL; } @@ -115,7 +118,7 @@ bool XSSAuditor::canEvaluateJavaScriptURL(const String& code) const if (!isEnabled()) return true; - if (findInRequest(code)) { + if (findInRequest(code, true, false, true)) { DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute a JavaScript script. Source code of script found within request.\n")); m_frame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, consoleMessage, 1, String()); return false; @@ -141,6 +144,16 @@ bool XSSAuditor::canLoadExternalScriptFromSrc(const String& context, const Strin if (!isEnabled()) return true; + // If the script is loaded from the same URL as the enclosing page, it's + // probably not an XSS attack, so we reduce false positives by allowing the + // script. If the script has a query string, we're more suspicious, + // however, because that's pretty rare and the attacker might be able to + // trick a server-side script into doing something dangerous with the query + // string. + KURL scriptURL(m_frame->document()->url(), url); + if (m_frame->document()->url().host() == scriptURL.host() && scriptURL.query().isEmpty()) + return true; + if (findInRequest(context + url)) { DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute a JavaScript script. Source code of script found within request.\n")); m_frame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, consoleMessage, 1, String()); @@ -182,7 +195,7 @@ String XSSAuditor::canonicalize(const String& string) return result.removeCharacters(&isNonCanonicalCharacter); } -String XSSAuditor::decodeURL(const String& string, const TextEncoding& encoding, bool decodeEntities) +String XSSAuditor::decodeURL(const String& string, const TextEncoding& encoding, bool decodeEntities, bool decodeURLEscapeSequencesTwice) { String result; String url = string; @@ -193,6 +206,13 @@ String XSSAuditor::decodeURL(const String& string, const TextEncoding& encoding, String decodedResult = encoding.decode(utf8Url.data(), utf8Url.length()); if (!decodedResult.isEmpty()) result = decodedResult; + if (decodeURLEscapeSequencesTwice) { + result = decodeURLEscapeSequences(result); + utf8Url = result.utf8(); + decodedResult = encoding.decode(utf8Url.data(), utf8Url.length()); + if (!decodedResult.isEmpty()) + result = decodedResult; + } if (decodeEntities) result = decodeHTMLEntities(result); return result; @@ -235,18 +255,20 @@ String XSSAuditor::decodeHTMLEntities(const String& string, bool leaveUndecodabl return String::adopt(result); } -bool XSSAuditor::findInRequest(const String& string, bool decodeEntities, bool allowRequestIfNoIllegalURICharacters) const +bool XSSAuditor::findInRequest(const String& string, bool decodeEntities, bool allowRequestIfNoIllegalURICharacters, + bool decodeURLEscapeSequencesTwice) const { bool result = false; Frame* parentFrame = m_frame->tree()->parent(); if (parentFrame && m_frame->document()->url() == blankURL()) - result = findInRequest(parentFrame, string, decodeEntities, allowRequestIfNoIllegalURICharacters); + result = findInRequest(parentFrame, string, decodeEntities, allowRequestIfNoIllegalURICharacters, decodeURLEscapeSequencesTwice); if (!result) - result = findInRequest(m_frame, string, decodeEntities, allowRequestIfNoIllegalURICharacters); + result = findInRequest(m_frame, string, decodeEntities, allowRequestIfNoIllegalURICharacters, decodeURLEscapeSequencesTwice); return result; } -bool XSSAuditor::findInRequest(Frame* frame, const String& string, bool decodeEntities, bool allowRequestIfNoIllegalURICharacters) const +bool XSSAuditor::findInRequest(Frame* frame, const String& string, bool decodeEntities, bool allowRequestIfNoIllegalURICharacters, + bool decodeURLEscapeSequencesTwice) const { ASSERT(frame->document()); @@ -285,7 +307,7 @@ bool XSSAuditor::findInRequest(Frame* frame, const String& string, bool decodeEn if (string.length() < pageURL.length()) { // The string can actually fit inside the pageURL. - String decodedPageURL = m_cache.canonicalizeURL(pageURL, frame->document()->decoder()->encoding(), decodeEntities); + String decodedPageURL = m_cache.canonicalizeURL(pageURL, frame->document()->decoder()->encoding(), decodeEntities, decodeURLEscapeSequencesTwice); if (allowRequestIfNoIllegalURICharacters && (!formDataObj || formDataObj->isEmpty()) && decodedPageURL.find(&isIllegalURICharacter, 0) == -1) @@ -302,7 +324,7 @@ bool XSSAuditor::findInRequest(Frame* frame, const String& string, bool decodeEn // the url-encoded POST data because the length of the url-decoded // code is less than or equal to the length of the url-encoded // string. - String decodedFormData = m_cache.canonicalizeURL(formData, frame->document()->decoder()->encoding(), decodeEntities); + String decodedFormData = m_cache.canonicalizeURL(formData, frame->document()->decoder()->encoding(), decodeEntities, decodeURLEscapeSequencesTwice); if (decodedFormData.find(canonicalizedString, 0, false) != -1) return true; // We found the string in the POST data. } |