Given the most recent PSA: https://www.drupal.org/psa-2016-003 and diving into the workings of why it happened, brought the idea up that "maybe" Drupal needs to create a secured temporary file directory within /files that is only accessible to Drupal and the webserver (not public).
With PSA-2016-003 and the public file system, "if" a file is allowed to upload by anonymous (or anybody who can upload a file really), a temporary file is created and accessible to the public. This creates an open door for anyone to access provided they have the URL. To mitigate the problem, the site builder must either convert the entire file system to private, otherwise it is left up to each individual module to provide a private upload method that can be different than the sitewide public method.
What I propose to mitigate this problem in the future, since Drupal itself is not mitigating this, is to have Drupal either create a temporary /files/tmp folder that is not accessible to the public where all tmp files are subsequently stored, or, create some sort of hash on the uploaded temporary file that is impossible for the uploading party to decipher. For the example of image.jpg, it would be uploaded and renamed to GbJvyf-image.jpg. (IMO, they should be hashed last, after they are sanitized through modules like transliteration) This method would require some obvious DB overhead within Drupal to maintain and properly deal with the hashed files, then remove the hash and rename them once they are permanently placed.
If the secured /files/tmp directory is used, I believe securing this folder across various website servers and hosting platforms might be somewhat difficult to accomplish. (apache vs nginx vs lightspeed etc).
The main idea of this is to allow anonymous and authenticated uploads by any module, but secure those temporary files until the system has decided what needs to happen with it.
Without some sort of security measure in place on this matter, it doesn't matter if the user is trusted or authenticated because they can still mitigate this security hole. Since temporary files are not logged (that I know of), there is no way for a site builder to even know that a trusted / authenticated user is out there uploading files with the sole purpose of mitigating this security hole and if cron is set to run at a longer interval, those files can potentially be indexed and accessible out in the wild for quite some time.
Comments
Comment #2
philsward commentedComment #3
philsward commentedI just tested this theory against a D7 site and D8 site and in both situations, the temporary file location provided right after upload (the link provided in the edit form) does indeed provide full unfettered access to the file for the public. This tells me that a hashed file, won't really work either unless Drupal can somehow mask the hash which makes the whole thumbnail and link a lot more difficult.
IMO, the best method is for the files to be uploaded to a secured folder first, then moved upon a form submit. Without this, websites that allow authenticated users, is still just as susceptible to this problem as a site that deals with this for anonymous with the only security block being the registration of an account on the site. Sites that require email verification have yet an additional layer of security, however we all know the bots / people employing the bots, can get around that as well...
Point is, PSA-2016-003 really only addresses this for anonymous uploads and the proposed fixes, are only mitigating the issue on a module by module basis. Core itself, is still fully susceptible to this attack, even with trusted authenticated users, despite the PSA suggesting that anonymous / untrusted is the culprit. This round it was, but what about the next time? There's still a gaping security hole that needs to be filled.
Comment #4
philsward commentedAnother thought is to have Drupal use the private file system by default and push the private file system by default. This becomes more difficult for new Drupalers to setup and point to the private file structure, however it would help mitigate this issue. If this approach is taken, two things I can think of, need to happen:
1) The private field option needs to be moved above the public for the file field with the directions added as such:
2) The switch between public and private needs to have a DB statement run that will automatically migrate the files from the public location over to the private location or vice versa, along with the links and redirects involved with them.
Unfortunately, unless the temporary file problem is secured and fixed for the public file system, pushing the private file system still won't resolve this issue for public fields created by authenticated users that explicitly need to be indexed by Google. This means the best overall route (I can see) is to use some sort of secured global tmp directory.
Comment #5
David_Rothstein commentedChanging a file URL as it's moved from temporary to permanent would get complicated because the original file URL is frequently already in use (for example, if you upload a file and then embed it in a node body before saving).
Also, this would require the private file system to be set up anyway (the temporary file URLs would need to use the private file system -- unless you want to block everyone including the original uploader from seeing them, but that would mean no image thumbnail previews, etc).
Temporary files can be audited e.g. with the File Entity module, or by using Views to create a list of temporary files on the site for administrators to monitor. But I think we should definitely have some good documentation somewhere (possibly linked via the PSA?) that gives more information about how to do that.
Comment #6
David_Rothstein commentedTo clarify, that's for Drupal 7. In Drupal 8 you can do it with core alone at /admin/content/files.
Comment #7
agileware commentedWould be great to see this fix included in Drupal 7.
Comment #8
philsward commented@David_Rothstein do you at least acknowledge that since core does not mitigate this security hole, the problem might crop up again in the future through various module?
I totally get that the chances of this scenario is quite low, but "why leave your keys in the truck just because you live in the country?"
Comment #9
David_Rothstein commentedI'm not sure I'd call it a security hole, more of a content moderation issue. If your site is configured to allow untrusted users to upload publicly-viewable files, then by definition they might upload files you don't like. So the question is how do you catch that. If they upload and then submit, you can catch it via whatever content moderation system you are using. But if they upload a temporary file (and then never submit) it is more likely to go undetected.
That's why I said above that I definitely think we should have better documentation on how to find files in that state. Maybe we should have better built-in tools also - perhaps Drupal 7 should have some kind of "Reports" page that lists temporary files that haven't been deleted yet, with the ability for an administrator to delete them manually?
Comment #11
pwolanin commentedWe should consider using a shorter value (like 0.2 * DRUPAL_MAXIMUM_TEMP_FILE_AGE ) for temp file cleanup for files owned by anonymous as a partial mitigration
Comment #12
philsward commented@pwolanin That's not a bad idea... Partial mitigation is better than none at all.
@David_Rothstein
So, what exactly constitutes an 'untrusted user'? Authenticated? Can't an authenticated user still be an untrusted user? The issue goes deeper than it being a content moderation problem.
Any Drupal website that allows an open or even verified user registration system, is at risk "if" the site has an entity with an image or file field that can be edited by the authenticated user. It narrows the playing field quite a bit, but the problem is still there. Once those sites are found, it wouldn't be hard for someone with a little knowledge to write a bot that would sit out there and authenticate with the site, then upload a file without submitting, then watch to see how long it takes to get removed. If it cleans out with cron and that's every hour, set the bot to automatically re-upload the file every 61 minutes and now the file becomes persistent on the site, "hidden" from the site maintainer. You'd never know it was happening unless you were looking for it.
Comment #13
David_Rothstein commented@philsward, I think an untrusted user is anyone you don't trust :) Obviously that can include authenticated users in addition to anonymous (it depends on the site).
If your site allows people you don't trust to upload publicly-viewable files, then by definition they might upload things you don't like. So you have to keep an eye on it. This is obviously not limited to temporary files. That is why I think the primary problem with temporary files is simply that Drupal doesn't make it easy for site administrators to monitor them (and that they tend to slip through other content moderation solutions that are already in place on the site). So improving that would be good. But other solutions certainly might help also.
Comment #14
philsward commented@David_Rothstein curious, how did the latest Core release dealt with this on the private file side of an anonymous user uploading to the private file system? I honestly haven't looked into the problem or the fix, but when I ran across the release notes, it sounded very similar to what I describe here. Is it similar or completely different?
On a side note, I don't see the need to make it easier for administrators to sift through temporary files when it "could" be mitigated altogether through some sort of scrubbing process performed at upload. Fix it once and for all and forget about it.
Comment #15
David_Rothstein commentedYes, the issue fixed in the latest core release is similar. It deals with it by storing information in the session to identify the particular anonymous user who uploaded the file. Then it denies access to the file to everyone who doesn't have that data in the session.
The reason it can do that, though, is because the private file system can deny access - unlike the public file system. So to do this for public files, you'd have to store them in a private location first and then move them later after upload (as you have suggested in this issue), but that's what would get complicated to actually do.
Comment #16
philsward commented@David_Rothstein Thanks for that explanation :-). I had a feeling it was similar, but your statement makes sense on why it isn't easy for public...
Still, something to keep in mind though.
Comment #22
geek-merlinWhen i tested this scenario (upload a file in a node, but do not submit the node) on D8, my file landed in the private filesystem (which i configured the field to use!) after uploading. (With temporary status, so it should get garbage collected soon. EDIT: it is.) So it looks to me like this is a misunderstanding: After uploading, if configured so, the file sits in private storage with temporary status, not in tmp folder.
Or do i miss something?
Comment #23
geek-merlinPostponing on clarification of #22.
Comment #24
geek-merlinMeanwhile i worked on the related issue of uploaded and submitted files.
Comment #25
philsward commented@geek.merlin I think the part you're missing is if the system is set to public files. Private files mitigates this by design.
If the site is set to use public files and a user uploads a file, that file sits out in the uploaded location until it gets scrubbed.
If a fresh install of D8 is used with public files and all uploaded files get dumped at ../files, a bot could automatically login to a site and re-upload a malicious file for distribution every few hours. If the upload path never changes, then it would be pretty easy to have a bad file getting perpetually uploaded to the server with no-one even knowing how or why it's happening since a node isn't being saved to track it.
What started this whole thing was someone uploading PDFs through webform that had phishing links in them. These PDFs were then getting indexed in Google and showing up all over the place. You simply uploaded a file within the webform, copy the link that was graciously provided by drupal and close the page. This created the mad-dash for webform to move all attachments to private instead of actually addressing the problem itself.
Same thing can happen with any drupal site where a user has access to an upload form and the filesystem is set to public. I just tested it on D8 and found that a file is still accessible after uploading it without submitting the node: 8.7.7
Why would someone want files set to public? For indexing. PDFs get indexed by google. Images get indexed by google. TXT files get indexed by google. I have manuals, safety data sheets and brochures on one of my sites which all need to be in the public, index-able space. Moving these to private makes it more difficult for indexing purposes, thus I have less content for exposure.
Clear as mud?? ;-)
Comment #26
philsward commentedJust tested this here on d.o, which uses public files and allows anyone to create an account.
I uploaded a test.txt file: https://www.drupal.org/files/issues/2019-11-13/test.txt
I forgot to check the link, so I uploaded it a second time: https://www.drupal.org/files/issues/2019-11-13/test_0.txt
Never saved the form, yet those files will be publicly accessible until cron scrubs them.
In otherwords, "anyone" can signup on D.O and upload a file without saving it to an issue. Create a bot to do it automatically, records the link, then turns around and posts that link out on a blackhat file sharing website. Bot then checks the link to see if it's dead or alive and if dead, do it all over. Free file hosting upto 50MB on this smorgasboard of extension types! jpg jpeg gif png txt xls pdf ppt pps odt fodt ods fods odp fodp gz tgz patch diff zip test info po pot psd yml mov mp4 avi mkv
The likely hood of this happening are obviously small, but still a security hole and I have now literally written a public manual for someone to take advantage of it.
Update: More than 12 hours later and the files still haven't been scrubbed...
Comment #27
geek-merlin@philsward: The referenced issue is about uploading all files to private storage and only after their node is reviewed and published, file is also moved to public storage.
Comment #28
philsward commented@geek.merlin I would be fine with that, the point of this issue is to mitigate the gaping security hole that is currently wide open.
I just checked and those files I uploaded the other day to this issue and never saved, are still sitting out there...
I think we're on the same page with moving the upload system to a more secured setup all-around. How it happens, I don't really care, just that someone finally takes it serious enough to fix it.
I like the idea of sending the upload to a private area and then moving it from there. One thing to consider is maybe tying the view-able permission to the upload session, meaning the only person who can see the uploaded file before save, is the person who uploaded it. The way Drupal currently works, is to show the link to the file after upload or in the case of an image, the stylized image itself.
"How do we create an upload system that is still user friendly for the up-loader, but still secure enough to be inaccessible from everyone else?"
Then, there's the issue of whether cookies are enabled or not in order to track the session.
Also, how hard would it be to spoof a session? (rhetorical, something to consider)
Comment #29
geek-merlin> One thing to consider is maybe tying the view-able permission to the upload session, meaning the only person who can see the uploaded file before save, is the person who uploaded it.
This is what Drupal currently does for private uploads of anonymous users. See file access handler code.
Comment #30
philsward commentedComment Removed. Should have read previous comment more closely.
Comment #38
smustgrave commentedClosing as outdated after so many years. If still a valid feature request please reopen updating issue summary for D10 and up
Thanks.!