Reputation: 23
I've created a basic page where people "sign up" to upload/delete files in their own isolated folder. The uploading/deleting of files are secure, however I would also like to allow admins to "deny" files.
The code alone works, except if anyone changes the URL to somewhere else on the server, they can "deny" any file on the system putting it at risk. I am looking to create a system function of which detects if the file they are targeting exists anywhere in the directory tree.
Here's the code that I am using of which I'd like to create a function that returns either true/false.
<?php
if(isset($_GET['deny'])) {
$tree_start = "Uploads";
$targeted_file = $_GET['deny'];
$safe_to_delete = in_directory_tree($tree_start, $targeted_file); <-- Looking for this
if( $safe_to_delete == false ) {die("This file does not exist in the directory tree");}
rename($_GET['deny'], "./Uploads/@Denied/". basename($_GET['deny']) );
}
?>
My Directory tree:
.htaccess <-- Prevent downloading of the database
admin.php <-- Problematic file browser script
index.php <-- User File management script
Users.db <-- Names and hashed passwords
Uploads:
[FILE] htaccess <-- Prevent script execution (layer 2).
[DIR] @Accepted: Notes.png, Video.mp4, etc...
[DIR] @Denied: Project.png, new_timetable.txt, etc...
[DIR] Admin: Proj1.txt, Proj1.png, etc...
[DIR] User1: Task1.txt, Task2.txt, etc...
[DIR] User2: Video1.txt, date.txt, etc...
Upvotes: 1
Views: 214
Reputation: 23
I have solved this problem by preventing path traversal instead of checking if the file exists in this folder. This code works for me (returns true only when a file in /uploads exists (and blocks going back using C:/, ../, etc), and returns true only when the file does exist. Here is the finished code:
<?php
// Code used for the deny button:
// <button onclick="location.href='?deny=SmilerRyan/Project.png';">Deny</button>
if(isset($_GET['deny'])) {
$userpath = $_GET['deny'];
$basepath = 'Uploads/';
$realBase = realpath($basepath);
$userpath = $basepath . $userpath;
$realUserPath = realpath($userpath);
if ($realUserPath === false || strpos($realUserPath, $realBase) !== 0) {
die("Invalid path - Possible Attack Blocked");
} else {
rename($userpath, "./Uploads/@Denied/" . basename($userpath) );
}
}
?>
Upvotes: 0
Reputation: 1351
give this code a try:
function in_directory_tree($dir,$file_to_search){
$filesList = new RecursiveDirectoryIterator("uploads");
$targetFile = "contact.php" ;
foreach(new RecursiveIteratorIterator($filesList) as $file)
{
$contents = explode("\\",$file);
if (in_array($targetFile, $contents))
return true;
}
return false;
}
This code will load the directory and start searching recursively, if it reaches the end without finding the file it will return false, otherwise it will return true.
I used RecursiveDirectoryIterator
as it will help us get inside directories to list them
Upvotes: 1