Posílání souborů pod správným názvem

Školení, která pořádám

Pokud chceme některé soubory ke stažení zpřístupnit jen vybraným uživatelům, je vhodné je učinit přímo přes web nepřístupné (v Apache např. umístěním mimo DocumentRoot nebo do adresáře s omezeným přístupem). Pokud to neuděláme, vystavujeme se riziku, že cestu k souboru uživatel uhodne nebo jinak zjistí a stáhne si soubor bez ověření. Po ověření práv je tedy nejlepší soubor uživateli poslat přímo PHP skriptem. Tomu se nevyhneme ani v případě, kdy je soubor uložen v databázi a přímá cesta k němu neexistuje.

Aby prohlížeč věděl, co si se souborem má počít, musíme ho samozřejmě poslat se správným Content-Type. Pokud soubor chceme navíc poslat pod správným názvem, už to tak jednoduché bohužel není. Hlavička Content-Disposition, pomocí které se název souboru dá předat, je sice široce implementovaná, v HTTP ale přímo není a některé proxy ji opravdu ignorují. Pokud tedy nechceme uživateli např. spustitelný soubor posílat pod názvem stahnout.php, nelze se na tuto hlavičku spolehnout.

<?php
// zde ověřit, jestli má uživatel k souboru $_GET["filename"] právo
header("Content-Type: application/octet-stream");
// při stažení přes některé proxy servery se název souboru nepředá
header("Content-Disposition: attachment; filename=\"$_GET[filename]\"");
readfile("data/$_GET[filename]");
?>

Řešit se to dá tak, že název souboru uvedeme jako poslední část URL. V Apache je možné zapnout direktivu AcceptPathInfo a použít cestu stahnout.php/aplikace.exe.

<?php
$filename = substr($_SERVER["PHP_SELF"], strlen($_SERVER["SCRIPT_NAME"]) + 1);
// zde ověřit, jestli má uživatel k souboru $filename právo
header("Content-Type: application/octet-stream");
readfile("data/$filename");
?>

Tento problém lze pochopitelně řešit i pomocí mod_rewrite například pravidlem RewriteRule ^/data/(.*) /stahnout.php?filename=$1.

Jakub Vrána, Dobře míněné rady, 11.7.2005, diskuse: 6 (nové: 0)

Diskuse

ikona dgx:

To je vskutku fikané řešení :-)

Jen by mě zajímalo, jak časté jsou proxy, co Content-Disposition nepodporují. Jestli je to jedna z tisíce nebo třeba každá desátá.

ikona Jakub Vrána OpenID:

Poměr špatných bohužel neznám, každopádně jsem se s tím už setkal a musel jsem to řešit.

pck:

Dobry Den!

V celom mojom portali pouzivam kodovanie UTF-8. Nazvy suborov mam ulozenu v databaze tiez kodovane s UTF-8. Pri stiahnuti suborov s hore spomenutou metodou dostanem necitatelne znaky v mene suborov. Vedeli by ste mi pomoct?

ikona MiSHAK.wz.cz:

teoreticky pri zacatku spojeni nastavit kodovani komunikace na iso-8859-1 je to nejaka fce mysql staci pohledat

Mirek Renda:

Dobrý den,

z aplikace posíláme obdobným způsobem PDF. Na majoritě počítačů to v pohodě přijde do prohlížeče, vyhodí systémový dialog otevřít/uložit atd.

Na pár počítačích to otevře prázdné okno IE, které zůstane viset (nejde zavřít, nutné shodit IE nebo restartovat).

Nedaří se mi příjít, kde je problém.

Matně si pamatuji, že jsme kdysi měli problém s pořadím jednotlivých hlaviček, ale už si nevybavím příčiny ani důsledky.

Neřešil jste to někdo? Dím moc. MR

ikona Jakub Vrána OpenID:

Podívejte se, jaké hlavičky vrací bezproblémové stažení souboru (bez PHP wrapperu) a pošlete ty stejné. Pokud navíc URL bude vypadat jako skutečný soubor se správnou koncovkou (zajistitelné např. pomocí mod_rewrite), tak to prostě musí fungovat.

Pokud se vám podaří dohledat příčinu chyby, tak ji sem prosím napište.

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.