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.

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]];
}

?>

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.