php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #78011 pathinfo behavior regarding extensions can lead to XSS risk
Submitted: 2019-05-14 14:12 UTC Modified: 2019-05-16 07:55 UTC
From: hanno at hboeck dot de Assigned:
Status: Not a bug Package: Filesystem function related
PHP Version: 7.3.5 OS:
Private report: No CVE-ID: None
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: hanno at hboeck dot de
New email:
PHP Version: OS:

 

 [2019-05-14 14:12 UTC] hanno at hboeck dot de
Description:
------------
With the pathinfo function a script can check a file extension. However the interpretation of "file extension" differs from common webserver behavior (tested on apache) on a file that only consists of a dot and an extension (e.g. .jpg).

This can lead to a Cross Site Scripting vulnerability. Imagine the following situation:
A web page allows users to upload files, but it only allows them to upload jpg files. To validate that the script will call pathinfo (e.g. "pathinfo($_FILES['upload']['name'], PATHINFO_EXTENSION)") and compare it to jpg and will reject the upload otherwise.
These should be safe, as a web server will serve all *.jpg files with an image/jpeg mime type. However a .jpg file will bypass that check and the webserver will deliver it (depending on its configuration) either without a mime type or with an autodetected mimetype. Both lead to XSS.

I put a small example script below that exposes this behavior.

I haven't yet found a real world example of this, but this looks like a problematic behavior that can easily lead to XSS unless a developer is very familiar with the subtleties of web servers and pathinfo. I recommend that PHP pathinfo() interprets a .jpg file as a file named ".jpg" without an extension.

Test script:
---------------
<?php
if (array_key_exists("upload", $_FILES)) {
    if (pathinfo($_FILES['upload']['name'], PATHINFO_EXTENSION) != 'jpg') {
        die("Only jpg allowed");
    }
    move_uploaded_file($_FILES['upload']['tmp_name'], $_FILES['upload']['name']);
    echo "Upload succeeded!<br>";
}
?>       
<form action="." enctype="multipart/form-data" method="POST">
<input type="file" name="upload" accept=".jpg"><br>
<input type="submit" value="Upload jpg">
</form>



Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-05-16 07:33 UTC] cmb@php.net
This behavior is explicitely noted in the manual[1]:

| If the basename of the path starts with a dot, the following
| characters are interpreted as extension, and the filename is empty
| (see third example below).

So the behavior is certainly not an oversight, but rather a
deliberate design decision, and changing it would cause a BC
break.  Maybe we should just document that more prominently?

[1] <https://www.php.net/manual/en/function.pathinfo.php>
 [2019-05-16 07:55 UTC] stas@php.net
-Status: Open +Status: Not a bug
 [2019-05-16 07:55 UTC] stas@php.net
Works as intended. 

I would suggest to reject files with empty names or configure the webserver so it'd serve them as jpeg.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 14:01:32 2024 UTC