You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I just wanted to share what I learned about IMG tag security in browsers while building the user profile feature: #1923
So in this feature we wanted to allow users to upload images of themselves and for other users to be able to display those images.
My initial idea for how to implement this was to allow a user to upload an image, convert it to base64 as a data URL and then save it in the backend. When other users replicate that image, they could display it in an IMG tag using the src property directly. That sounds wonderful, but are there any tricky aspects to consider around validation?
I did quite a bit of searching on the internet, but couldn't find much around image validation.
One aspect that seemed clear was that I wanted to validate image size. So on the backend the base64 encoded image data is checked to ensure it is less than a certain size. Still there are some open questions, like how does compression fit in with all this? Can someone send a highly compressed image that passes these backend checks but then inflates and takes up a lot of memory when displayed in the browser?
I also wondered what type of images should be allowed. Can an SVG include scripts? Does the browser filter those out properly? To keep things simple to start, I decided to limit images to PNG and check the magic byte sequence at the beginning of the image data.
So what about XSS or CSRF? Since we are passing a URL to the src tag, it could make a get request to an external server and I think potentially perform a CSRF. We don't want that happening, so for now we can disallow regular URLs like www.this.example.
But what about the data URLs? These can be quite tricky too since, for example, this is a valid data URL: data:text/html,%3Cscript%3Ealert%28%27hi%27%29%3B%3C%2Fscript%3E "An HTML document with <script>alert('hi');</script> that executes a JavaScript alert. Note that the closing script tag is required." (https://developer.mozilla.org/en-US/docs/web/http/basics_of_http/data_urls) I'm not sure that data URL will do anything in an IMG src tag because it's an image tag, but still we don't want to allow URLs like that.
Eventually I settled on validating that the data URL prefix matches data:image/png;base64,. I think as long as we use a data URL like data:image/png;base64,<data> and the actual data resembles PNG image data (at least has the magic byte sequence), then for now I trust the browser to properly handle displaying that image and performing the image validation it needs to display the image safely. And if there is anything important that we are missing, then hopefully we can learn about that and improve it.
I also briefly browsed through Signal's codebase and didn't find anything notable in terms of validating images.
So to sum it up, users pass a data URL with base64 encoded image data to the backend. The backend then checks that the data URL starts with data:image/png;base64, and that <data> is less than a specific size and starts with the magic byte sequence of PNG images.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
I just wanted to share what I learned about IMG tag security in browsers while building the user profile feature: #1923
So in this feature we wanted to allow users to upload images of themselves and for other users to be able to display those images.
My initial idea for how to implement this was to allow a user to upload an image, convert it to base64 as a data URL and then save it in the backend. When other users replicate that image, they could display it in an IMG tag using the
src
property directly. That sounds wonderful, but are there any tricky aspects to consider around validation?I did quite a bit of searching on the internet, but couldn't find much around image validation.
One aspect that seemed clear was that I wanted to validate image size. So on the backend the base64 encoded image data is checked to ensure it is less than a certain size. Still there are some open questions, like how does compression fit in with all this? Can someone send a highly compressed image that passes these backend checks but then inflates and takes up a lot of memory when displayed in the browser?
I also wondered what type of images should be allowed. Can an SVG include scripts? Does the browser filter those out properly? To keep things simple to start, I decided to limit images to PNG and check the magic byte sequence at the beginning of the image data.
So what about XSS or CSRF? Since we are passing a URL to the
src
tag, it could make a get request to an external server and I think potentially perform a CSRF. We don't want that happening, so for now we can disallow regular URLs likewww.this.example
.But what about the data URLs? These can be quite tricky too since, for example, this is a valid data URL:
data:text/html,%3Cscript%3Ealert%28%27hi%27%29%3B%3C%2Fscript%3E
"An HTML document with <script>alert('hi');</script> that executes a JavaScript alert. Note that the closing script tag is required." (https://developer.mozilla.org/en-US/docs/web/http/basics_of_http/data_urls) I'm not sure that data URL will do anything in an IMGsrc
tag because it's an image tag, but still we don't want to allow URLs like that.Eventually I settled on validating that the data URL prefix matches
data:image/png;base64,
. I think as long as we use a data URL likedata:image/png;base64,<data>
and the actual data resembles PNG image data (at least has the magic byte sequence), then for now I trust the browser to properly handle displaying that image and performing the image validation it needs to display the image safely. And if there is anything important that we are missing, then hopefully we can learn about that and improve it.I also briefly browsed through Signal's codebase and didn't find anything notable in terms of validating images.
So to sum it up, users pass a data URL with base64 encoded image data to the backend. The backend then checks that the data URL starts with
data:image/png;base64,
and that<data>
is less than a specific size and starts with the magic byte sequence of PNG images.Beta Was this translation helpful? Give feedback.
All reactions