Let me begin by stating that what I'm covering today isn't actually new. It's stuff I've covered here before. But after a conversation with a reader via email I had to write up a quick test to confirm it myself. I don't believe this is a security issue, but I was kinda surprised and therefore I figured it was best to whip up a quick blog post.
Let's begin with some basics. I assume you know that JavaScript running in the browser does not have access to your file system. That's a really, really good thing. Chrome used to support a file system API (and it may still support it, but it's definitely deprecated) that gave you access to a sandboxed file system, but it certainly was not allowed to touch the user's main file system. Now that binary support in IndexedDB is well supported, there isn't really a need for writing files to the disk.
However, JavaScript can read files that the user selects via an file type input field. You can see a simple demo of this below:
See the Pen File Read demo by Raymond Camden (@cfjedimaster) on CodePen.
Be sure to select a text file only, but you can also read binary data too. (The code would just need to adjust for it.) Also, I apologize for not using Vue. I feel bad. ;)
So here is where the interesting little tidbit came up. In one of my earlier demos, I showed selecting images and getting previews. It also supported multiple selections. So you could pick one image. Then pick another. And so on.
What that demo showed, and what didn't really click with me, is that once a user selects a file, you have read access to it, even after they select another file. As I said, I can see why that works, and it isn't a security issue per se. I mean, the user did select the file. But it kinda surprised me that after I cleared my selection, I could still read it. This CodePen demonstrates this, a bit poorly (I'll explain why in a second):
See the Pen Testing multi file upload by Raymond Camden (@cfjedimaster) on CodePen.
This demo lets you pick a file, then some more, then more (etc.), and finally upload them all to Postman. Postman doesn't seem to handle the result very well, but from what I can see in DevTools, all files are definitely being uploaded.
I guess that's all I have to say about it. Is anyone else surprised or is it just me?
Header photo by Kiwihug on Unsplash
Archived Comments
Luckily, while the web app does have access to the first-selected file permanently (i.e., even after the user selects a different file or clears the file input), that file is only a copy of the original file on the user’s disk.
For example, if you select an image via a file input and then edit the image locally, the web app will not have access to that updated image, but only to the version of the image at the time when you selected it in the web app (I checked [1]).
Update: Wait a second, In Chrome it actually does. What?! The web app has access to the latest version of the file on the user’s disk, regardless of the value of the file input. This is not good!
[1]: https://codepen.io/simevida...
What happens if I select foo.gif, edit it locally, then select it again the file input? (Although as we discussed before, it won't fire a change event, so pretend we selected goo.gif on the second time, and foo on the third).
Sorry - didn't see your update. But if FF does what you say, then I'd be interested in knowing.
When I select the same file repeatedly in Firefox (Nightly), the change event fires and I get the latest (edited) version. In Chrome, if I select a different file and then the first file again, I also get the edited version.
Cool. If you store a variable for each instance (like I did in my CodePen), are they all pointers to the *final* version or do they stay as they were when read?
From my testing, in Firefox the file reference points to a copy of the file (in Firefox’s memory, I presume) which was created at the moment the user selected it (so, it’s *not* the edited version), whereas in Chrome the reference points directly the the file on the user’s disk (via a proxy object, I guess), since you can access the latest version of the file at any time.
Thank you for investigating this.
Thinking randomly here. What if the front end passed the variable to a service worker. Could then the browser "watch" the file even when you aren't on the page?
I haven’t started testing with service workers yet, but I’m going to assume yes :) (
postMessage
supportsFile
objects—the passed file would be a deep clone, but it should still be linked to the same file on the user’s disk).