Vypnutí magic_quotes_gpc

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í.

Jakub Vrána, Řešení problému, 13.1.2006, on-line

Diskuse

ikona dgx:

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

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

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

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

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

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 01:27:08

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 01:30:05

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 20:05:50

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

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

ikona spaze:

asi tyhle:

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

;)
13.2.2006 15:16:23

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

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

M.:

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

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

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

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

ikona Jakub Vrána:

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

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

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

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

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 14:49:42

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 17:41:05

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 01:55:24

ikona Jakub Vrána:

... a mezeru.
5.6.2007 14:48:53

vlado:

je to parada
11.8.2006 01:16:16

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 14:32:40

ikona Jakub Vrána:

Ano.
5.6.2007 14:50:13

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 15:11:13

tomasr:

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

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 15:07:09

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 15:10:58

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 15:24:49

ikona Jakub Vrána:

Ano.
1.4.2008 16:38:05

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 15:42:12

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 09:48:01

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 05:06:09

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 04:46:36

ikona Jakub Vrána:

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

3m2:

aha :) dik :))

u mna to ale blblo, mozno chybka v php
30.7.2008 04:49:13

š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 18:59:50

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 04:10:05

š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 17:17:05

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 03:30:12

š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 09:47:33

ikona Jakub Vrána:

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

štíhloprd:

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

š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 08:53:09

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 17:38:43

ikona Jakub Vrána:

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

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 05:12:35

harvey:

Len ma tak napadlo, že čo takto rekurzívne použiť užívateľom definovanú funkciu, na všetky prvky pola, čo by mohlo vapadať asi takto:
<? php
function definovanaFunkcia(&$value,$key)
    {
        $value=addslashes($value);
        $key=addslashes($key);
    }
//použitie na všetky prvky poľa
array_walk_recursive($_GET, 'definovanaFunkcia');
?>
Samozrejme treba si overiť, či je magic_quotes_gpc zapnuté, či vypnuté a podľa toho použiť správnu uživatelskú
7.2.2011 17:38:44

ikona Jakub Vrána:

Funkce je k dispozici od PHP 5, nedovoluje ale změnit hodnoty klíčů.
8.2.2011 12:18:20

harvey:

Aaaaa, to mi akosi uniklo, ďakujem za napomenutie.
Vyriešil som to na koniec takto, aj keď je to vlastne riešenie ktoré predpokladá magic_quotes_gpc vypnuté, ale samozrejme, dá sa to upraviť. Použil som síce rekurzívne volanie funkcie, ale pomocou premennej $depth sa dá nastavit, ako hlboko sa može funkcia zanoriť, ak je to hlbšie, vypíše sa text, prip. sa to dá inak ošetriť. Na klúče som použil regulerny výraz, aby to nezhodili nejaké skripty vpísané do adresného riadku prehliadača, kde by pole $_GET mohlo narobiť psie kusy.
<?php
   
function safeArray(&$input,$depth=3)
    {
        $output='';
        if(is_array($input))
        {
            $depth--;
            reset($input);
            while(list($key,$value)=each($input))
            {
                $key=preg_replace('/[^\w]/','X',$key);
                if(is_array($value))
                {
                    if($depth>0)
                    {
                        $output[$key]=safeString($value,$depth);
                    }
                    else
                    {
                        $output[$key]='nejaka hlaska';
                    }
                }
                else
                {
                    $output[$key]=addslashes(htmlspecialchars($value));
                }
            }return $input=$output;
        }
}
// volanie funkcie
safeArray($_GET);
?>
8.2.2011 20:24:42

radas:

Zdravím,
snažim se zabezpečit POST/GET data při vkládáni do databáze inspiroval jsem se přízpěvkem od harvey

<?php
function clearValues(&$value, $key)
{
$value=trim($value);
$value=strip_tags($value);
$value= addslashes($value);
$value=mysql_real_escape_string($value);
return
$value;
}

//volam
array_walk($_GET,"clearValues");
array_walk_recursive($_POST, "clearValues");
?>

můžu se spolehnout, že tato funkce již bezpečně "očisti" posíláné data do databaze na nehrozi mi SQL injection?

8.3.2011 23:57:56

ikona Jakub Vrána:

Ne, to je celé úplně špatně. Při vkládání do databáze stačí použít mysql_real_escape_string (pokud je vypnuté magic_quotes_gpc), při vypisování dat do stránky potom htmlspecialchars.
9.3.2011 09:55:14
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.