In my application, a user can load custom content such as a third-party hosted image. I have specific goals (for security):
- Load the image in a sandboxed iframe and "transfer" the image to the "host". This allows the "host" to avoid needing to define a CSP with
image-src
for each possible user-uploaded image. It also allows the browser to handle things like CSP validation for the image source and then the host can trust a data URL of the image. - Keep the "host" CSP restricted to only allow iframes from our "sandbox" iframe.
- Keep the "sandbox" CSP restricted for user-defined content. E.g. using
img-src
to only allow images from that image’s host source.
In order to "transfer" the image to the host, I need a blob/data URL/object URL. I’ve tried the following solutions:
- Use
fetch()
in the "sandbox" to make an HTTP request for the image URL and get the blob. This is treated asconnect-src
rather thanimg-src
and "opens up" security problems. - Create an
<img>
with the source of the image, dynamically create a<canvas>
, usecanvas.getContext('2d').drawImage(img, 0, 0)
(and/or related APIs), and then finally usecanvas.toBlob()
. This works great for JPEGs and PNGs, but only grabs the first frame for GIFs and doesn’t include the raw source for an SVG.
Ideally, I’m looking for an image-oriented API (so that img-src
is respected). For example:
- Telling
fetch()
to expect an image? - Using an API like
createImageBitmap()
from the<img>
but with all the data in the loaded image. - Using an API directly on
<img>
to "transfer" it to a different context (e.g. "sandbox" iframe to "host").
Is this possible?
2
Answers
I think you are looking for post message.
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
this is the way you can send data between an iframe an host.
send a "retrieve image" message to the iframe. have the iframe
fetch()
the image blob then send theresponse.blob()
back to the hostdisclaimer: this is a gist not full code, you need to put the peices in the right places and handle fetching the image, use a standard fetch and run the blob() method on the result.
I might be missing something, but here are some thoughts of mine. It feels like a situation where a backend for frontend can assist the browser-based app and enable an optimal solution that reduces security compromises.
EXAMPLE
If your app runs at
https://www.example.com
, it might process an image request like this, by calling a utility API that does the work of updating your web host with the real image:This might remove the need for iframes, and your CSP could be pretty strict:
DEPLOYMENT VARIATIONS
Logically the BFF is a separate component, though it might be deployed in a couple of ways:
SAME SITE: The browser might need to use CORS when calling the BFF if it uses a different type of deployment to the web host. Requests from the browser to the BFF might use cookie credentials.
SAME ORIGIN: Both the web host and BFF could be deployed behind a reverse proxy like nginx and use paths under the
https://www.example.com
base URL. This would further simplify the CSP.