1

We're trying to secure our upload forms in PHP regarding image uploads and PDF files. For this purpose, we're currently trying to do the following in all cases, according to our research done so far:

  1. Check MIME type
  2. Check file size
  3. Scan contents for malicious contents
  4. Upload the validated files into a directory which is non-executable and inaccessible from the web root. For the upload, we validate the filenames according to OWASP's regex recommendation.

When it comes to steps 1 and 2, built-in PHP tools like Symfony's validation package seem to be suitable. Step 4 can generally be executed in plain PHP. The problems we have are two:

  • How do you complete step 3; what is considered more secure? File scanning vs File validation? We're wondering as some posts of this forum seemingly recommend to validate file types e.g. for PDF, and also for images, instead of using malware scanner software, e.g. clamav. These command-line tools however also seem to have reported heavy vulnerabilities; so we were wondering; Should you rather go for scanning software or file conversion/validation tools, to try to reduce the risk that your uploaded files hold malicious code? Are there clearly different risk levels or is this rather an opinion-based thing?
  • Another thing is that we're also completely clueless about how we're supposed to validate base64-string uploads. Is it safe to base64_decode() potentially malicious code, to then validate the file as a stream? Or would you rather upload the decoded file into a temporary directory for scanning, or is this too risky? Our experience here is limited, as you may notice.
8
  • I'd use CDR and bypass the scanning problem altogether
    – schroeder
    May 9, 2023 at 16:28
  • Yes thanks but I meant in the sense of how you would implement such a thing in PHP / via open-source integration? We can't find anything on the net so far; except the mentioned example of ImageMagick (e.g. this). Would you consider ImageMagick as an example of CDR? Any examples that you could use for PDFs in PHP?
    – DevelJoe
    May 9, 2023 at 17:06
  • Short example of CDR to answer my question: Ghostscript to convert PDF to PostScript and Back to PDF, then store result; ImageMagick to check if an image is a valid image. Am I understanding the principles of CDR in the proper way?
    – DevelJoe
    May 9, 2023 at 18:15
  • Ghostscript sounds good, but ImageMagick is a bit overkill. I've tried the PHP Fileinfo extension with a PNG, JPEG and PDF file (all without file extensions), and it seems to work fine. Since you linked to an answer where ImageMagick is used to "scrub" the image from metadata, I would also caution that this may create a false sense of security. Even if all textual data is gone, an attacker could still embed code within the image data itself. So the real solution is to make sure the image gets interpreted as an image by validating the magic numbers and setting the file extension / Content-Type.
    – Ja1024
    May 9, 2023 at 20:37
  • Cheers, will do! And what do you think about the base64-string; how are these traditionally validated? I can literally find nothing online that does not recommend to base64_decode on the not-yet validated string and then validate the obtained stream, but that just does not feel safe to me...? Would it maybe be wiser to convert the base64 - string back to the original file and upload it as traditional file upload on the client-side? To then be able to use the same image file validation logic on the server-side?
    – DevelJoe
    May 10, 2023 at 15:33

1 Answer 1

1

I wouldn't put too much effort into trying to scan or validate the files. Malware embedded into images or PDF documents is rather specialized, so I doubt that a run-of-the-mill scanner will help much. Even if it does recognize some common malware, an attacker would likely be able to hide it without too much effort.

The most important measures are this:

  • Make a whitelist of acceptable MIME types. Do a basic check of the input format (magic numbers, valid structure etc.).
  • Be aware that complex image formats like SVG can contain scripts. Either disallow this format or remove any scripting.
  • As to PDF, remove any active content. There are several suggestions on this forum, e. g. converting the file to PostScript and back to PDF.
  • Store the file outside of the document root with a randomly generated name and a file extension that you have chosen, not the client.
  • When you serve the files, make absolutely sure that the HTTP response has the correct Content-Type, so that there's little risk of the browser treating them as anything other than images or PDF files. Also give the file an appropriate extension, so that when the user stores it somewhere, it will not be misinterpreted.

If you're particularly worried about attacks against the server, you could also delegate the handling and storing of the files to a virtual machine.

5
  • Thanks and I was focusing on these aspects too, until I read this post, mentioning: "Never go for file extensions, MIME type or naive magic numbers. File extensions can be changed trivially, even by less experienced users. MIME types can be evaded by any even moderately experienced attacker, just like magic numbers." ...?
    – DevelJoe
    May 9, 2023 at 16:54
  • And about the part in "Do a basic check of the input format (magic numbers, valid structure etc.)."; how would you do that in PHP for images and PDF? I'm rather confused as there are quite some possibilities (e.g. stuff like imagecreatefromjpeg, command-line tools like the mentioned ImageMagick, etc.
    – DevelJoe
    May 9, 2023 at 16:58
  • As to the question about file extension, MIME types etc.: It's true that you mustn't rely on the client-provided extension, MIME type and file structure. The client can of course claim anything they want about the file. My point is that when you serve the files, you must make sure that it won't be misinterpreted by a user's browser or operating system. That's when you need to provide the right Content-Type, the right extension and a file that at least structurally looks like a PDF document or image.
    – Ja1024
    May 9, 2023 at 17:09
  • Clear, but then I'm wondering about the checking of an input format etc.; how are you supposed to do this while not being able to rely on the uploaded file's type in a safe way? And same for your Content-Type header..? You mention 'client-provided extension'; if I'm correct that's the $_FILES stuff which can be manipulated as controlled by the client in PHP. But stuff like finfo_open, filesize, pathinfo(basename($path), \PATHINFO_EXTENSION) is thus considered reliable / cannot be spoofed? At least that's the stuff that symfony uses in their source; so this should be reliable?
    – DevelJoe
    May 9, 2023 at 17:18
  • You can completely ignore the client-provided Content-Type header and file extension. Take the file content and check if it either has the structure of a PDF document or of one of the image formats you accept. I'm sure there's a PHP extension which looks for magic bytes. When you you've determined the file type from the structure, you set the Content-Type and file extension yourself. Yes, the file may contain all kinds of malicious code as data. But as long as it's only ever interpreted as an image or PDF document (without active content), the code is just data and won't simply get executed.
    – Ja1024
    May 9, 2023 at 17:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .