Proměnné zvenku

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

Ve svých skriptech se nikdy nespoléhám na to, že bude zapnutá direktiva register_globals a pokud to mohu ovlivnit, tak ji v konfiguraci vypnu. Nebojím se ani tak podstrčení hodnoty interní proměnné útočníkem (protože proměnné důsledně inicializuji), ale pole $_GET, $_POST a podobné používám hlavně kvůli přehlednosti kódu. Když se podívám do některých svých starších skriptů a vidím změť proměnných, u kterých není na první pohled patrné, jestli jsou interní, přišly v URL a nebo jsou to třeba cookies, tak mám co dělat, abych se v tom vyznal. Z tohoto důvodu nepoužívám ani zkopírování těchto polí do obyčejných proměnných, jak to ukazuje následující kód:

<?php
// nedoporučený kód
$id = $_POST["id"];
$nazev = $_POST["nazev"];
$email = $_POST["email"];
// ...
?>

To jednak kód zbytečně zamořuje a jednak způsobuje popsaný problém nejasnosti původu proměnných při pozdější úpravě kódu.

Používání polí $HTTP_GET_VARS a podobných bych se nebál označit jako archaismus (proměnné $_GET a podobné byly zavedeny v PHP 4.1.0, tedy v prosinci 2001).

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

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

Diskuse

RejpalCZ:

Vám příjde zápis $_POST("promenna") v kódu přehlednější než prostě $promenna?
Není lepší do $var vložit hodnotu _POST / _GET pomocí funkce? Na začátku každého skriptu a funkce zinicializovat proměnné a pak používat kratší a imho přehlednější zápis?

<?php
function getP ($what) // returns _POST[$what]. Inicialize when empty
{
if (isset(
$_POST[$what]))
  { return $_POST($what); }
return
'';
}

function
MyFunction ($args)
{
// inicializace proměnných
$MyVar1 = getP('myvar1');
$MyVar2 = getP('myvar2');

// funkční část
...
...
// návrat hodnoty
return $out;
}
?>

Zabijete tím několik much jednou ranou, jednak máte po problémech s inicializací, druhak ve fci getP (příp. getG) i jednoduše ošetříte magic_quotes a další nepříjemnosti.

Peta:

Kdyz uz, tak $_POST('promenna')

Dale musim souhlasit s Jakubem - ja take radeji pisi o neco dele, ale skripty jsou citelnejsi...

RejpalCZ:

Narážel jsem na příklad, kde je taky použit tvar $_POST["promenna"].

Tady jde o to, že kupa $_POST['neco'] v kódu mi zrovna čitelnější nepřijde :/, zvlášť když to máte ještě ošetřit fcemi Xslashes.

Pavel Zbytovský:

A zvlášť když to chcete psát do řetězce... co je lepší:
<?php
$str
= "<b>".$_POST["promenna"]."</b>\n";
?>
       nebo
<?php
$str
= "<b>$promenna</b>\n";
?>

ikona Jakub Vrána OpenID:

<?php
$str
= "<b>$_POST[promenna]</b>\n";
?>
je stoprocentně správný způsob.

Lukoko:

<?php
$str
= "<b>$_POST[promenna]</b>\n";
?>
nebude fungovat u dvouromerneho pole
pokud mam neco jako
<input type="text" name="neco[]" >
<input type="text" name="neco[]" >
...
tak potom dostanu
$_POST[neco][0] a to takto nelze zapsat, je nutné

"<b>".$_POST[neco][0]."</b>\n";

Kody:

<?php
$str
= "<b>{$_POST['promenna'][0]}</b>\n";
?>
Tohle funguje i u vícerozměrných polí.

xxx:

spravny zpusob ?? jak si to vubec dovolujete tvrdit ???? hm taky nekdo snedl Salamounovo hovno, ze ??!!

tark:

Tady vidíme krásný příklad blbečka. :-)

xxx:

Tady vidíme krásný příklad blbečka. :-)

Radim:

Jakube, pokud neco nedoporucujete, je jiste i neco, co doporucujete. Dik.

ikona Jakub Vrána OpenID:

Doporučuji používat všude v kódu plný zápis, tedy např. $_GET["id"]. Psaní navíc je minimum a v kódu je pak na první pohled patrné, co pochází odkud.

Andrew:

Určitě to je správnější používat, jak píšeš, ale souhlasím také s níže uvedeným příspěvkem, že jakmile to začneš spracovávat, tak by se to mělo hodit jinam a nepředělávat to v superglobálních proměnných.
Já mám ale příklad, kde to potřebuji hned na začátku dostat z $_GET. Je to pro výpis stavových hlášení, které ale mohou být poslána zvenčí, nebo ze samotného skriptu a jsou si rovnocena.

ikona Karel Dytrych:

Tak ty promenne muzes zpracovavat uvnitr nejake funkce ktera si vezme GET/POST jako vstupni parametr... pak je jasne, ze se zpracuje ve funkci to co se do ni posle.

Používejte diakritiku. Vstup se chápe jako čistý text, 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: Reakce na: Karel Dytrych

grovik:

Souhlasim, naopak mnoho lidi pouziva kopirovani promenych $_POST a $_GET do jinych, staci odradkovat o stranku a v kodu se nikdo nevyzna.

Krispin:

no..ja teda vsude v kodu nepouzivam $_POST['promenna'] / $_GET['promenna']...neprijde mi tot aky moc prehledny...mam uz zazitej zvyk, ze na zacatku stranky si zkontroluju ocekavana data pomoci Ereg(I) a vlozim je do jejich promennych ekvivalentu: $_POST['promenna'] => $_prommenna -- jediny co nevim odkud pochazi, jeslti z POST, nebo GET, ale to uz neni takova traga...hlavni je ze vim, ze je z venku, ze je na 100% spravna a obsahuje pouze ocekavana data a ze nemusim vypisovat tu dlouhou variantu...:)

pif:

musim souhlasit, ze napr $_promenna je prehlednejsi varianta :)). Ostatne, snad je i skoro jedno, jestli hned vidime get nebo post, a reknu si aha, get. Aha post. Nenapada me, proc by  to melo bejt tak uzitecny.

Hlavni je, kdyz s superglobalnimi poli pocitame a kontrolujeme.

ikona Jakub Vrána OpenID:

Pro neznalého člověka to přehlednější není. Když v cizím kódu vidím $_promenna, tak si řeknu - aha to bude nějaká konstanta/globální proměnná/lokální proměnná - na první pohled není patrné, co je to zač. Navíc je kód zamořen balastem, jak je zmíněno v článku (pokud je použita funkce extract, je balastu méně).

Při psaní kódu by měly objektivní výhody (na první pohled je patrné, že je proměnná zvenku) převažovat nad subjektivními (zápis $_promenna se mi zdá přehlednější než $_GET["promenna"]).

Lord Mystic:

Musím jen souhlasit. Začínal jsem s php jako samouk a naučil jsem se spousty zlozvyků. Ale ještě před tím jsem se učil asp a tam se něco jako $_GET muselo zapisovat vždy když se načítala proměnná z venku. Když jsem pak začal s php tak jsem byl šťastný jak blecha že je to super jednoduché a že stačí zapsat jen $promena, ale dnes po několika letech programování i při tvorbě složitějších webu musím jednoznačně říct, že používám výhradně $_GET všude a nepřiřazuji to jednoduché proměnné - ty jsou jen pro daný script lokálně.

A věřte mi, php směřuje přesně tímto směrem, hodně k bezpečnosti. A ty složené závorky i elegantně vyřeší problém s uvozovakami.

Squad_Lead:

Presne toto se mi libi pouzivat.
U jednoduchych kodu $promenna = $_POST['jmeno'];
se zacatecnikovi (me) toto libilo, pak ale nastala ouha ouha. kod mel 1600 radku a musel jsem to upravovat.

ikona dgx:

"mix $_GET a $_POST je $_REQUEST. Pokud nevím (a je to jedno) odkud data pocházejí, můžeme použít i toto superglobální pole.

Pro psaní kvalitního a přehledného kódu by se měli dodržovat tyto zásady:

1) pro přístup k syrovým vstupním proměnným používat $_GET, $_POST, $_REQUEST (nebo také $_FILES)

2) v těchto polích proměnné neměňte (kromě samozřejmého odstranění nesmyslu jménem slashes)

3) Jakmile pole ověříte a převedete na správný typ, můžete je zkopírovat do globální/lokální proměnné $xxx a s tou dále pracovat.

ikona dgx:

a ještě bonus, funkce na odstranění slashes ze superglobálních polí. Funkce dávám k dispozici pod licencí GNU, je tedy třeba, aby každá aplikace, kde se použije, byla také pod GNU a obsahovala příslušný copyright :-)))
<?php
/***
* Recursive stripSlashes
***/
function stripSlashes_r(&$arr) {
  foreach (array_keys($arr) as $key)
    if (is_array($arr[$key])) stripSlashes_r($arr[$key]);
    else $arr[$key] = stripSlashes($arr[$key]);
}

// normalize GPC:
if (get_magic_quotes_gpc()) {
  stripSlashes_r($_GET);
  stripSlashes_r($_POST);
  stripSlashes_r($_COOKIE);
}
?>

ikona llook:

Já obvykle používám tohle:
<?php
if (get_magic_quotes_gpc()) {
    $_GET = array_map('stripslashes', $_GET);
    $_POST = array_map('stripslashes', $_POST);
    $_COOKIE = array_map('stripslashes', $_COOKIE);
    $_REQUEST = array_map('stripslashes', $_REQUEST);
}
?>
Ale pokud někdo chce předávat pole ve stylu index.php?a[]=b&c[]=d, pak může použít něco takovýhodle:
<?php
if (get_magic_quotes_gpc()) {
    function s($val) {
        if (is_array($val)) {
            return array_map('s', $val);
        }
    }
    $_GET = array_map('s', $_GET);
    $_POST = array_map('s', $_POST);
    $_COOKIE = array_map('s', $_COOKIE);
    $_REQUEST = array_map('s', $_REQUEST);
}
?>

ikona llook:

Sakra, mám tam chybu:
<?php
   
function s($val) {
        if (is_array($val)) {
            return array_map('s', $val);
        }
        return stripslashes($val);
    }
?>
Tak to dopadá, když píšu skripty z hlavy...

ikona dgx:

Ale llooku, nazvat funkci jako "s" ? A deklarovat ji navíc podmíněčně? To se pak divíš, proč na jednom hostingu aplikace běží, a na jiném háže chybu. :-)

Lépe snad takto
<?php

function stripSlashes_r($array) {
  return is_array($array) ? array_map('stripSlashes_r', $array) : stripslashes($array);
}

// normalize GPC:
if (get_magic_quotes_gpc()) {
  $_GET    = stripSlashes_r($_GET);
  $_POST   = stripSlashes_r($_POST);
  $_COOKIE = stripSlashes_r($_COOKIE);
}
?>
p.s. občas se mi komentář nedostane na správné místo, takže kdyby něco, toto je reakce na llooka 05.03.2005 15:30:49

maro:

Pochopil jsem to správně, že když používám _REQUEST, tak mi stačí změnit _GET a _POST? Nebo tam musím přidat i _REQUEST?

ikona Jakub Vrána OpenID:

Přinejmenším vyzkoušením bys zjistil, že $_GET a ostatní s $_REQUEST nejsou nijak provázané a změnou jednoho neovlivníš druhé.

RejpalCZ:

Ad dgx: Když tu zveřejním svou úžasnou část kódu
<?php
echo $promenna;
?>
pod licencí GNU, budou muset být všechny projekty tento řádek obsahující taktéž pod licencí GNU? (je to trošku provokace, ale vážně to nevím a zajímalo by mně, kde je ta hranice)

Jinak s vaším postupem výše uvedeným souhlasím, dodal bych jen, že je IMHO lepší k těm syrovým datům z _GET a _POST přistupovat ne přímo, ale pomocí ověřovací fce (viz výše).

ikona dgx:

pokud by ho někdo zkopíroval přímo odsud a pastnul do své aplikace tak ano :-)

Michal:

Tak takhle to skutecne neni. Je uplne jedno odkud se ten kod ve clipboardu ocitnul. Dulezite je, ze oba vase GPL-pokusy jsou natolik kratke a obecne, ze se proste copyrightem chranit nedaji :-)

Stano:

No, ak si dobre pametam, tak pri kode ktory chce autor zverejnit pod licenciou GPL musi byt pridelena aj cela verzia tejto licencie - co je niekolko stran textu, a co v tomto pripade nebolo uskutocnene, takze kupirujte kolko len chcete.

honza:

no dobre, ale urcite se nevyhnu prepisovani do vlastni promene v tomto pripade
v html kdyz je input text
<input type="text" name="pole[jmeno]" value="Jmeno">

tak v v php musim udelat
<?php
$pole
= $_POST["pole"];
$jmeno = $pole["jmeno"];
?>
- nebo snad tohle jde take nejak obejit?

Krispin:

toto je vicerozmerne pole...data z nej vydolujes takto: $_POST['pole']['jmeno']

Dave:

Ahoj, rad bych se zeptal ohledne tech promennych. Potreboval bych Javascriptem otestovat rozliseni a predat výsledek testu do nejake promenne v PHP abych s ni mohl dal pracovat, nemohl by mi nekdo poradit pls?
Predem diky.

Krispin:

musit to PHP nejak poslat...PHP je serverovy skript, takze jestlize mu chces dat vedet rozliseni, tak mu to musis dat v nejakym odkazu, aby se reloadla stranka...

Bjarne:

Tak tenhle tip podle me neni vubec dobrej, aspon pro me ne. Ja xmrti nesnasim psani silenosti typu $_POST['promenna']... proto mam fce podobne tem nahore getPost getGet getSession apod.
argument ze pak neni poznat co to vubec je za promennou a kde se vzala neberu... spravnej programator ma poradek v nazvu promennych a pomoci vhodny konvence vi co je co zac.

analagie, ktera me napada je napriklad "madarska notace" pri programovani ve WIN32 :)

Milan:

Zacinam s PHP a neprenasi se mi promene, netusite cim to je? V php.ini mam nastevno: register_globals = on  i tak mi to nejde? Netusim, kde muze byt problem - jedu na Apache. Dik za radu.

skybedy:

Škoda, u téhle diskuze řešící jednu z elementárních věcí mi trochu chybí Jakubova reakce na postoj DGX.

ikona Jakub Vrána OpenID:

Pokud myslíš bod 3. v příspěvku http://php.vrana.cz/promenne-zvenku.php#d-51, tak s tím se neztotožňuji, což je uvedeno už v článku.

skybedy:

Jasné, ale hlavním důvodem je tedy přehlednost, což je celkem subjektivní věc, nikoli důvody třeba bezpečnostní, je to tak?

Denis:

V mých aplikacích mám na práci s $_POST, $_GET atd. třídu, která se inicializuje hned na začátku programu. Po inicializaci této třídy již proměnné $_POST a $_GET neexistujou, jelikož třída je unsetne. To proto, abych měl jistotu, že je nějaký "blbec" nebude nikde dále v programu používat.
Tato třída si hodnoty z GET a POST nakopíruje do svého pole a pokud někdo chce k těmto hodnotám přistupovat, pak jedině pomoci této třídy.

Přístup k hodnotám pak provádím např. takto:
$InstanceTridy->GetValue(PARAMNAME_USERID);

Pokud se stane, že potřebuju nějaké paramtery z POSTu nebo GETu nějak kontrolovat, upravovat atd., vše se děje v oné třídě, takže na jednom místě.

Za aplikace, kde se všude v kódu povalují $_POST['name'] atd. bych dával dvacet na holou :D

ikona Jakub Vrána OpenID:

Tu třídu bych chtěl vidět. Z tohoto popisu mi přijde, že to žádný valný smysl nemá.

Dvain:

Někde se bohužel používá, resp. používalo i tohle:

<?php

$get
= array_keys ($_GET);
for (
$i=0; $i<count ($get); $i++) {
    ${
$get[$i]} = $_GET[$get[$i]];
}

?>
avatar © 2005-2022 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.