Omezení oprávnění Apache na Windows 10 pro C:/www/, direktiva open_basedir – ano/ne?
- m.brecher
- Generous Backer | 871
Ahoj,
chtěl bych zabezpečit PC kde mám vývojový Apache server s PHP + Nette proti smazání disku PC v případě chyby v parametru $filepath v metodě Nette\Utils\FileSystem::delete($filepath).
Na vývojovém PC mám Windows 10.
Webové projekty mám v:
C:/www/
PHP 8.0 nainstalováno jako modul Apache, pache běží jako služba Windows pod defaltním účtem Local System, httpd.conf:
LoadModule php_module "C:\Program Files\PHP8\php8apache2_4.dll"
PHPIniDir "C:\Program Files\PHP8"
DocumentRoot "C:\www"
php.ini:
; open_basedir
upload_tmp_dir = "C:\www\_upload_tmp_file"
session.save_path = "C:\www\_session_tmp"
Řešení pomocí konfigurační direktivy PHP open_basedir jsem kdysi zkoušel, ale narazil jsem na problémy s blokací composeru, funkce PHP is_file() vyhazovala warningy a ani odborné servery tuto cestu moc nedoporučují.
V dokumentaci Apache https://httpd.apache.org/…windows.html je doporučováno pro běh Apache jako služby Windows vytvořit jiný účet než Local System.
Se svými omezenými znalostmi v této problematice odhaduji že:
Omezení oprávnění k souborovým operacím pro účet pod kterým se Apache spouští předpokládám že stejným způsobem omezí možnosti PHP, protože PHP je spuštěno jako modul Apache. Kdyby se tento účet podařilo vytvořit (ve Windows to asi nebude snadné), byl by disk PC proti nechtěnému smazání ochráněn a PHP by mohlo smazat pouze všechny PHP/Nette projekty v C:/www/.
Když by se pomocí ini_set(‚open_basedir‘, $appDir.‚/‘) omezily oprávnění konkrétní aplikace na složku příslušného projektu, nevznikne tam nějaký problém s update projektu pomocí composeru, popř. s Nette Frameworkem (také používá PHP funkci is_file(), která může díky open_basedir vyhazovat warning). Když to promýšlím, může se tam narazit na řadu problémů. V nejhorším bych na open_basedir rezignoval a alespoň bych ochránil zbytek disku mimo C:/www/, nedávno jsem si totiž pomocí Nette\Utils\FileSystem málem smazal disk.
Nemá někdo hlubší praktické zkušenosti v této oblasti a mohl by poradit?
Díky předem
Editoval m.brecher (14. 9. 2022 3:51)
- m.brecher
- Generous Backer | 871
Ahoj,
tak jsem trochu experimentoval a zkusil jsem v Nette projektu dynamicky nastavit open_basedir v Bootstrap.php:
class Bootstrap
{
public static function boot(): Configurator
{
$configurator = new Configurator;
$appDir = dirname(__DIR__);
...........
ini_set('open_basedir', implode(';',[$appDir.'/', $appDir.'/_upload_tmp_file/']));
return $configurator;
}
Aplikace fungovala, PHP má skutečně zamezen přístup k souborům mimo C:/www/, kvůli uploadování souborů jsem povolil ještě práva k souborům pro adresář C:/www/_upload_tmp_file/.
Takže by to možná takto mohlo fungovat. Funkce PHP is_file() (použité v mém kódu) sice vyhazuje warningy, ale v production módu to nevadí, jenom se zbytečně logují. Ale s tím se dá žít. Nette warningy z is_file() nevyhazuje, takže dobrý.
Co mne překvapilo dosti, že ačkoliv jsem v open_basedir nepovolil složku pro soubory session, která je mimo C:/www/ (C:/www/_session_tmp/), přihlašování fungovalo a aplikace bez problémů soubory session zapisovala. Usuzuji, že to má Nette pečlivě ošetřeno a samo si práva pro C:/www/_session_tmp/ nastaví.
Ještě mě napadá, že dát inicializaci open_basedir do Bootstrap.php asi není úplně Best practice a nejlepší bude to umístit do BasePresenter::startup().
Editoval m.brecher (13. 9. 2022 4:11)
- Pepino
- Člen | 257
Nebude to nějakým jiným nastavením? Zkusil jsem hodit
open_basedir = c:/www/
přímo do php.ini a žádná chyba (php 8).
Nemám potlačené výpisy chyb.
Jinak co se týče umístění ini_set
, tak to dej přímo do
index.php
ještě před načítání čehokoliv. V
bootu
nebo startupu
už je pozdě.
- Marek Bartoš
- Nette Blogger | 1274
Zda to bude na začátku bootstrapu (před inicializací containeru a debuggeru) nebo v indexu je jedno, volají se ihned po sobě.
I v indexu může být, v závislosti na nastavení php (kupříkladu když má php nastavené session auto start a je aktivní session) pozdě. open_basedir je vhodné mít v php.ini, ještě před startem aplikace.
- m.brecher
- Generous Backer | 871
Ahoj,
tak jsem dál testoval zda jak v reálných podmínkách na domácím pc s Windows použít open_basedir pro omezení práv PHP v jednom projektu právě na adresář tohoto projektu.
@MarekBartoš a @Pepino v diskusi radili použít open_basedir v php.ini. To vypadalo zajímavě – omezit všechny projekty na DocumentRoot celého serveru C:/www a uvnitř každého projektu dynamicky v index.php nebo Bootstrap.php dále omezit na podadresář projektu C:/www/path-to-project. PHP skripty by nemohly omylem smazat soubory v jiných projektech ani na zbytku PC a kdyby se v projektu na přidání kódu zapomnělo, byla by alespoň ochrana celého disku PC.
Toto by fungovalo bezvadně, kdybych nenarazil na problém, že pokud se nastaví open_basedir v php.ini, tak to omezí funkci composeru. Testoval jsem pouze composer show a s tímto výsledkem:
php.ini:
open_basedir = ‚C:/www‘
C:\www\dev\playweb>composer show
PHP Warning: Unknown: open_basedir restriction in effect. File(C:\ProgramData\ComposerSetup\bin\composer.phar) is not within the allowed path(s): (C:\www) in Unknown on line 0
PHP Warning: Phar::mapPhar(): open_basedir restriction in effect. File(C:\ProgramData\ComposerSetup\bin\composer.phar) is not within the allowed path(s): (C:\www) in C:\ProgramData\ComposerSetup\bin\composer.phar on line 28
PHP Warning: require(phar://composer.phar/bin/composer): Failed to open stream: phar error: invalid url or non-existent phar "phar://composer.phar/bin/composer" in C:\ProgramData\ComposerSetup\bin\composer.phar on line 29
PHP Fatal error: Uncaught Error: Failed opening required 'phar://composer.phar/bin/composer' (include_path='.;C:\php\pear') in C:\ProgramData\ComposerSetup\bin\composer.phar:29
Stack trace:
#0 {main}
thrown in C:\ProgramData\ComposerSetup\bin\composer.phar on line 29
Tak povolíme také C:\ProgramData\ComposerSetup:
php.ini:
open_basedir = ‚C:/www;C:\ProgramData\ComposerSetup‘
C:\www\dev\playweb>composer show
PHP temp directory (C:\Users\milos\AppData\Local\Temp) does not exist or is not writable to Composer. Set sys_temp_dir in your php.ini
In JsonFile.php line 85:
is_file(): open_basedir restriction in effect. File(C:/Users/milos/AppData/Roaming/Composer/config.json) is not within the allowed path(s): (C:\www;C:\ProgramData\ComposerSetup)
Dále povolíme C:\Users\milos\AppData\Local\Temp a C:/Users/milos/AppData/Roaming/Composer:
php.ini:
open_basedir =
‚C:/www;C:\ProgramData\ComposerSetup;C:\Users\milos\AppData\Local\Temp;C:/Users/milos/AppData/Roaming/Composer‘
C:\www\dev\playweb>composer show
In Factory.php line 200:
file_exists(): open_basedir restriction in effect. File(C:/Users/milos/AppData/Local/Composer/.htaccess) is not within the allowed path(s): (C:\www;C:\ProgramData\ComposerSetup
;C:\Users\milos\AppData\Local\Temp;C:/Users/milos/AppData/Roaming/Composer)
Přidáme tedy ještě C:/Users/milos/AppData/Local/Composer:
php.ini:
open_basedir =
‚C:/www;C:\ProgramData\ComposerSetup;C:\Users\milos\AppData\Local\Temp;C:/Users/milos/AppData/Roaming/Composer;C:/Users/milos/AppData/Local/Composer‘
C:\www\dev\playweb>composer show
latte/latte v2.11.5
.....
Takže composer show se sice podařilo rozchodit, ale nastalo podstatné snížení výkonu, stránky se rendrují nikoliv cca 100 ms, ale 600 ms.
Závěr:
Při standardní instalaci composeru nelze rozumným způsobem použít open_basedir v php.ini na omezení práv skriptů PHP k souborům v celém domácím Windows 10 PC. Rozumné je zůstat pouze u dynamického nastavení ini_set(‚open_basedir‘), které dle provedených testů zabrání dávkovému smazání souborů jiných Nette projektů, nebo celého disku domácího PC.
Kód jsem zatím vložil do Bootstrap.php, protože tam už mám větev pro lokální domácí stroj a protože mě šlo v podstatě hlavně o ochranu před vlastní chybou ve vlastním PHP kódu při mazání souborů, tak si myslím, že to takhle bude stačit.
Otestované řešení:
class Bootstrap
{
public static function boot(): Configurator
{
$configurator = new Configurator;
$appDir = dirname(__DIR__);
.......
.......
$configurator->addConfig($appDir . '/config/common.neon');
$configurator->addConfig($appDir . '/config/services.neon');
if($_SERVER['HTTP_HOST'] === 'localhost'){
$configurator->addConfig($appDir . '/config/local.neon'); // nastavení pro domácí (vývojový) server
// povolena složka projektu + složka pro dočasné upload soubory
ini_set('open_basedir', implode(';', [$appDir, ini_get('upload_tmp_dir')]));
}
return $configurator;
}
}
Překvapivě jsem při testech zjistil, že session Nette nebylo restrikcemi open_basedir nijak na funkci omezeno, takže není potřeba adresář pro ukládání souborů session speciálně povolovat, Nette si to umí povolit samo.
Editoval m.brecher (13. 9. 2022 14:35)