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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
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: Sun Dec 22 01:01:30 2024 UTC