Skip to content

Commit

Permalink
Cherry-pick 272448.100@safari-7618-branch (7f3ac60). https://bugs.web…
Browse files Browse the repository at this point in the history
…kit.org/show_bug.cgi?id=264811

    Content-Type x-mixed-replace can be abused to bypass CSP
    https://bugs.webkit.org/show_bug.cgi?id=264811
    rdar://118394343

    Reviewed by John Wilander and Brent Fulgham.

    When replacing the document in a multipart/x-mixed-replace response, the
    DocumentLoader would reset its CSP every time a new response was received.
    This change makes the CSP persistent across document replacements when
    loading multipart content. Now the CSP can only become more restrictive
    as new parts are received.

    * LayoutTests/http/tests/security/contentSecurityPolicy/multipart-three-part-expected.txt: Added.
    * LayoutTests/http/tests/security/contentSecurityPolicy/multipart-three-part.py: Added.
    * LayoutTests/http/tests/security/contentSecurityPolicy/multipart-two-part-expected.txt: Added.
    * LayoutTests/http/tests/security/contentSecurityPolicy/multipart-two-part.py: Added.
    * Source/WebCore/loader/DocumentLoader.cpp:
    (WebCore::DocumentLoader::shouldClearContentSecurityPolicyForResponse const):
    (WebCore::DocumentLoader::responseReceived):
    * Source/WebCore/loader/DocumentLoader.h:

    Canonical link: https://commits.webkit.org/272448.100@safari-7618-branch

Canonical link: https://commits.webkit.org/274313.62@webkitglib/2.44
  • Loading branch information
rreno authored and aperezdc committed Mar 11, 2024
1 parent b99816d commit f5909af
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CONSOLE MESSAGE: Blocked script execution in 'http://127.0.0.1:8000/security/contentSecurityPolicy/multipart-three-part.py' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.
layer at (0,0) size 800x600
RenderView at (0,0) size 800x600
layer at (0,0) size 800x600
RenderBlock {HTML} at (0,0) size 800x600
RenderBody {BODY} at (8,8) size 784x584
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env python3

import sys

sys.stdout.write(
'Content-Type: multipart/x-mixed-replace; boundary=TEST\r\n'
'Content-Security-Policy: sandbox\r\n\r\n'
'--TEST\r\n'
'Content-Type: text/html\r\n\r\n'
'--TEST\r\n'
'Content-Type: text/html\r\n'
'Content-Security-Policy:\r\n\r\n'
'<script>alert("FAIL")</script>\r\n'
'--TEST--'
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CONSOLE MESSAGE: Blocked script execution in 'http://127.0.0.1:8000/security/contentSecurityPolicy/multipart-two-part.py' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.
layer at (0,0) size 800x600
RenderView at (0,0) size 800x600
layer at (0,0) size 800x600
RenderBlock {HTML} at (0,0) size 800x600
RenderBody {BODY} at (8,8) size 784x584
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python3

import sys

sys.stdout.write(
'Content-Type: multipart/x-mixed-replace; boundary=TEST\r\n'
'Content-Security-Policy: sandbox\r\n\r\n'
'--TEST\r\n'
'Content-Type: text/html\r\n'
'Content-Security-Policy:\r\n\r\n'
'<script>alert("FAIL")</script>\r\n'
'--TEST--'
)
15 changes: 11 additions & 4 deletions Source/WebCore/loader/DocumentLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -897,19 +897,26 @@ static URL microsoftTeamsRedirectURL()
return URL { "https://www.microsoft.com/en-us/microsoft-365/microsoft-teams/"_str };
}

bool DocumentLoader::shouldClearContentSecurityPolicyForResponse(const ResourceResponse& response) const
{
return response.httpHeaderField(HTTPHeaderName::ContentSecurityPolicy).isNull() && !m_isLoadingMultipartContent;
}

void DocumentLoader::responseReceived(CachedResource& resource, const ResourceResponse& response, CompletionHandler<void()>&& completionHandler)
{
ASSERT_UNUSED(resource, m_mainResource == &resource);

if (!response.httpHeaderField(HTTPHeaderName::ContentSecurityPolicy).isNull()) {
if (shouldClearContentSecurityPolicyForResponse(response))
m_contentSecurityPolicy = nullptr;
else {
ReportingClient* reportingClient = nullptr;
if (m_frame && m_frame->document())
reportingClient = m_frame->document();

m_contentSecurityPolicy = makeUnique<ContentSecurityPolicy>(URL { response.url() }, nullptr, reportingClient);
if (!m_contentSecurityPolicy)
m_contentSecurityPolicy = makeUnique<ContentSecurityPolicy>(URL { response.url() }, nullptr, reportingClient);
m_contentSecurityPolicy->didReceiveHeaders(ContentSecurityPolicyResponseHeaders { response }, m_request.httpReferrer(), ContentSecurityPolicy::ReportParsingErrors::No);
} else
m_contentSecurityPolicy = nullptr;
}
if (m_frame && m_frame->document() && m_frame->document()->settings().crossOriginOpenerPolicyEnabled())
m_responseCOOP = obtainCrossOriginOpenerPolicy(response);

Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/loader/DocumentLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,8 @@ class DocumentLoader
bool maybeLoadEmpty();
void loadErrorDocument();

bool shouldClearContentSecurityPolicyForResponse(const ResourceResponse&) const;

bool isMultipartReplacingLoad() const;
bool isPostOrRedirectAfterPost(const ResourceRequest&, const ResourceResponse&);

Expand Down

0 comments on commit f5909af

Please sign in to comment.