Vypnutí magic_quotes_gpc

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

Dopsal jsem knihu

O konfigurační direktivě magic_quotes_gpc by měl vědět každý PHP programátor. Já si na svých serverech mohu konfiguraci PHP určit dle libosti, takže se na určité nastavení této direktivy mohu spolehnout, ale např. projekt určený k nasazení v různých prostředích s touto direktivou musí počítat. Vzhledem k tomu, že tuto direktivu není možné nastavit ve skriptu funkcí ini_set (protože se vyhodnocuje ještě před spuštěním skriptu), je nutné s jejími různými hodnotami počítat buď přímo v kódu nebo se na její nastavení ve skriptu spolehnout a data ošetřit na začátku skriptu:

<?php
// odstranění ošetření dat způsobeného direktivou magic_quotes_gpc
if (get_magic_quotes_gpc()) {
    $process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
    while (list($key, $val) = each($process)) {
        foreach ($val as $k => $v) {
            unset($process[$key][$k]);
            if (is_array($v)) {
                $process[$key][stripslashes($k)] = $v;
                $process[] = &$process[$key][stripslashes($k)];
            } else {
                $process[$key][stripslashes($k)] = stripslashes($v);
            }
        }
    }
    unset($process);
}
?>

Kód se dá samozřejmě převrátit tak, aby data naopak v případě vypnutí direktivy ošetřoval, takže pak je možné psát elegantní kód ve tvaru "WHERE id IN ('" . implode("', '", $_GET["kategorie"]) . "')" a naopak je nutné vypisovaná data zvenku ošetřovat funkcí stripslashes.

Kód funguje i s poli parametrů a do proměnné $process postupně přidává další prvky ke zpracování – proto je použita archaická konstrukce list() = each(). Všimněte si, že se ošetřuje i pole $_FILES, na to se v podobných kódech často zapomíná. Ošetřené jsou i klíče polí, takže se nesmí zapomenout ani na ně. Řešení pomocí rekurzivní funkce je potenciálně nebezpečné kvůli možnému vyčerpání paměti, jak ukazuje Ilia:

<?php
file_get_contents("http://www.example.com/?a" . str_repeat("[]", 1024) . "=1");
?>

Přijďte si o tomto tématu popovídat na školení Bezpečnost PHP aplikací (8.10.2010, Praha).

Jakub Vrána, Řešení problému, 13.1.2006, diskuse: 51 (nové: 0)

Diskuse

ikona dgx:

hmmm, velmi vtipné a elegantní řešení!
# 13.1.2006 03:32:11 reagovat

Ondrej:

V diskuzi na phpguru.org pises, ze je adslashovana i promenna $_FILES[]['name'] ale na tehle strance projizdis cele pole $_FILES - nemuzes si nadelat paseku, kdyby ti to stripslashovalo treba $_FILES[]['tmp_name'] - pak by move_uploaded_file nenaslo docasnej soubor.
# 13.1.2006 10:08:59 reagovat

ikona Jakub Vrána:

tmp_name přiděluje PHP a dá se spolehnout na to, že ' ani podobné znaky součástí jména nebudou.
# 13.1.2006 11:18:47 reagovat

Ondrej:

btw: to prochazeni vnorenych poli je fakt docela mazany ... chvili mi trvalo, ze jsem pochopil, co to vlastne delas :-)
# 13.1.2006 12:18:38 reagovat

Andrew:

To jo, mazané to je. Já jsem sice neměl problém zjistit o co tam jde, ale zato mi trvalo, než jsem měl to srdce se rozloučit s krasným jednoduchým a krátkým slashováním polí:
<?php
function array_addslashes($data) {
  return (
is_array($data) ? array_map('array_addslashes', $data) : addslashes($data));
}
?>
No ale bezpečnost je bezpečnost a byť se rekurzí dá vyřešit spousta krásných věcí, používat je třeba s mírou.
# 16.1.2006 00:57:26 reagovat

Honza M.:

\ jistě bude podobný znak. A ten se objevuje v tmp_name pod windows, řekl bych. Doufám, že neplácám blbosti, ale každopádně mi po zavolání této funkce přestaly fungovat metody is_uploaded_file a move_uploaded_file.
# 7.6.2008 00:27:08 reagovat

Honza M.:

Jo. Koukám ruší to znak \ v tmp_name. Ten se pod windows vyskytuje a je to očividně špatně, že se ruší.
# 7.6.2008 00:30:05 reagovat

juneau:

Asi se mi něco takového stalo: copy&pastnul jsem kód do stránky s formulářem pro upload více souborů, tedy výsledné pole ve tvaru $_FILES['fotografie']['name'][1-15] ... a od té doby hodinu a půl řešil, proč funkce copy() funguje a move_uploaded_file() ne. Cesty byly správně, žádné uvozovky nikde, ale move...() prostě nejela. Po smazání tohoto kódu vše opět funkční. Všiml jsem si, že při move_uploaded_file() nedocházelo ani k vytvoření .tmp souboru, pokud mohu soudit. Takže takový malý bugreport, nebo možná moje blbost, ale po té hodině a půl už nemám nervy na "průzkum místa činu".
# 5.9.2007 19:05:50 reagovat

ikona MiSHAK.wz.cz:

Jo souhlasím u jmen souborů se některé znaky zakázané. Hezky napsané jen co je pravda :-)
# 13.1.2006 15:06:04 reagovat

Mordae:

Hmm, nějak mi vypadlo z hlavy které to jsou...? Prave mi proslo:

touch 'a!h@o#j$l%i^d&i*j(a)k_s+e|v}e{d"e:c?o>z<'
rm a\!h\@o#j\$l%i\^d\&i\*j\(a\)k_s+e\|v\}e\{d\"e\:c\?o\>z\<

Co jste rikal o tech nazvech?
# 13.2.2006 12:21:25 reagovat

ikona spaze:

asi tyhle:

$ touch '*'
touch: cannot touch `*': No such file or directory

;)
# 13.2.2006 15:16:23 reagovat

Pepca:

V Linuxu se v názvu souboru nesmí vyskytovat pouze dva znaky: lomítko, protože odděluje addresáře a "\0" protože se tím v C ukončují řetězce.
# 15.2.2007 11:57:10 reagovat

lukas:

Mně pripada lepsi napsat si funkce na import konkretni promenne, protoze pak se zabiji dve mouchy jednou ranou - osetreni magic_quotes a validaci hodnoty uvnitr promenne. Napriklad mivam definovane funkce invite_str, invite_num, ... (vsechny osetruji magic quotes a ta druha killne skript kdyz zjisti, ze hodnota uvnitr neni numeric).
# 13.1.2006 15:39:11 reagovat

M.:

Trochu OT, ale proč jste označil konstrukci "list() = each()" za archaickou?
# 14.1.2006 18:07:38 reagovat

ikona Jakub Vrána:

Pro průchod polí se obvykle používá spíše foreach: http://php.vrana.cz/prochazeni-poli.php.
# 14.1.2006 23:08:44 reagovat

Xixli:

Za zmienku by ešte možno stálo nastaviť si magic_quotes_gpc cez .htaccess: php_flag magic_quotes_gpc off
Avšak to tvoje je tiež pekné :-)
# 14.1.2006 22:06:30 reagovat

joe:

resil nekdo problem s tim, ze php slashuje nejen hodnoty, ale i hodnoty klicu v poli?
nechce se mi kvuli tomu vytvaret docasne pole a tim potom prepisovat vstup. Ale jine reseni mne bohuzel nenapada :(
# 27.1.2006 16:09:01 reagovat

ikona Jakub Vrána:

To je dobrá připomínka. Kód jsem patřične opravil.
# 27.1.2006 17:30:11 reagovat

joe:

Jeste jako bonus:
PHP5 slashuje jmena promennych vzdy. pokud se jedna o pole, oslashuje pouze posledni prvek. tj:
<?php
/*
<input type="text" name="a'b" value="val's" />
==> [a\'b] => val's

<input type="text" name="a'b[a'b][a'b]" value="val's" />
==> [a'b] => Array
        (
            [a'b] => Array
                (
                    [a\'b] => val's
                )

        )
*/
?>
tj. vyhnout se slashum, jak zubarovi s vrtackou.
# 27.1.2006 21:49:24 reagovat

ikona Jakub Vrána:

Víš to jistě? Já dostanu výsledek:

Array ( [a'b] => Array ( [a\'b] => Array ( [a\'b] => val\'s ) ) )

PHP 5.0.5 a 5.1.2 pod Windows. Funkci jsem napsal tak, aby se podle toho chovala. Obzvlášť se mi nezdá tebou prezentovaný výsledek pro jednoduchý případ: [a\'b] => val's.
# 28.1.2006 00:00:46 reagovat

joe:

kod:      http://phpfi.com/98860
vysledky: http://phpfi.com/98861

http://bugs.php.net/bug.php?id=29776
# 28.1.2006 11:39:53 reagovat

ikona Jakub Vrána:

Byla to nejspíš chyba PHP, zpětné lomítko je nyní ve všech klíčích - upravil jsem podle toho kód.
# 5.6.2007 13:49:42 reagovat

Honza Odvárko:

magic_quotes_gpc slashuje klíče superglobals od případu k případu jinak, pročež je dost složité toto nějak stručně zjistit->ošetřit. Záleží nejen na verzi PHP (pro budoucí kompatibilitu si račte hodit korunou), ale taky na tom, zda hodnotou prvku je řetězec nebo pole. Viz tabulku: http://cz2.php.net/manual/en/function.get-…-gpc.php#49612
# 22.7.2007 16:41:05 reagovat

ikona dgx:

Jelikož hodnoty klíčů určuje programátor (nikoliv uživatel vyplňující formulář), tak se mi zdá vhodnější tyto "nebezpečné" znaky vůbec nepoužívat. S případě slashování jde především o uvozovky, z historických důvodů není možné v klíči či názvu proměnné použít ani tečku.
# 11.8.2006 00:55:24 reagovat

ikona Jakub Vrána:

... a mezeru.
# 5.6.2007 13:48:53 reagovat

vlado:

je to parada
# 11.8.2006 00:16:16 reagovat

tomasr:

Jak bude vypadat ten kód pro ošetřování řetězců při vypnuté directivě magic_quotes_qpc? Jen se změní stripslashes za addslashes?
# 5.6.2007 13:32:40 reagovat

ikona Jakub Vrána:

Ano.
# 5.6.2007 13:50:13 reagovat

tomasr:

Moc to nechápu tu fci a nevim, jak to spojit s tim ošetřením typu (u getů čísla přetypovávat na integer, atd.). Používam toto a chtěl bych se zeptat:
  1.Zda je to bezpečné?
  2.Jelikož musim  tu fci u každýho dotazu volat na každou proměnou a zbytečně narůstá kód, nešlo by to nějak z automatizovat?
Předem děkuju.

<?php
static function validacePromenne($promenna, $typ = 'int')
{
  switch (
$typ) {
    case
'int':
     
$promenna = intval($promenna);
      break;
    case
'string':
      if (!
get_magic_quotes_gpc()) $promenna =
     
addslashes($promenna);
      break;
    default:
$typ = 'int';
  }
  return
$promenna;
}

static function
odstranitLomitka($hodnota)
{
 
$hodnota = is_array($hodnota) ?
 
array_map('odstraneniLomitek', $hodnota) :
 
stripslashes($hodnota);
  return
$hodnota;
}
?>
# 5.6.2007 14:11:13 reagovat

tomasr:

Jakube nenapsal bys mi nějaký inspirativní příspěvek?
# 6.6.2007 14:45:42 reagovat

Gifi:

Mam jednu otazku. osetreni magic_quotes_gpc pomoci kodu ktery si zde uvedl pouziju pouze tedky kdyz to na serveru je nastaveny pokud ne neuvedu ale zpusob prace pro ukladani do databaze a nacitani dat zustava stejny? a jeste otazka trosku mimo. kdyz pouziju fci addslashes tak v mysql databazi pri prochazeni nevidim zadne escapovane znaky. premyslel jsem jestli mysql pouziva pri vypsani na vystupu stripslashes. zkousel jsem taky vlozit do formularoveho pole nejaky mysql dotaz jestli se vykona a tak overit jestli fci addsplashes funguje. s programkem teprv zacinam takze pls jednoduse
# 1.4.2008 14:07:09 reagovat

ikona Jakub Vrána:

Ano, kód slouží k tomu, aby se s daty pracovalo stejně nezávisle na nastavení direktivy.

Escapování slouží jen k tomu, aby bylo možné data uložit, v databázi jsou uložená čistá. Např. 'McDonald\'s' se uloží jako McDonald's. Databáze žádné odošetření při výstupu neprovádí.
# 1.4.2008 14:10:58 reagovat

Gifi:

a tak to se vysvetluje. kdyz jsem mel zapnutou direktivu magic_quotes na on a jeste do databaze je ukladal pomoci addsplashes tak v databazi byli uvozovky vyditelne, a to jen proto, ze se escapovani provadelo dvakrat, ano?
# 1.4.2008 14:24:49 reagovat

ikona Jakub Vrána:

Ano.
# 1.4.2008 15:38:05 reagovat

Gifi:

jeste me vrta hlavou malicost. kdyz pouziju na vystupu htmlspecialchars tak na strance odkaz nefunguje a vypsal tak jak ho vidim v editoru,ale kdyz tuto direktivu nepouziju ve formularich nebo textarea taky neni aktivni. je proto nutne tyto znaky prevadet
# 1.4.2008 14:42:12 reagovat

3m2:

manual pise ze Magic Quotes sa tyka len GET, POST, and COOKIE.
Preco potom prehanas algoritmom aj $_REQUEST a $_FILES???

dik za odpoved
# 14.7.2008 08:48:01 reagovat

ikona Jakub Vrána:

$_REQUEST je sjednocení zmíněných tří a na některé položky ve $_FILES se magic_quotes aplikuje taky.
# 19.7.2008 04:06:09 reagovat

3m2:

ok $_FILES je jasne :)

ale ked to aplikujes na GET,POST,COOKIE a aj REQUEST

tak vlastne volas stripslashes 2x
mozes teda zmrsit data, alebo nie?
# 30.7.2008 03:46:36 reagovat

ikona Jakub Vrána:

Ne. REQUEST není reference na ostatní, je to kopie.
# 30.7.2008 03:48:11 reagovat

3m2:

aha :) dik :))

u mna to ale blblo, mozno chybka v php
# 30.7.2008 03:49:13 reagovat

štíhloprd:

To s tím REQUEST (a proč ho tam používáš) jsem nepochopil. Mohl bys to vysvětlit podrobněji? (prosím)
# 4.10.2008 17:59:50 reagovat

ikona Jakub Vrána:

$_REQUEST je potřeba ošetřit proto, že se s ním zachází stejně jako s $_GET, $_POST a $_COOKIE (magic_quotes_gpc se na $_REQUEST aplikuje stejně jako na ně).
# 6.10.2008 03:10:05 reagovat

štíhloprd:

Hmmmm... koukám, bez referencí by to asi nešlo tak elegantně.

1) S PHP 6.0 se magic_quotes_gpc a magic_quotes_runtime ruší.
2) Teď ještě, jak na magic_quotes_runtime()? :-)
# 20.8.2008 16:17:05 reagovat

ikona Jakub Vrána:

magic_quotes_runtime stačí vypnout v kódu pomocí ini_set(). magic_quotes_gpc se zohledňuje už před spuštěním kódu, takže to vypnout pomocí ini_set() nestačí.
# 21.8.2008 02:30:12 reagovat

štíhloprd:

Case study: dejme tomu, že jsem pro klienta udělal stránky v PHP verze 4 nebo 5, které s magic_quotes_gpc počítají (ptají se na výsledek funkce get_magic_quotes_gpc()). Za rok klientův hosting upgraduje na PHP 6, kde magic_quotes_gpc odpadnou ( http://cz.php.net/manual/en/info.configuration.…-quotes-gpc ). Co s tím? :-) resp. Jak ten web napsat tak, aby mi za rok klient nemusel volat nas*aný, že mu mnou dělané stránky hází chyby?

(Hledal jsem, ale nenašel:
-co v PHP 6 způsobí zavolání funkce get_magic_quotes()?
-budou v PHP 6 magic_quotes_gpc "off" nebo "on"? (resp. budou uvozovky escapovány nebo ne?)
# 3.10.2008 08:47:33 reagovat

ikona Jakub Vrána:

Direktiva magic_quotes_gpc bude pochopitelně vypnutá. get_magic_quotes() vrátí vždy false.
# 3.10.2008 08:52:36 reagovat

štíhloprd:

Aha, díky. Teď jsem to teprve našel podrobněji:
http://cz.php.net/magic_quotes
# 3.10.2008 08:55:30 reagovat

štíhloprd:

ještě bych dodal, že dle dokumentace (v<6.0):
-pokud je magic_quotes_gpc off, get_magic_quotes_gpc() vrací 0
-pokud je magic_quotes_gpc on, get_magic_quotes_gpc() vrací 1
asi bude třeba to vyzkoušet, popř. testování na false, resp. 0 a 1 provádět přes === nebo !== .

A tady jsem vygooglil seznam změn pro PHP 6.
http://www.corephp.co.uk/archives/19-Prepare-for-PHP-6.html
# 4.10.2008 07:53:09 reagovat

ikona david@grudl.com:

Zkusil jsem ověřit, kde všude PHP 5.2.6 nacpe ony fuckinglomítka:

- $_GET, $_POST, $_COOKIE: do všech klíčů + hodnot

- $_FILES: jen do některých klíčů!

Například [input type="file" name="o'file"] a upload souboru o'brazek.png vytvoří pole

array(2) {
  "o\'file" => array(4) {
    "name" => string(11) "o" // jméno se poškodí
    "type" => string(11) "image/x-png"
    "error" => int(0)
    "size" => int(20624)
  }
  "o'file" => array(1) {
    "tmp_name" => string(23) "C:\PHP\temp\php3ED8.tmp"
  }
}

Závěr je ten, že pro pole $_FILES je potřeba volit odlišný přístup. Doporučil bych je ze zpracování ZCELA VYNECHAT a na straně aplikace zajistit, že v klíčích se nebudou používat znaky ' " \ a NULL-byte.
# 11.10.2008 16:38:43 reagovat

ikona Jakub Vrána:

$_FILES jsem z kódu odstranil. Různé verze PHP se ale chovaly různě.
# 16.10.2008 03:22:00 reagovat

Pinqui:

Takže by mělo stačit dát k´d pro tuto kontrolu před začátek html (doctype) a pak by se mělo již vše ošetřovat?
# 9.7.2009 04:12:35 reagovat

Vložit příspěvek

Používejte diakritiku. Nelze používat HTML značky, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

© 2005-2010 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.