Reputation: 41
I'm working on an app that generates documents for users using PhpWord.
I created a "firewall.php" file to make sure that users can only see their own files.
The files are being generated fine and I have no problem viewing them from the "uploads" folder.. But when downloading them through filewall.php, they're getting messed like this:
뽐䬃Д ¤憂匎ꕩ䷱ àȀ _牥汳⼮牥汳궒셊̱ႆ 䖘笷�氯⋴⚲㻀遌眗㞙遌땽筃兴ꆬ舽컌㼟�갷ܿ䩹恙햠⡘癃벴辋㭐夰㠜㦐脣旘㑗 ᩑ쩎 顕膄沠ញ柛鏇山ꑐ☻丞ꖔ꧓ᇭ⭶ꑗ畽ꯓ伆㐓ꛚ㨃槫隠�놵❁螂�䱥㯉偎儭Ꚏ쒀揻哚璉ꠊ᧴禡헟蕸뜛ⰽ냝笊狎謎䋁醛埂ᣧ貮⽩擷姘䒧천틍▕ꚉ澟睎亻쿶鞍鹼쳦͐䬃Д ¤憂叼뉧ꂿ ;Ā d潣偲潰猯慰瀮硭沝辽櫃〔藷㺅톞죍၊遥䩋졒뚳邮涁疯遮跽⧘鶻鴟䞵䮘ꨙ勶蒍砼횢ʴຍ碿式侢쩬킙褐᪱䈖굾偝ꈈ褽ူ㝢撎ᜩ댝↘簬㖖ꚧᐌលى緯ⶼ鋽ⱏ畽隰゠ͷ蠛僼ሯ㏿죾 ῷ㔖鹖켱仞ᨮ❵矫㸩㤥ニ 䈴롪┷퀌遊뒫ﶶ︆偋̄᐀Ȁࠀꑡ艓 砋̜偿氝䗎�厈田恅싵髸ᰣ灜뎁㍊摧씿褻耭쑼㘲佼쵙ꁪ⚻辎陃ṹ饒�䏟㰞続蒇U_睩阦�ꈋ瀬㷐
This is my code for downloading the Docx file (firewall.php):
<?php
require_once( $_SERVER['DOCUMENT_ROOT'] .'/app.php');
$user_id = intval( $_SESSION['user_id']);
if( $user_id) {
$file = htmlspecialchars( $_GET['file']);
$file_arr = explode('/', $file);
$file_ext = trim( mb_substr( $file, -4), '.');
$disposition = 'inline';
if( strpos( $file_ext, 'docx') !== false) {
$file_ext = 'vnd.openxmlformats-officedocument.wordprocessingml.document';
$disposition = 'attachment';
}
// Allow only if user_id is the same as in the file's path
if( intval( $file_arr[1]) == $user_id) {
$file_path = __DIR__ ."/app/uploads/$file";
header('Content-Description: File Transfer');
header('Content-Disposition: '. $disposition .'; filename="'. $file_arr[2] .'"');
header('Content-Type: application/'. $file_ext);
header('Content-Transfer-Encoding: binary');
header('Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0');
header('Expires: 0');
header('Pragma: public');
header('Content-Length: ' . filesize( $file_path));
readfile( $file_path);
} else {
echo '403: Forbidden';
}
}
die();
$file = 'users/1/304811_Doc_02-2021_330.docx';
Content-Type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
$file_path = '/var/www/dashboard/app/users/1/304811_Doc_02-2021_330.docx';
$dispostion = 'attachment';
Upvotes: 1
Views: 312
Reputation: 41
Solved by adding ob_end_clean(); before headers
$file_path = __DIR__ ."/app/uploads/$file";
ob_end_clean();
header('Content-Description: File Transfer');
header('Content-Type: application/'. $file_ext);
header('Content-Disposition: '. $disposition .'; filename="'. $file_arr[2] .'"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: '. filesize( $file_path));
readfile( $file_path);
Upvotes: 0
Reputation: 751
I recreated your folder structure and it worked fine, downloading a valid docx of mine, but you wrote:
$file_path = '/var/www/dashboard/app/users/1/304811_Doc_02-2021_330.docx';
And the path is wrong, because after /app/ there should be /uploads/ according to your code. So I tried downloading a non-existing file and resulted in downloading two PHP warnings and the wrong encoding could cause displaying that gibberish characters.
However I remind you that your code is vulnerable to path traversal attack leading to an arbitrary file read for not properly escaping slashes in the GET variable file. PoC:
firewall.php?file=users/my_id/../../../../index.php
Upvotes: 1