Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can $_FILES[...]['size'] be forged?

There's a well-known caveat about not trusting the MIME type sent via file upload in PHP ($_FILES[...]['type']) as this is sent by the HTTP client and could therefore be forged.

There's a similar caveat for the file name ($_FILES[...]['name']), which is sent by the HTTP client and could contain potentially dangerous characters.

However, I can't see how the file size ($_FILES[...]['size']) could be forged, as it does not seem to be part of the request payload, at least I can't see it in the dev tools in Chrome, where the payload looks like:

------WebKitFormBoundarytYAQ3ap4cmAB46Ek
Content-Disposition: form-data; name="picture"; filename="picture.jpg"
Content-Type: image/jpeg

Original file name and MIME type are here as expected, but no sign of a size parameter.

Still, I've just stumbled upon Symfony's UploadedFile implementation, that considers the file size as client-originated and therefore not trustable:

UploadedFile::getClientSize()

Returns the file size. It is extracted from the request from which the file has been uploaded. Then is should not be considered as a safe value.

Can the file size be part of the request payload, and therefore be forged, or is it always inferred from the actual file pointed to by $_FILES[...]['tmp_name'], and therefore always trustable?

like image 755
BenMorel Avatar asked Jul 03 '14 23:07

BenMorel


2 Answers

As suggested by @Dagon in the comments, I checked the PHP source in rfc1867.c.

The lines involved in defining the [size] attribute are:

[1042] wlen = write(fd, buff, blen);
       ...
[1056] total_bytes += wlen;
       ....
[1242] ZVAL_LONG(&file_size, total_bytes);
       ...
[1270] snprintf(lbuf, llen, "%s[size]", param);
       ...
[1275] register_http_post_files_variable_ex(lbuf, &file_size, ...

Which I translate as:

  • 1042 The temp file is written in wlen size chunks
  • 1056 In each iteration, wlen is added to total_bytes
  • 1242 total_bytes is assigned to the file_size zval
  • 1270 The target variable name ...[size] is assigned to lbuf
  • 1275 file_size is registered under the name contained in lbuf, ...[size]

So without doubt, the only variable ever assigned to $_FILES[...]['size'] is the actual number of bytes written to the temporary file whose path is assigned to $_FILES[...]['tmp_name'].

As far as I can see, there is no way to forge the size attribute.

like image 90
BenMorel Avatar answered Sep 21 '22 09:09

BenMorel


See BenMorel's answer, the short answer is, no! the size can not be forged.

The only part not to trust is the size and image type provided by the browser

<input type="hidden" name="MAX_FILE_SIZE" value="30000" />
<input type="file" name="pictures" accept="image/png"/>

In both cases, it's possible to spoof the browser, so you must implement a backend solution to further validate the size and image type.

Also thanks to @Andy Gee in the comment section for improving this answer

$_FILES[i]['type'] is sent by the client so should not be trusted. use mime_content_type($_FILES[i]['tmp_name']) to make sure.

Or use my shameless plug: https://github.com/samayo/bulletproof

like image 35
samayo Avatar answered Sep 20 '22 09:09

samayo