Changeset 239742 in webkit
- Timestamp:
- Jan 8, 2019, 1:28:53 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 14 added
- 17 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/http/tests/cookies/same-site/resources/click-hyperlink.php (modified) (1 diff)
-
LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin-expected.txt (added)
-
LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin.html (added)
-
LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation-expected.txt (added)
-
LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation.html (added)
-
LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation-expected.txt (added)
-
LayoutTests/http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation.html (added)
-
LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt (added)
-
LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes.html (added)
-
LayoutTests/http/tests/security/frameNavigation/resources/iframe-that-performs-parent-navigation.html (modified) (1 diff)
-
LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-failure-page.html (added)
-
LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-same-origin.html (added)
-
LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-with-previous-user-gesture.html (added)
-
LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-success-page-with-user-gesture.html (added)
-
LayoutTests/http/tests/security/resources/should-have-loaded.html (added)
-
LayoutTests/http/tests/security/resources/should-not-have-loaded.html (added)
-
LayoutTests/http/tests/security/resources/xss-DENIED-window-open-parent-attacker.html (modified) (1 diff)
-
LayoutTests/http/tests/security/xss-DENIED-window-open-parent-expected.txt (modified) (1 diff)
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/dom/Document.cpp (modified) (9 diffs)
-
Source/WebCore/dom/Document.h (modified) (2 diffs)
-
Source/WebCore/dom/UserGestureIndicator.cpp (modified) (2 diffs)
-
Source/WebCore/page/DOMWindow.cpp (modified) (2 diffs)
-
Source/WebCore/page/DOMWindow.h (modified) (1 diff)
-
Source/WebCore/page/Frame.h (modified) (2 diffs)
-
Source/WebCore/page/Location.cpp (modified) (3 diffs)
-
Source/WebCore/page/Settings.yaml (modified) (1 diff)
-
Source/WebCore/platform/network/ResourceRequestBase.h (modified) (1 diff)
-
Source/WebKit/ChangeLog (modified) (1 diff)
-
Source/WebKit/Shared/WebPreferences.yaml (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r239732 r239742 1 2019-01-08 Chris Dumez <cdumez@apple.com> 2 3 Prevent cross-site top-level navigations from third-party iframes 4 https://bugs.webkit.org/show_bug.cgi?id=193076 5 <rdar://problem/36074736> 6 7 Reviewed by Alex Christensen. 8 9 Add layout test coverage. 10 11 * http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin-expected.txt: Added. 12 * http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin.html: Added. 13 * http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation-expected.txt: Added. 14 * http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation.html: Added. 15 * http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation-expected.txt: Added. 16 * http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation.html: Added. 17 * http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt: Added. 18 * http/tests/security/block-top-level-navigations-by-third-party-iframes.html: Added. 19 * http/tests/security/resources/navigate-top-level-frame-to-failure-page.html: Added. 20 * http/tests/security/resources/navigate-top-level-frame-to-success-page-same-origin.html: Added. 21 * http/tests/security/resources/navigate-top-level-frame-to-success-page-with-previous-user-gesture.html: Added. 22 * http/tests/security/resources/navigate-top-level-frame-to-success-page-with-user-gesture.html: Added. 23 * http/tests/security/resources/should-have-loaded.html: Added. 24 * http/tests/security/resources/should-not-have-loaded.html: Added. 25 1 26 2019-01-08 Truitt Savell <tsavell@apple.com> 2 27 -
trunk/LayoutTests/http/tests/cookies/same-site/resources/click-hyperlink.php
r230944 r239742 14 14 ?> 15 15 <a href="<?php echo $_GET['href']; ?>" <?php echo $targetAttribute; ?>>Click</a> 16 <script>document.querySelector("a").click()</script> 16 <script> 17 internals.withUserGesture(() => { 18 document.querySelector("a").click(); 19 }); 20 </script> 17 21 </body> 18 22 </html> -
trunk/LayoutTests/http/tests/security/frameNavigation/resources/iframe-that-performs-parent-navigation.html
r120174 r239742 12 12 function performTest() 13 13 { 14 parent.location = "http://localhost:8000/security/frameNavigation/resources/navigation-changed-iframe.html"; 14 internals.withUserGesture(() => { 15 parent.location = "http://localhost:8000/security/frameNavigation/resources/navigation-changed-iframe.html"; 16 }); 15 17 } 16 18 </script> -
trunk/LayoutTests/http/tests/security/resources/xss-DENIED-window-open-parent-attacker.html
r91152 r239742 1 1 <script> 2 open("javascript:alert('failed')", "_top"); 3 parent.postMessage("", "*"); 2 internals.withUserGesture(() => { 3 open("javascript:alert('failed')", "_top"); 4 parent.postMessage("", "*"); 5 }); 4 6 </script> -
trunk/LayoutTests/http/tests/security/xss-DENIED-window-open-parent-expected.txt
r178527 r239742 1 CONSOLE MESSAGE: line 2: Blocked a frame with origin "http://localhost:8080" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.1 CONSOLE MESSAGE: line 3: Blocked a frame with origin "http://localhost:8080" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match. 2 2 This test passes if there is no alert dialog. 3 3 -
trunk/Source/WebCore/ChangeLog
r239737 r239742 1 2019-01-08 Chris Dumez <cdumez@apple.com> 2 3 Prevent cross-site top-level navigations from third-party iframes 4 https://bugs.webkit.org/show_bug.cgi?id=193076 5 <rdar://problem/36074736> 6 7 Reviewed by Alex Christensen. 8 9 Prevent cross-site top-level navigations from third-party iframes if the following conditions are met: 10 1. Its tries to navigate the top-level page cross-site (different eTDL+1) 11 2. The user has never interacted with the third-party iframe or any of its subframes 12 13 This experiment's intent is to block suspicious main-frame navigations by third-party content. The feature 14 is behind a runtime experimental feature flag, on by default. 15 16 Tests: http/tests/security/allow-top-level-navigations-by-third-party-iframes-to-same-origin.html 17 http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-previous-user-activation.html 18 http/tests/security/allow-top-level-navigations-by-third-party-iframes-with-user-activation.html 19 http/tests/security/block-top-level-navigations-by-third-party-iframes.html 20 21 * dom/Document.cpp: 22 (WebCore::printNavigationErrorMessage): 23 (WebCore::Document::canNavigate): 24 (WebCore::Document::canNavigateInternal): 25 (WebCore::Document::isNavigationBlockedByThirdPartyIFrameRedirectBlocking): 26 * dom/Document.h: 27 * dom/UserGestureIndicator.cpp: 28 * page/DOMWindow.cpp: 29 (WebCore::DOMWindow::setLocation): 30 * page/DOMWindow.h: 31 * page/Frame.h: 32 * page/Location.cpp: 33 (WebCore::Location::replace): 34 (WebCore::Location::setLocation): 35 * page/Settings.yaml: 36 1 37 2019-01-08 Alex Christensen <achristensen@webkit.org> 2 38 -
trunk/Source/WebCore/dom/Document.cpp
r239640 r239742 457 457 } 458 458 459 static void printNavigationErrorMessage(Frame *frame, const URL& activeURL, const char* reason)460 { 461 String message = "Unsafe JavaScript attempt to initiate navigation for frame with URL '" + frame ->document()->url().string() + "' from frame with URL '" + activeURL.string() + "'. " + reason + "\n";459 static void printNavigationErrorMessage(Frame& frame, const URL& activeURL, const char* reason) 460 { 461 String message = "Unsafe JavaScript attempt to initiate navigation for frame with URL '" + frame.document()->url().string() + "' from frame with URL '" + activeURL.string() + "'. " + reason + "\n"; 462 462 463 463 // FIXME: should we print to the console of the document performing the navigation instead? 464 frame ->document()->domWindow()->printErrorMessage(message);464 frame.document()->domWindow()->printErrorMessage(message); 465 465 } 466 466 … … 3338 3338 } 3339 3339 3340 bool Document::canNavigate(Frame* targetFrame )3340 bool Document::canNavigate(Frame* targetFrame, const URL& destinationURL) 3341 3341 { 3342 3342 if (!m_frame) … … 3349 3349 return true; 3350 3350 3351 if (!canNavigateInternal(*targetFrame)) 3352 return false; 3353 3354 if (isNavigationBlockedByThirdPartyIFrameRedirectBlocking(*targetFrame, destinationURL)) { 3355 printNavigationErrorMessage(*targetFrame, url(), "The frame attempting navigation of the top-level window is cross-origin and the user has never interacted with the frame."_s); 3356 return false; 3357 } 3358 3359 return true; 3360 } 3361 3362 bool Document::canNavigateInternal(Frame& targetFrame) 3363 { 3364 ASSERT(m_frame); 3365 3351 3366 // Cases (i), (ii) and (iii) pass the tests from the specifications but might not pass the "security origin" tests. 3352 3367 3353 3368 // i. A frame can navigate its top ancestor when its 'allow-top-navigation' flag is set (sometimes known as 'frame-busting'). 3354 if (!isSandboxed(SandboxTopNavigation) && targetFrame == &m_frame->tree().top())3369 if (!isSandboxed(SandboxTopNavigation) && &targetFrame == &m_frame->tree().top()) 3355 3370 return true; 3356 3371 3357 3372 // ii. A frame can navigate its top ancestor when its 'allow-top-navigation-by-user-activation' flag is set and navigation is triggered by user activation. 3358 if (!isSandboxed(SandboxTopNavigationByUserActivation) && UserGestureIndicator::processingUserGesture() && targetFrame == &m_frame->tree().top())3373 if (!isSandboxed(SandboxTopNavigationByUserActivation) && UserGestureIndicator::processingUserGesture() && &targetFrame == &m_frame->tree().top()) 3359 3374 return true; 3360 3375 3361 3376 // iii. A sandboxed frame can always navigate its descendants. 3362 if (isSandboxed(SandboxNavigation) && targetFrame ->tree().isDescendantOf(m_frame))3377 if (isSandboxed(SandboxNavigation) && targetFrame.tree().isDescendantOf(m_frame)) 3363 3378 return true; 3364 3379 … … 3366 3381 // 1. If A is not the same browsing context as B, and A is not one of the ancestor browsing contexts of B, and B is not a top-level browsing context, and A's active document's active sandboxing 3367 3382 // flag set has its sandboxed navigation browsing context flag set, then abort these steps negatively. 3368 if (m_frame != targetFrame && isSandboxed(SandboxNavigation) && targetFrame->tree().parent() && !targetFrame->tree().isDescendantOf(m_frame)) {3383 if (m_frame != &targetFrame && isSandboxed(SandboxNavigation) && targetFrame.tree().parent() && !targetFrame.tree().isDescendantOf(m_frame)) { 3369 3384 printNavigationErrorMessage(targetFrame, url(), "The frame attempting navigation is sandboxed, and is therefore disallowed from navigating its ancestors."_s); 3370 3385 return false; … … 3372 3387 3373 3388 // 2. Otherwise, if B is a top-level browsing context, and is one of the ancestor browsing contexts of A, then: 3374 if (m_frame != targetFrame &&targetFrame == &m_frame->tree().top()) {3389 if (m_frame != &targetFrame && &targetFrame == &m_frame->tree().top()) { 3375 3390 bool triggeredByUserActivation = UserGestureIndicator::processingUserGesture(); 3376 3391 // 1. If this algorithm is triggered by user activation and A's active document's active sandboxing flag set has its sandboxed top-level navigation with user activation browsing context flag set, then abort these steps negatively. … … 3388 3403 // 3. Otherwise, if B is a top-level browsing context, and is neither A nor one of the ancestor browsing contexts of A, and A's Document's active sandboxing flag set has its 3389 3404 // sandboxed navigation browsing context flag set, and A is not the one permitted sandboxed navigator of B, then abort these steps negatively. 3390 if (!targetFrame ->tree().parent() && m_frame != targetFrame && targetFrame != &m_frame->tree().top() && isSandboxed(SandboxNavigation) && targetFrame->loader().opener() != m_frame) {3405 if (!targetFrame.tree().parent() && m_frame != &targetFrame && &targetFrame != &m_frame->tree().top() && isSandboxed(SandboxNavigation) && targetFrame.loader().opener() != m_frame) { 3391 3406 printNavigationErrorMessage(targetFrame, url(), "The frame attempting navigation is sandboxed, and is not allowed to navigate this popup."_s); 3392 3407 return false; … … 3402 3417 // See http://www.adambarth.com/papers/2008/barth-jackson-mitchell.pdf for 3403 3418 // historical information about this security check. 3404 if (canAccessAncestor(securityOrigin(), targetFrame))3419 if (canAccessAncestor(securityOrigin(), &targetFrame)) 3405 3420 return true; 3406 3421 … … 3416 3431 // and/or "parent" relation). Requiring some sort of relation prevents a 3417 3432 // document from navigating arbitrary, unrelated top-level frames. 3418 if (!targetFrame ->tree().parent()) {3419 if ( targetFrame == m_frame->loader().opener())3433 if (!targetFrame.tree().parent()) { 3434 if (&targetFrame == m_frame->loader().opener()) 3420 3435 return true; 3421 3436 3422 if (canAccessAncestor(securityOrigin(), targetFrame ->loader().opener()))3437 if (canAccessAncestor(securityOrigin(), targetFrame.loader().opener())) 3423 3438 return true; 3424 3439 } … … 3426 3441 printNavigationErrorMessage(targetFrame, url(), "The frame attempting navigation is neither same-origin with the target, nor is it the target's parent or opener."); 3427 3442 return false; 3443 } 3444 3445 // Prevent cross-site top-level redirects from third-party iframes unless the user has ever interacted with the frame. 3446 bool Document::isNavigationBlockedByThirdPartyIFrameRedirectBlocking(Frame& targetFrame, const URL& destinationURL) 3447 { 3448 if (!settings().thirdPartyIframeRedirectBlockingEnabled()) 3449 return false; 3450 3451 // Only prevent top frame navigations by subframes. 3452 if (m_frame == &targetFrame || &targetFrame != &m_frame->tree().top()) 3453 return false; 3454 3455 // Only prevent navigations by subframes that the user has not interacted with. 3456 if (m_frame->hasHadUserInteraction()) 3457 return false; 3458 3459 // Only prevent navigations by unsandboxed iframes. Such navigations by unsandboxed iframes would have already been blocked unless 3460 // "allow-top-navigation" / "allow-top-navigation-by-user-activation" was explicitly specified. 3461 if (sandboxFlags() != SandboxNone) 3462 return false; 3463 3464 // Only prevent navigations by third-party iframes. 3465 if (canAccessAncestor(securityOrigin(), &targetFrame)) 3466 return false; 3467 3468 // Only prevent cross-site navigations. 3469 auto* targetDocument = targetFrame.document(); 3470 if (targetDocument && (targetDocument->securityOrigin().canAccess(SecurityOrigin::create(destinationURL)) || registrableDomainsAreEqual(targetDocument->url(), destinationURL))) 3471 return false; 3472 3473 return true; 3428 3474 } 3429 3475 -
trunk/Source/WebCore/dom/Document.h
r239640 r239742 707 707 SocketProvider* socketProvider() final; 708 708 709 bool canNavigate(Frame* targetFrame );709 bool canNavigate(Frame* targetFrame, const URL& destinationURL = URL()); 710 710 711 711 bool usesStyleBasedEditability() const; … … 1645 1645 void checkAppearanceDependentPictures(); 1646 1646 1647 bool canNavigateInternal(Frame& targetFrame); 1648 bool isNavigationBlockedByThirdPartyIFrameRedirectBlocking(Frame& targetFrame, const URL& destinationURL); 1649 1647 1650 #if ENABLE(INTERSECTION_OBSERVER) 1648 1651 void notifyIntersectionObserversTimerFired(); -
trunk/Source/WebCore/dom/UserGestureIndicator.cpp
r239427 r239742 28 28 29 29 #include "Document.h" 30 #include "Frame.h" 30 31 #include "ResourceLoadObserver.h" 31 32 #include <wtf/MainThread.h> … … 60 61 ResourceLoadObserver::shared().logUserInteractionWithReducedTimeResolution(document->topDocument()); 61 62 document->topDocument().setUserDidInteractWithPage(true); 63 if (auto* frame = document->frame()) { 64 if (!frame->hasHadUserInteraction()) { 65 for (; frame; frame = frame->tree().parent()) 66 frame->setHasHadUserInteraction(); 67 } 68 } 62 69 } 63 70 } -
trunk/Source/WebCore/page/DOMWindow.cpp
r239639 r239742 2104 2104 } 2105 2105 2106 void DOMWindow::setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& urlString, SetLocationLocking locking)2106 void DOMWindow::setLocation(DOMWindow& activeWindow, const URL& completedURL, SetLocationLocking locking) 2107 2107 { 2108 2108 if (!isCurrentlyDisplayedInFrame()) … … 2114 2114 2115 2115 auto* frame = this->frame(); 2116 if (!activeDocument->canNavigate(frame)) 2117 return; 2118 2119 Frame* firstFrame = firstWindow.frame(); 2120 if (!firstFrame) 2121 return; 2122 2123 URL completedURL = firstFrame->document()->completeURL(urlString); 2124 if (completedURL.isNull()) 2116 if (!activeDocument->canNavigate(frame, completedURL)) 2125 2117 return; 2126 2118 -
trunk/Source/WebCore/page/DOMWindow.h
r239427 r239742 146 146 147 147 Location& location(); 148 void setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& location, SetLocationLocking = LockHistoryBasedOnGestureState);148 void setLocation(DOMWindow& activeWindow, const URL& completedURL, SetLocationLocking = LockHistoryBasedOnGestureState); 149 149 150 150 DOMSelection* getSelection(); -
trunk/Source/WebCore/page/Frame.h
r238771 r239742 173 173 bool documentIsBeingReplaced() const { return m_documentIsBeingReplaced; } 174 174 175 bool hasHadUserInteraction() const { return m_hasHadUserInteraction; } 176 void setHasHadUserInteraction() { m_hasHadUserInteraction = true; } 177 175 178 // ======== All public functions below this point are candidates to move out of Frame into another class. ======== 176 179 … … 349 352 unsigned m_navigationDisableCount { 0 }; 350 353 unsigned m_selfOnlyRefCount { 0 }; 354 bool m_hasHadUserInteraction { false }; 351 355 352 356 protected: -
trunk/Source/WebCore/page/Location.cpp
r238771 r239742 226 226 } 227 227 228 void Location::replace(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& url )228 void Location::replace(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& urlString) 229 229 { 230 230 auto* frame = this->frame(); … … 233 233 ASSERT(frame->document()); 234 234 ASSERT(frame->document()->domWindow()); 235 236 Frame* firstFrame = firstWindow.frame(); 237 if (!firstFrame || !firstFrame->document()) 238 return; 239 240 URL completedURL = firstFrame->document()->completeURL(urlString); 241 // FIXME: The specification says to throw a SyntaxError if the URL is not valid. 242 if (completedURL.isNull()) 243 return; 244 235 245 // We call DOMWindow::setLocation directly here because replace() always operates on the current frame. 236 frame->document()->domWindow()->setLocation(activeWindow, firstWindow, url, LockHistoryAndBackForwardList);246 frame->document()->domWindow()->setLocation(activeWindow, completedURL, LockHistoryAndBackForwardList); 237 247 } 238 248 … … 265 275 } 266 276 267 ExceptionOr<void> Location::setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& url )277 ExceptionOr<void> Location::setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& urlString) 268 278 { 269 279 auto* frame = this->frame(); 270 280 ASSERT(frame); 271 if (!activeWindow.document()->canNavigate(frame)) 281 282 Frame* firstFrame = firstWindow.frame(); 283 if (!firstFrame || !firstFrame->document()) 284 return { }; 285 286 URL completedURL = firstFrame->document()->completeURL(urlString); 287 // FIXME: The specification says to throw a SyntaxError if the URL is not valid. 288 if (completedURL.isNull()) 289 return { }; 290 291 if (!activeWindow.document()->canNavigate(frame, completedURL)) 272 292 return Exception { SecurityError }; 293 273 294 ASSERT(frame->document()); 274 295 ASSERT(frame->document()->domWindow()); 275 frame->document()->domWindow()->setLocation(activeWindow, firstWindow, url);296 frame->document()->domWindow()->setLocation(activeWindow, completedURL); 276 297 return { }; 277 298 } -
trunk/Source/WebCore/page/Settings.yaml
r239474 r239742 333 333 initial: false 334 334 335 thirdPartyIframeRedirectBlockingEnabled: 336 initial: true 337 335 338 cookieEnabled: 336 339 initial: true -
trunk/Source/WebCore/platform/network/ResourceRequestBase.h
r239427 r239742 261 261 inline String toRegistrableDomain(const URL& a) 262 262 { 263 return ResourceRequestBase::partitionName(a.host().toString()); 263 auto host = a.host().toString(); 264 auto registrableDomain = ResourceRequestBase::partitionName(host); 265 // Fall back to the host if we cannot determine the registrable domain. 266 return registrableDomain.isEmpty() ? host : registrableDomain; 264 267 } 265 268 -
trunk/Source/WebKit/ChangeLog
r239741 r239742 1 2019-01-08 Chris Dumez <cdumez@apple.com> 2 3 Prevent cross-site top-level navigations from third-party iframes 4 https://bugs.webkit.org/show_bug.cgi?id=193076 5 <rdar://problem/36074736> 6 7 Reviewed by Alex Christensen. 8 9 Add experimental feature flag, on by default. 10 11 * Shared/WebPreferences.yaml: 12 1 13 2019-01-08 Alex Christensen <achristensen@webkit.org> 2 14 -
trunk/Source/WebKit/Shared/WebPreferences.yaml
r239704 r239742 41 41 humanReadableName: "Automatic HTTPS upgrade" 42 42 humanReadableDescription: "Automatic HTTPS upgrade for known supported sites" 43 category: experimental 44 45 ThirdPartyIframeRedirectBlockingEnabled: 46 type: bool 47 defaultValue: true 48 humanReadableName: "Block top-level redirects by third-party iframes" 49 humanReadableDescription: "Block top-level redirects by third-party iframes" 43 50 category: experimental 44 51
Note:
See TracChangeset
for help on using the changeset viewer.