User:Brooke Vibber/Secure code modules through iframe sandboxing
The HTML iframe element provides for embedding one web application inside another, with various degrees of sandboxing available through cross-origin protections and the sandbox attribute. The parent application and the child frame application can communicate with each other asynchronously by sending strings or JSON objects over the postMessage interface.
Prior art
edit- Extension:SVGEdit - uses an iframe to embed a web application
- sending data in/out over postMessage
- iframe works remotely-hosted, in which case XSS worries in the app don't directly expose the MediaWiki site
- Extension:EmbedScript - experiments for in-content JS widgets
- sending code in over postMessage
- Wikimedia Hackathon 2016/Showcase#Security-isolated JavaScript widgets - updated experiments for in-content JS widgets with cleaner API modeling, with an eye towards an extensible messaging API and eventual support for reader & editor plugins
- planning to adapt Commons:WikiProject WikiWidgets scripts
- thinking about adapting Extension:Widgets
State of the sandbox
edit- Using an external domain to enforce cross-origin barrier works in every browser
- Requires a domain to coordinate with.
- Either host data blobs on that site, or use a shim that allows postMessage'ing in some code and resources to use. (as EmbedScript extension did)
- postMessage sends JSON objects via structured-clone on most/all browsers -- test me!
- (some old ones may still be stuck on strings, requiring JSON.stringify/parse)
- iframe sandbox attribute is pretty well supported as of April 2016
- Allows creating an iframe without a separately-hosted domain, and restricting its scripting access.
- sandbox='scripting' (without adding 'cross-origin') should allow JS to run while still isolating it from access to the parent frame JS world, or the site's cookies, localStorage, indexedDB, etc.
- iframe srcdoc attribute is slightly less well supported, still missing on Edge
- can be used to provide HTML/JS source to load into the iframe, without a separate document that could be loaded outside a frame
- if we have a shim .html, it can allow HTML/JS to be injected from the parent frame over postMessage, as done by EmbedScript
- Biggest "hole" in the sandbox is web bugs
- HTML and JS in a frame can trigger remote network requests through <img>, <script>, <link>, XMLHTTPRequest, etc etc.
- This exposes user IP address to off-site servers (privacy issue definitely, security issue maybe)
- Runs risk that user-written code may pull in off-site resources deliberately or as a shortcut (sustainability, copyright issues; further security/privacy complications if remote code is loaded)
Content widget thoughts
edit"Content widgets" such as the interactive 'Game of Life' widget on W:es:Juego de la vida are an obvious application, as isolation from the main MediaWiki web page security context is pretty much a prerequisite for getting these made and distributed widely without a HUGE security review bottleneck.
There are two major technical issues:
- "web bug" problem exposing users' privacy when loading offsite resources
- temptation to use offsite resources (copyright & sustainability issues)
These are hard to solve technically -- recompiling JavaScript code to use a secure DOM proxy is conceivable, but likely to be very slow. (See the old caja JS sandbox project.)
A good UX for creating, reviewing, approving, and using widgets would go a long way:
- jsfiddle-like UI for editing/running in isolation?
- code-friendly revision history/diff view & commenting UI
- use ContentHandler for custom storage & views
- store HTML+JS+CSS in a single blob versioned together?
- a good 'incoming review' queue
- static analysis to check for common problems?
- detect direct references to remote resources and highlight them
- workable versioning / release blessing
- a widget that's in use on articles shouldn't stop working because it's been updated and not yet reviewed!
- allow a user to preview their own code widgets at least on test pages
- allow other users to see those previews, with a security opt-in?
- maybe consider local code to be edited and 'published' code versions that get reviewed and can be used in main content
- trigger updates to the new version after a release review
- a good searchable "catalog" of published+reviewed widgets, that can be searched and widgets set up from within both visual and source editors
- probably need to be able to have shareable code libraries -- common graphing libraries, jQuery, etc could be reused frequently
UI plugin thoughts
editThere are two main targets for iframe-isolated UI plugins:
- Defense in depth for MediaWiki extension code installed by site operators
- especially when pulling in a large HTML/JS code base from another project like an SVG editor or WebGL 3d model renderer!
- avoid polluting main JS context with someone else's scary globals
- allow large JS modules to be unloaded when an extension dialog closes
- Safer way to write and share user-written code like today's user scripts and gadgets
- would require designing suitable async hook APIs, and some kind of registration system
- most of the concerns for content widgets above apply here as well (security/privacy, editing/review)
- a UI plugin may have more legit reasons to access an external site, such as integrating a service on Tool Labs with a good on-wiki UI
Extension code can roll its own loader & postMessage API, but having common infrastructure would be nice.
Security isolation for user-written UI plugins could make it easier to safely share tools similar to today's user scripts and gadgets:
- Page editor plugin could register a toolbar button that activates a dialog, then allows async access to the editor's text or DOM contents and selection state. Could implement custom search-replace, wikification, a fancy new citation formatting wizard, etc.
- Uploaded file editor plugin could register a custom editor action on File: pages, to implement vector or raster editing, client-side OCR, passing data into server-side OCR, etc.
- Fancier interaction requirements -- such as adding hover handlers to links -- could be mediated through install-time manifest, permission dialogs, ...?
Narrower APIs in the isolated frame have downsides and upsides:
- bad: porting existing code may have to make significant changes, and may have to add more APIs to support stuff over time
- good: future-proof - code is unable to dig into internals of HTML structure and JS API surface that weren't meant to be relied upon
- good: device portability - more likely to be able to use same plugin APIs on desktop, mobile, and even in native apps (via a web view)
It could also be very useful to provide 'blessed' libraries for the child frame to use on demand:
- jQuery, subset of mediaWiki.utils, etc
- ability to pull code libraries from other plugins?