W3C home > Mailing lists > Public > public-webappsec@w3.org > October 2015

Fwd: Intent To Implement: UI Security / "IronFrame"

From: Dan Kaminsky <dan@doxpara.com>
Date: Wed, 14 Oct 2015 20:00:30 -0700
Message-ID: <CAEW7ACnLwf6ztW8DcUMXt7_QR1EDqq1zviy0hW6VCKALkhR-+Q@mail.gmail.com>
To: public-webappsec@w3.org
Just posted this on the blink-dev list.  If you've got an interest in
strong viewability / anti-clickjacking you might want to track this thread.

---------- Forwarded message ----------
From: Dan Kaminsky <dan@whiteops.com>
Date: Wed, Oct 14, 2015 at 7:58 PM
Subject: Fwd: Intent To Implement: UI Security / "IronFrame"
To: Dan Kaminsky <dan@doxpara.com>



---------- Forwarded message ----------
From: Dan Kaminsky <dan@whiteops.com>
Date: Wed, Oct 14, 2015 at 7:51 PM
Subject: Intent To Implement: UI Security / "IronFrame"
To: blink-dev@chromium.org


*Contact emails*
dan@whiteops.com

*Spec*
http://www.w3.org/TR/UISecurity/
(Code at this time isn't following the spec, rather I'm working on a
mechanism to make doing something like this performant.  I/White Ops are
now W3C now. and we're working with WebAppSec on this directly.)

See also
http://www.slideshare.net/dakami/i-want-these-bugs-off-my-internet-51423044
and https://www.youtube.com/watch?v=9wx2TnaRSGs

*Summary*
Quite a few issues for the web platform come down to the fact that embedded
content can't be entirely sure what's being presented to the user.  For
example, navigations to a payment provider (for example, from a shopping
cart ot Paypal) occur in lieu of an iframe to Paypal, because the nested
iframe could be occluded with an image overlay containing other prices.
Same Origin Policy, effective as it is, does serve to prevent embedded
content from detecting this sort of manipulation, forcing user hostile
designs.

A simple fix strategy might involve pixel comparisons between a frame's
content and the top view.  This unfortunately degrades to the video parsing
problem, and is unlikely to ship on any platform.  We've been working on
Ironframe, an approach based on leveraging the compositing engine to
provide the desired security semantics without wrecking performance.
Essentially, we force the creation of a GraphicsLayer encapsulating a
"protected iframe", determine its position and size relative to the top
viewport, and reparent against the root GraphicsLayer.  Then, we send
events to the documentElement of the raised iframe, describing the
visibility of the frame.  In concert with ancestorOrigins, this provides
sufficient context for a frame to know whether it should allow a
transaction to succeed.

Is this feature supported on all six Blink platforms (Windows, Mac, Linux,
Chrome OS, Android, and Android WebView)?

Yes

*Demo*
See http://ironframe.whiteops.com.  In lieu of building against the linked
patch, you may also watch the video at https://youtu.be/c9ed399c3ik .

In general, call document.requestVisibility() or set <html
requestVisibility=1 style="transform:translateZ(0px)"> in an arbitrarily
nested iframe and try to overlay or modify content.  A handler on
document.documentElement.onerror will give you feedback on how visible the
frame thinks it is.

*Notes*

The goal is for a frame to either know it's visible, or to know that it
isn't.  (Knowing how visible is a bonus.)  As far as security is concerned,
it is OK for it to be undefined whether a given case is fully visible or
known suppressed (like from being scrolled off page), but of course there
are other engineering considerations I'm happy to listen to.  Classic
timing based clickjacking attacks, i.e. sure it's visible, but it's only
been there for a few milliseconds, are catchable by looking at event
timestamp and building a delay layer; I'm likely to build this debouncer
into some other API.

Roughly, we have four kinds of transformations that may affect a nested
iframe:  Position, Size, Occlusion, Transformation.  Position and Size are
sacred -- if we're at 50x50 and we have 100x100 pixels, that's all we get.
Learning that 50x50x100x100 is a bit tricky though; I'm basically walking
the element stack and intersecting bounding rects to figure out what my
visible region could be.

In general, this approach is surprisingly robust; I think the only thing I
had to notably special case was SVG ForeignObject (I would guess nobody in
Blink compositing is at all surprised).  Given that I'm handling everything
from parent elements shrinking on me to clip paths being applied to drop
shadows from elements that aren't even on top of me, that's pretty nice.

Multiple Ironframes are handled by having one win and the other lose.
Eventually you'll be able to unrequestVisibility().

The code is not remotely stable in Debug, because I'm doing layer surgery
out of turn.  Advice for how to properly handle the compositor and various
layer stacks would be greatly appreciated -- it's, uh, complicated in
here.  The big thing I wanted to do was a) not poll and b) not thrash.
This is as much a performance fix as it is a security fix.  With
performance (mostly) proven, stability is the next goal.

Some degree of work on the GraphicsLayer side might be nice.  It'd be nice
(and probably important) to squash all IronFrame layers into one
renderlayer, which can't happen now since iframes can't squash.  I also
wouldn't hate be able to support drop shadows over Ironframes (at the cost
of reducing the number of visible pixels) but that's hard without some sort
of layer splitting capacity.

It's a goal to make sure this effort does not block process-per-origin work
going on elsewhere, meaning the closer I can get to
DeprecatedPaintLayer/GraphicsLayer, the better.  I'm sorta mining required
values from all over the place right now.

IronFrame is designed around frame level granularity for viewability,
mostly because if you*don't* have a frame (and a cross-origin frame at
that) you can't leverage Same Origin Policy to keep anyone from knocking
out whatever security you thought you had.  It may be the case that from a
*design* perspective it's useful to know xywh of an element, even in a
foreign frame, relative to the top viewport.  There's some goop already
supporting this with visibilityOnly as an HTML attribute.  I intend to
explore this further (courtesy of Alex Russell / Position Observer work).

Ultimately, with the input exclusivity that iframes have always had, and
the output exclusivity that Ironframe brings, I want to update the address
bar to say that a user is interacting with a particular domain.  Figuring
out that UI is another ball of wax.


Received on Thursday, 15 October 2015 03:01:05 UTC

This archive was generated by hypermail 2.3.1 : Monday, 23 October 2017 14:54:15 UTC