Scriptorama.nl

Header image showing a keyboard, mouse, laptop and books on design patterns

Hoe $_SERVER['PHP_SELF'] een XSS probleem kan veroorzaken

In dit artikeltje keken we naar een paar standaard dingen die vaak fout gaan bij mensen die met PHP beginnen. In deze korte toevoeging kijken we even naar wat variabelen in $_SERVER en hoe je die zou moeten gebruiken.

Het blijkt namelijk dat een van de meest gebruikte elementen uit $_SERVER: $_SERVER['PHP_SELF'] in veel gevallen niet veilig is; in elk geval met het gebruik van Apache. Als er namelijk PathInfo wordt meegegeven aan de URL en je gebruikt $_SERVER['PHP_SELF'] om bijvoorbeeld forms terug te posten naar dezelfde pagina, wordt deze path info ook direct weergegeven!

voorbeeld.php:

PHP:
  1. <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
  2. </form>

Als je voorbeeld.php aanroept via:

CODE:
  1. http://localhost/voorbeeld.php/"&gt;&lt;script&gt;alert('hatzee')&lt;/script&gt;

... heb je zomaar een Cross Site Scripting lek te pakken:

PHP:
  1. <form action="/voorbeeld.php"><script>alert('hatzee');</script>" method="post">
  2. </form>

En dat is natuurlijk niet de bedoeling!

What to do?

Allereerst kun je dit probleem tijdelijk omzeilen door PathInfo uit te schakelen. Dit kun je doen door in een .htaccess file het volgende neer te zetten:

CODE:
  1. AcceptPathInfo off

Het enige vervelende daaraan is dat een url als /index.php/artikel/1 dan gegarandeerd niet meer werkt. Je zult simpelweg een 404 foutmelding krijgen.

Maar gelukkig zijn er nog wat andere alternatieven. Zo kun je bijvoorbeeld $_SERVER['SCRIPT_NAME'] of $_SERVER['REQUEST_URI'] gaan gebruiken maar deze laatste werkt alleen niet in IIS.

Je zou ook, via een stukje script of met de hand, de juiste URL kunnen samenstellen en weergeven:

PHP:
  1. define('APP_WEB_PATH', '/website/');
  2.  
  3. // Gebruik deze in de form tag
  4. define('PAGE_WEB_PATH', APP_WEB_PATH . basename(__FILE__));

Als laatste heb je nog de mogelijkheid tot het bewerken van $_SERVER['PHP_SELF']:

PHP:
  1. /* Optie 1 - HTML tags omzetten naar HTML entities: */
  2. echo htmlentities($_SERVER['PHP_SELF']);
  3.  
  4. /* Optie 2 - HTML tags compleet verwijderen, met als safety-net toch nog HTML entities */
  5. echo htmlentities(strip_tags($_SERVER['PHP_SELF']));
  6.  
  7. /* Optie 3 - PATH_INFO uit $_SERVER['PHP_SELF'] verwijderen: */
  8.  
  9. function getPHPSelf()
  10. {
  11.   if (isset($_SERVER['PATH_INFO']) && strlen($_SERVER['PATH_INFO'])> 0)
  12.   {
  13.     return substr(
  14.       $_SERVER['PHP_SELF'],
  15.       0,
  16.       (strlen($_SERVER['PHP_SELF']) - @strlen($_SERVER['PATH_INFO']))
  17.     );
  18.   } else {
  19.     return $_SERVER['PHP_SELF'];
  20.   }
  21. }

Conclusie

Hoewel $_SERVER vaak als een "veilige" variabele wordt gezien is dit helaas niet het geval. Bepaalde elementen, zoals $_SERVER['PHP_SELF'] kunnen door kleine omstandigheden (PathInfo ja/nee) in de omgeving toch omgetoverd worden tot een onveilige waarde.

Reageer ook!

behalve dat de gebruiker zichzelf lastig kan vallen met javascript-gedoe, wat voor ander gevaar schuilt hier dan in?
Bovendien gaat dit alleen op als de magic_quotes uit staan; staan ze aan dat wordt je wel heel erg beperkt in het inserten van js-code.

Daar vergis je je toch in. Met de functie String.fromCharCode() kun je een hele hoop doen. Op PHPFreakz was ook iemand die dat niet zo geloofde, lees het volgende topic maar eens:

http://phpfreakz.nl/forum.php?forum=6&iid=779261#id779274

@Matieu

Mooi/goed voorbeeld. Het lijkt zo eenvoudig, maar zo was bijv hotmail hier in zijn vroege jaren ook gevoelig voor. *Kijkt stiekum even naar eigen scripts*

Matieu => Mathieu
Excuses :)

Ik geloof eigenlijk niet, dat als je een veilig script met $_SERVER["PHP_SELF"] etc. gebruikt dat je dan zo'n groot lek zou hebben.
Volgens mij ligt het gewoon net aan hoe netjes jij omgaat met het schrijven van je PHP...

.. probeer het maar es. Zie ook het PHPFReakz topic dat ik aanhaal in de comments..

Sorry maar even een vraag van een beginner ... waar moet je deze code dan plaatsen ?

Zou het zo lukken ?
echo htmlentities(strip_tags($_SERVER['PHP_SELF']));"?>

of moet je de code
echo htmlentities(strip_tags($_SERVER['PHP_SELF']));

in het eigenlijke PHP-gedeelte zetten ?

Als je naar dezelfde pagina wilt posten kun je ook nog gewoon geen waarde voor het action attribuut opgeven.

[...] Zoals de term user input het al zegt, het is invoer van de gebruiker. Nu zullen heel veel ontwikkelaars alleen denken aan $_GET. Okay, er zijn nog slimme onder ons die ook nog denken aan $_POST. Echter het blijft niet beperkt tot deze super globals. Een ander belangrijke vector zijn HTTP headers. Deze data wordt doorgegeven aan super global $_SERVER. Zoals Mathieu in "Hoe $_SERVER[’PHP_SELF’] een XSS probleem kan veroorzaken" laat zien is $_SERVER niet heilig. Een vaak over het hoofd gezien vector is $_COOKIE, ja de koekjes. Deze zijn ook te manipuleren en is dus ook user input. In feite zijn cookie gegevens gewoon HTTP headers. Om kort te zijn, alle super globals in PHP zijn user input en ga er altijd vanuit de user input niet te vertrouwen is en valideer deze altijd. [...]

Als ik AcceptPathInfo off opneem in m'n .htaccess krijg ik een Internal Server Error op elke pagina... Kan dat aan m'n Apache installatie liggen?

"alle super globals in PHP zijn user input "
Dus ook sessie's?

Niet per definitie, Bram. Het ligt er een beetje aan hoe je omgaat met sessie variabelen ($_SESSION['test'] = $_GET['test']; blijft natuurlijk een potentieel gevaar) en in hoe verre je de host vertrouwt :)

bedankt, dat had ik nog nooit opgemerkt,

ik zal er voortaan op letten!

groet,
Roelof

Gebruik gewoon "" als action. Werkt ook en is veilig.

[...] be your script file name. (The reason why I didn’t use $_SERVER['PHP_SELF'] is because of this Dutch source about how this element can cause an XSS [...]

Leave a comment
Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>