Tuesday, February 2, 2016

Limbajul PHP. Cookies. Greseala de incepator :-) (link aplicatie)

Un server poate gazdui un numar oarecare de scripturi sau de aplicatii-site, fiecare programat, eventual, sa foloseasca mecanismul de cookies. Fiecare dintre site-urile care trimit cookies seteaza acestor cookies, in afara de nume (cheie) si valoare, alti parametri precum timpul de expirare, domeniul si calea.

Exista, desigur, scenarii in care se folosesc valorile implicite ale parametrilor optionali.
Neglijarea acestor valori implicite (sau a logicii dupa care ele sunt generate) se soldeaza, uneori, cu consecinte neplacute.

Spre exemplu, fie enuntul urmator, dat ca tema in cadrul unui curs de PHP procedural:

" Se concepe un formular HTML in care utilizatorul alege dintr-un drop-down list site-ul catre care doreste sa mearga. Dupa expedierea formularului, utilizatorul va fi redirectionat catre site-ul ales. 
1) Adaugire: se va include un checkbox "Remember my decision". Odata ce utilizatorul a ales un site, a bifat checkbox-ul si a expediat formularul (fiind redirectionat catre site-ul ales), la fiecare vizita ulterioara el va fi automat redirectionat catre site-ul ales initial. 
2) Adaugire: odata ales site-ul default, la vizitele ulterioare, pagina va contine un buton "Reset my website preference". Apasarea acestui buton duce la anularea site-ului default - la urmatoarea vizita, utilizatorului i se va prezenta din nou pagina de selectie de site".

Primele doua cerinte nu comporta probleme: se folosesc formularele HTML corespunzatoare, functia header(), functia setcookies(). "Surprizele"pot sa apara la ultimul punct.

Presupunem ca formularul html este gazduit pe prima pagina a unui site, sa-i spunem site-ul A, iar selectia din drop-down list redirecteaza utilizatorul catre site-urile B, C, D…. Pentru simplitate, vom limita momentan drop-down list la un singur site, B, care poate fi bifat sau nu ca favorit, in vederea redirectarii automate.

Site-ul A se afla intr-un folder oarecare pe masina locala, sa-i spunem "practice1", iar site-ul B se afla intr-un subfolder al lui practice1, sa-i spunem "targets". Aceasta configuratie este importanta si vom vedea imediat de ce.

Site-ul A:

<DOCTYPE HTML>
<head>
<title>Site-ul A</title>
</head>
<body style = "background-color:AntiqueWhite">
<h3>Welcome! Select your target site form the list below:</h3>
<?php
$formular =<<<END
<form method = "POST" action = "$_SERVER[PHP_SELF]">
<select name = "target_site">
            <option value = "./targets/SiteB.php" selected = "sel">Site B</option>
            <option value = "./targets/SiteC.html">Site C</option>
</select>
<input type = "checkbox" name = "rem">Remember my option
<input type = "submit" name = "send" value = "Ok">
</form>
END;

if(isset($_COOKIE['fav'])) header("Location: $_COOKIE[fav]");

if(in_array("Ok", $_POST))
{
            if(array_key_exists("rem", $_POST))
            {
                        setcookie("fav", $_POST['target_site']);
                        header("Location: $_POST[target_site]");
            }
            else header("Location: $_POST[target_site]");
} else echo $formular;

?>
</body>
</html>



Daca s-a setat anterior (<==> daca browserul trimite catre server) cookie-ul cu numele (cheia) "fav" si valoarea unui site-tinta oarecare, atunci userul este redirectat catre site-ul tinta. Aceasta clauza este prioritara si functioneaza independent.

Daca s-a apasat butonul "Ok" (exista valoarea "Ok" in array-ul $_POST), atunci, daca s-a bifat casuta ' Remember my option' (=> exista cheia "rem" in $_POST) :  1) se va seta cookie-ul "fav" cu valoarea site-ului tinta; 2) se redirecteaza user-ul catre site-ul tinta.

Daca s-a apasat "Ok" fara se se bifeze casuta "Remember…" efectul este doar redirectarea userului fara setarea cookie-ului.

Daca niciunul dintre scenariile anterioare nu survine, atunci se afiseaza formularul cu drop-down list, casuta de bifare si butonul "Ok".

Site-ul tinta B. 

El are o functionalitate simpla: daca apasam butonul de reset (cheia "res" in $_POST) atunci, indiferent daca anterior s-a setat anterior printr-un cookie functionalitatea de redirectare catre B, vom fi intorsi la site-ul initial, A.

<DOCTYPE HTML>
<head>
<title>Site-ul B</title>
</head>
<body style = "background-color:lightgreen">
<h3>Hello! I am the selected target site.</h3>
<h3>Have you selected me as favorite? If so, when you type in your browser's bar the adress of the primary site you'll be automatically redirected here.</h3>
<?php

$formular1 =<<<END
<form method = "POST" action = "$_SERVER[PHP_SELF]">
<input type = "submit" name = "res" value = "Reset">
</form>
END;
if(array_key_exists("res", $_POST))
{
            setcookie("fav", "");
            header("Location: ../SiteA.php");
} else echo $formular1;
?>
</body>
</html>



Presupunand ca am tastat in browser adresa site-ului A si apoi am bifat ca favorit site-ul tinta B, acesta se va afisa, conform asteptarilor. Daca apasam butonul "Reset" in vederea resetarii cookie-ului "fav" si intoarcerii la site-ul A vom constata ca acest lucru nu se intampla.

Functionalitatea nedorita poate fi testata aici.

Unde este eroarea? Functia setcookie("fav", "") ar urma sa anuleze (in ipoteza ca e deja setat) cookie-ul cu cheia "fav", domeniul "localhost" si calea "/practice1/targets".

Aceasta e calea catre script-ul site-ului B in sistemul de foldere de pe masina pe care s-a facut testul.

Sa ne amintim insa ca site-urile A si B sunt in foldere diferite. Cand s-a setat cookie-ul "fav" pe browserul client pentru a obtine redirectarea automata catre B la tastarea adresei lui A, calea implicita a fost "/practice1" (captura urmatoare). NU "/practice1/targets" !



Asadar, script-ul lui B incearca sa stearga un cookie care nu exista pe client. Nume este mentionat corect ca argument al lui setcookie("fav", …) dar calea implicita la apelarea lui setcookie("fav", …) din B este alta decat cea generata implicit atunci cand a fost creat  "fav" de catre site-ul A!

Cu alte cuvinte, setcookie("fav", "") apelat din B nu poate reseta un cookie generat din A (cu setcookie("fav", "some_value") cand A si B sunt in foldere diferite.

Iata cum arata calea unui cookie generat pentru test in scriptul B prin adaugarea liniei
setcookie("test_path_cookie", 100);



Prin urmare, daca vrem sa stergem din B cookie-ul generat de A, trebuie sa setam corect calea:
setcookie("fav", "", 0, "/practice1", "localhost");

Domeniul localhost nu este obligatoriu de specificat in acest caz (ambele site-uri sunt sub acelasi domeniu).

ATENTIE: Sintaxa mentionata anterior este valabila pentru Chrome. In Mozilla si IExplorer browserul specifica urmatoarele cai la apelarea din A, respectiv B:

Path: '/practice1/' (din A)
Path: '/practice1/targets/' (din B)

Trebuie tinut cont de acest aspect cand lucram cu browsere diferite.

O alta cale independenta de browser prin care ne asiguram de stergerea unui cookie cu ajutorul unui script care NU a generat acel cookie este sa apelam din scriptul curent, cu un paramentru oarecare, scriptul initial. Pe baza testului de parametru putem sa impunem conditia de reset.

Facem acest lucru in codul urmator, adaugand inca un site, C,  la drop-down list-ul cerut de enunt si modificand clauzele if/else din site-ul A.

Site-ul C:

<!DOCTYPE HTML>
<head>
            <title>Site C</title>
<head>
<body style = "background-color:lightblue">
<form method = "POST" action = "../SiteA.php">
<br>Reset my redirect option...<br />
<input type = "submit" name = "res" value = "Reset">
</form>
</body>
</html>

Site-ul B, doar partea de cod <?php …?> cu clauza if/else adaugata ingrosat:

if(isset($_COOKIE['fav'])) header("Location: $_COOKIE[fav]");

if(in_array("Ok", $_POST))
{
            if(array_key_exists("rem", $_POST))
            {
                        setcookie("fav", $_POST['target_site']);
                        header("Location: $_POST[target_site]");
            }
            else header("Location: $_POST[target_site]");
}

else if(array_key_exists("res", $POST)
{
            setcookie("fav", "");
            header("Location: SiteA.php");
            echo $formular;
}
else echo $formular;


Functionalitatea ceruta de enunt poate fi testata aici.

No comments:

Post a Comment