Bug 158875

Summary: Add flag allow-popups-to-escape-sandbox to iframe sandbox
Product: WebKit Reporter: Bin Lu <binlu>
Component: FramesAssignee: Frédéric Wang (:fredw) <fred.wang>
Status: RESOLVED FIXED    
Severity: Normal CC: aestes, ap, beidson, bfulgham, binlu, buildbot, cdumez, clopez, commit-queue, darin, dbates, dvoytenko, esprehn+autocc, fred.wang, japhet, kangil.han, mark.lam, mkwst, rniwa, sam, simon.fraser, webkit-bug-importer, wilander
Priority: P2 Keywords: HTML5, InRadar
Version: WebKit Nightly Build   
Hardware: All   
OS: All   
Bug Depends on:    
Bug Blocks: 175300, 173162    
Attachments:
Description Flags
Patch
buildbot: commit-queue-
Archive of layout-test-results from ews102 for mac-elcapitan
none
Archive of layout-test-results from ews106 for mac-elcapitan-wk2
none
Archive of layout-test-results from ews112 for mac-elcapitan
none
Archive of layout-test-results from ews123 for ios-simulator-wk2
none
Patch
none
Patch
cdumez: review-
Patch none

Description Bin Lu 2016-06-17 08:52:32 PDT
There have been some improvements proposed to iframe sandbox:
https://wiki.whatwg.org/wiki/Iframe_sandbox_improvments
https://html.spec.whatwg.org/multipage/browsers.html#attr-iframe-sandbox-allow-popups-to-escape-sandbox
https://html.spec.whatwg.org/multipage/browsers.html#attr-iframe-sandbox-allow-modals

The reasoning behind these improvements is to make iframe sandbox usable for framed unsafe content (e.g., advertisements) and to disallow modal dialogs by default in sandboxed iframe:
allow-popups-to-escape-sandbox: With this flag, if a user interacts with an ad and clicks on it, it should be able to open up the ads landing page as a top level page in a new tab without being sandboxed.
allow-modals: The content inside sandboxed iframe should not be able to open modal dialogs unless this flag is specified.

Both Chrome and Firefox are supporting the new attributes now:
https://googlechrome.github.io/samples/allow-popups-to-escape-sandbox/index.html
https://googlechrome.github.io/samples/block-modal-dialogs-sandboxed-iframe/
https://bugzilla.mozilla.org/show_bug.cgi?id=1190641
Comment 1 Radar WebKit Bug Importer 2016-06-20 16:06:36 PDT
<rdar://problem/26904051>
Comment 2 John Wilander 2016-06-20 17:14:38 PDT
I don't remember if it's part of the spec but we should make sure the call to confirm() returns false if the call was blocked by the sandbox. Doing so provides a safe fallback if an iframe's code is denied to ask the user for confirmation.

Otherwise "allow-modals" will allow an attacker to grant JavaScript execution but block a confirm prompt. A benign service might want to allow iframe integration but do what it can to prohibit clickjacking. Knowing whether you were able to display a confirmation UI may be crucial.
Comment 3 Mike West 2016-06-21 00:04:12 PDT
Regarding `confirm()`, the spec does indeed require returning false if this sandboxing flag is present: see step 2 of https://html.spec.whatwg.org/multipage/webappapis.html#dom-confirm.
Comment 4 John Wilander 2016-06-21 16:57:04 PDT
Thanks, Mike!
Comment 5 Bin Lu 2016-12-19 09:30:12 PST
I'm wondering if there is any progress on this or if it's planned to be done early 2017.

Since many user complain about malicious ads, ads sandboxing have been been used to protect users from malicious ads on both mobile and desktop browsers. But Safari (or WebKit) not supporting "allow-popups-to-escape-sandbox" is blocking  sandboxing to be used to protect Safari users. Since Chrome, Firefox, Opera, etc are already supporting it, it would be great if Safari could support it soon.
Comment 6 Dima Voytenko 2017-04-25 09:54:38 PDT
Likewise, asking for update. Adding this sandbox flag would improve security on Web overall. This is currently a blocker for many embeds to turn on sandbox.
Comment 7 Frédéric Wang (:fredw) 2017-04-26 04:42:53 PDT
I plan to give a try to the allow-popups-to-escape-sandbox. I'm moving the allow-modals part to bug 171321 and renaming this one.
Comment 8 Frédéric Wang (:fredw) 2017-04-26 07:45:13 PDT
Created attachment 308244 [details]
Patch
Comment 9 Frédéric Wang (:fredw) 2017-04-26 07:46:53 PDT
OK, it was not so easy as I thought. There was a subtle case to handle in order to pass iframe_sandbox_popups_escaping-3... I basically copied the logic in Chromium to handle that case (see https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/frame/Frame.cpp?q=Frame::CanNavigateWithoutFramebusting&dr=CSs&l=258).
Comment 10 Build Bot 2017-04-26 09:03:26 PDT
Comment on attachment 308244 [details]
Patch

Attachment 308244 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.webkit.org/results/3610077

New failing tests:
http/tests/security/frameNavigation/xss-ALLOWED-parent-navigation-change.html
fast/files/null-origin-string.html
http/tests/fileapi/create-blob-url-from-data-url.html
http/tests/security/xss-DENIED-window-open-parent.html
Comment 11 Build Bot 2017-04-26 09:03:28 PDT
Created attachment 308253 [details]
Archive of layout-test-results from ews102 for mac-elcapitan

The attached test failures were seen while running run-webkit-tests on the mac-ews.
Bot: ews102  Port: mac-elcapitan  Platform: Mac OS X 10.11.6
Comment 12 Build Bot 2017-04-26 09:13:48 PDT
Comment on attachment 308244 [details]
Patch

Attachment 308244 [details] did not pass mac-wk2-ews (mac-wk2):
Output: http://webkit-queues.webkit.org/results/3610125

New failing tests:
http/tests/security/frameNavigation/xss-ALLOWED-parent-navigation-change.html
http/tests/fileapi/create-blob-url-from-data-url.html
http/tests/security/xss-DENIED-window-open-parent.html
Comment 13 Build Bot 2017-04-26 09:13:49 PDT
Created attachment 308254 [details]
Archive of layout-test-results from ews106 for mac-elcapitan-wk2

The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews.
Bot: ews106  Port: mac-elcapitan-wk2  Platform: Mac OS X 10.11.6
Comment 14 Build Bot 2017-04-26 09:22:06 PDT
Comment on attachment 308244 [details]
Patch

Attachment 308244 [details] did not pass mac-debug-ews (mac):
Output: http://webkit-queues.webkit.org/results/3610123

New failing tests:
http/tests/security/frameNavigation/xss-ALLOWED-parent-navigation-change.html
fast/files/null-origin-string.html
http/tests/fileapi/create-blob-url-from-data-url.html
http/tests/security/xss-DENIED-window-open-parent.html
Comment 15 Build Bot 2017-04-26 09:22:08 PDT
Created attachment 308256 [details]
Archive of layout-test-results from ews112 for mac-elcapitan

The attached test failures were seen while running run-webkit-tests on the mac-debug-ews.
Bot: ews112  Port: mac-elcapitan  Platform: Mac OS X 10.11.6
Comment 16 Build Bot 2017-04-26 09:42:25 PDT
Comment on attachment 308244 [details]
Patch

Attachment 308244 [details] did not pass ios-sim-ews (ios-simulator-wk2):
Output: http://webkit-queues.webkit.org/results/3610201

New failing tests:
http/tests/security/frameNavigation/xss-ALLOWED-parent-navigation-change.html
http/tests/fileapi/create-blob-url-from-data-url.html
http/tests/security/xss-DENIED-window-open-parent.html
Comment 17 Build Bot 2017-04-26 09:42:27 PDT
Created attachment 308261 [details]
Archive of layout-test-results from ews123 for ios-simulator-wk2

The attached test failures were seen while running run-webkit-tests on the ios-sim-ews.
Bot: ews123  Port: ios-simulator-wk2  Platform: Mac OS X 10.11.6
Comment 18 Bin Lu 2017-04-26 10:15:55 PDT
Thanks Fred for working on this!
Really looking forward to seeing its ship!

I've just created a follow-up bug for another flag 'allow-top-navigation-by-user-activation':
https://bugs.webkit.org/show_bug.cgi?id=171327
Comment 19 Frédéric Wang (:fredw) 2017-04-26 13:19:57 PDT
Created attachment 308278 [details]
Patch
Comment 20 Frédéric Wang (:fredw) 2017-05-08 11:49:32 PDT
@bfulgram: Since you reviewed bug 134850, could you please take a look at this? Thanks!
Comment 21 Chris Dumez 2017-06-04 10:33:53 PDT
Comment on attachment 308278 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=308278&action=review

> Source/WebCore/dom/Document.cpp:2979
> +        const char* reason = nullptr;

Let's call this failureReason, for clarity.

> Source/WebCore/dom/Document.cpp:2980
> +        if (!targetFrame->tree().isDescendantOf(m_frame) && targetFrame->tree().parent())

I do not understand the need for the "&& targetFrame->tree().parent()" check. We know:
1. targetFrame != m_frame, otherwise we would have returned early.
2. targetFrame is a descendant of m_frame based on of isDescendantOf() check.

Therefore, I'd believe that targetFrame has to have a parent.

> Source/WebCore/dom/Document.cpp:2982
> +        else if (!targetFrame->tree().parent() && targetFrame != &m_frame->tree().top() && isSandboxed(SandboxPropagatesToAuxiliaryBrowsingContexts) && (isSandboxed(SandboxPopups) || targetFrame->loader().opener() != m_frame))

Again, I do not understand, how can a frame NOT have a parent and NOT be the frame tree top?

The implementation of FrameTree::top() looks like so:
Frame& FrameTree::top() const
{
    Frame* frame = &m_thisFrame;
    for (Frame* parent = &m_thisFrame; parent; parent = parent->tree().parent())
        frame = parent;
    return *frame;
}

If frame does not have a parent, top() will return the frame itself...
Comment 22 Chris Dumez 2017-06-04 10:37:08 PDT
Comment on attachment 308278 [details]
Patch

The rest of the change looks good otherwise but I'll r- for now as I think the logic in Document::canNavigate() can and needs to be simplified.
Comment 23 Frédéric Wang (:fredw) 2017-06-08 01:59:46 PDT
Comment on attachment 308278 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=308278&action=review

@Chris: Thank you for your review, will upload a new patch soon.

>> Source/WebCore/dom/Document.cpp:2979
>> +        const char* reason = nullptr;
> 
> Let's call this failureReason, for clarity.

OK.

>> Source/WebCore/dom/Document.cpp:2980
>> +        if (!targetFrame->tree().isDescendantOf(m_frame) && targetFrame->tree().parent())
> 
> I do not understand the need for the "&& targetFrame->tree().parent()" check. We know:
> 1. targetFrame != m_frame, otherwise we would have returned early.
> 2. targetFrame is a descendant of m_frame based on of isDescendantOf() check.
> 
> Therefore, I'd believe that targetFrame has to have a parent.

As I see, the condition here is "target frame is NOT a descendant of m_frame" so we can not infer it has a parent.

>> Source/WebCore/dom/Document.cpp:2982
>> +        else if (!targetFrame->tree().parent() && targetFrame != &m_frame->tree().top() && isSandboxed(SandboxPropagatesToAuxiliaryBrowsingContexts) && (isSandboxed(SandboxPopups) || targetFrame->loader().opener() != m_frame))
> 
> Again, I do not understand, how can a frame NOT have a parent and NOT be the frame tree top?
> 
> The implementation of FrameTree::top() looks like so:
> Frame& FrameTree::top() const
> {
>     Frame* frame = &m_thisFrame;
>     for (Frame* parent = &m_thisFrame; parent; parent = parent->tree().parent())
>         frame = parent;
>     return *frame;
> }
> 
> If frame does not have a parent, top() will return the frame itself...

I believe you are right. I copied that part from Chromium, but they their "top" implementation seems a bit more complicated.
Comment 24 Frédéric Wang (:fredw) 2017-06-08 02:06:24 PDT
Created attachment 312289 [details]
Patch
Comment 25 Chris Dumez 2017-06-08 13:09:44 PDT
Comment on attachment 312289 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=312289&action=review

> Source/WebCore/dom/Document.cpp:3041
> +        const char* failureReason = nullptr;

I am trying to map this implementation to:
https://html.spec.whatwg.org/multipage/browsers.html#allowed-to-navigate

But this is not straightforward.

> Source/WebCore/dom/Document.cpp:3044
> +        else if (targetFrame != &m_frame->tree().top() && isSandboxed(SandboxPropagatesToAuxiliaryBrowsingContexts) && (isSandboxed(SandboxPopups) || targetFrame->loader().opener() != m_frame))

How do we know targetFrame is a popup here? The check says that targetFrame is not m_frame's top frame.

What if targetFrame is one of m_frame's subframes?

> Source/WebCore/dom/Document.cpp:3047
> +            if (!isSandboxed(SandboxTopNavigation))

I don't think this is possible. See this code earlier in the function:
if (!isSandboxed(SandboxTopNavigation) && targetFrame == &m_frame->tree().top())
    return true;
Comment 26 Frédéric Wang (:fredw) 2017-06-09 08:56:27 PDT
Created attachment 312437 [details]
Patch
Comment 27 Frédéric Wang (:fredw) 2017-06-09 09:07:53 PDT
(In reply to Frédéric Wang (:fredw) from comment #9)
> OK, it was not so easy as I thought. There was a subtle case to handle in
> order to pass iframe_sandbox_popups_escaping-3... I basically copied the
> logic in Chromium to handle that case (see
> https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/frame/
> Frame.cpp?q=Frame::CanNavigateWithoutFramebusting&dr=CSs&l=258).

So I think it is a bit difficult to compare the current WebKit behavior, Chromium's implementation and the specification. So let's implement the basic behavior here and move the changes to Document::canNavigate into bug 173162.
Comment 28 Frédéric Wang (:fredw) 2017-06-09 09:09:57 PDT
Comment on attachment 312289 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=312289&action=review

>> Source/WebCore/dom/Document.cpp:3044
>> +        else if (targetFrame != &m_frame->tree().top() && isSandboxed(SandboxPropagatesToAuxiliaryBrowsingContexts) && (isSandboxed(SandboxPopups) || targetFrame->loader().opener() != m_frame))
> 
> How do we know targetFrame is a popup here? The check says that targetFrame is not m_frame's top frame.
> 
> What if targetFrame is one of m_frame's subframes?

OK, I think this is incorrect. I'll follow-up on bug 173162.

>> Source/WebCore/dom/Document.cpp:3047
>> +            if (!isSandboxed(SandboxTopNavigation))
> 
> I don't think this is possible. See this code earlier in the function:
> if (!isSandboxed(SandboxTopNavigation) && targetFrame == &m_frame->tree().top())
>     return true;

Right.
Comment 29 Chris Dumez 2017-06-09 10:32:33 PDT
Comment on attachment 312437 [details]
Patch

r=me
Comment 30 WebKit Commit Bot 2017-06-09 10:59:22 PDT
Comment on attachment 312437 [details]
Patch

Clearing flags on attachment: 312437

Committed r218000: <http://trac.webkit.org/changeset/218000>
Comment 31 WebKit Commit Bot 2017-06-09 10:59:25 PDT
All reviewed patches have been landed.  Closing bug.