stripslashes() (DA)

PHP-funktionen stripslashes() bruges ofte, selv om den slet ikke er nødvendig. Jeg vil påstå at stripslashes() udelukkende er til “nødsituationer”, og altså kun bruges, hvis der er noget galt. Jeg vil her forklare hvorfor det er sådan, og hvorfor mange oplever problemer hvis de ikke bruger addslashes().

Denne artikel tager udgangspunkt i PHP’s funktioner addslashes() og stripslashes(), men princippet gælder også for andre måder at opnår det samme på, og også for andre sprog end PHP.

Problemet

Når man indsætter noget i en database, kan SQL-kommandoen fx. se sådan ud:

INSERT INTO tabel (foo, bar) VALUES (12, 'Dette er en prøve');

Bemærk at teksen “Dette er en prøve” er markeret med anførselstegn (”single quotes“). Man hvad så hvis der er apostroffer med i teksten?

INSERT INTO tabel (foo, bar) VALUES (12, 'Dette er Niels' prøve');

Apostroffen tolkes, helt korrekt, som “her slutter teksten”. Men serveren kender ikke til noget, der hedder “prøve”, så den siger at der er en fejl.

Løsningen

I computerverdenen bruger man ofte tegnet “backslash” (en skrå-streg, der vender den anden vej) til at vise, at det efterfølgende tegn ikke betyder det, det plejer at betyde. Det hedder at “escape“. I bla. MySQL betyder det fx. “det efterfølgende tegn plejer godt nok at markere begyndelsen eller slutningen af et stykke tekst, men her er det altså bare en apostrof”.

INSERT INTO tabel (foo, bar) VALUES (12, 'Dette er Niels\' prøve');

Men man behøver ikke at skrive backslash foran hver eneste apostrof. I PHP kan man fx. gøre sådan her:

<?php
$foo = (int)$_POST['foo'];
$bar = addslashes($_POST['bar']);
$sql = "INSERT INTO tabel (foo, bar) VALUES ($foo, '$bar')";
echo "<p>SQL: $sql</p>";
...

Det er i øvrigt ikke kun apostroffer der giver problemer, men løsningen er den samme med de andre tegn. Fra manualen: Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote (’), double quote (”), backslash (\) and NUL (the NULL byte).

Når alt er som det skal være

Når SQL-serveren ser en apostrof med en backslash foran, så ved den altså, at man blot vil have en apostrof. Derfor bliver backslashen ikke gemt nogen steder. Når man henter data ud af databasen, vil der derfor ikke være noget, der er escaped.

Hvorfor det nogle gange ser ud som om stripslashes() er nødvendig

PHP har en funktion, som hedder “magic_quotes_gpc“. Funktionen prøver selv at køre addslashes() når det er nødvendigt, og er ment som en hjælp til begyndere.

Når magic_quotes_gpc er slået til, vil PHP automatisk køre addslashes() på data der kommer fra klienten. “GPC” står for “Get Post Cookie”, dvs. det er get-data, post-data og cookie-data.

Dette er dog et helt forkert tidspunkt at køre addslashes() på. Det er jo ikke altid at GPC-data skal i databasen, nogle gange skal det fx. bare skrives ud på HTML-siden med det samme. Så er man nødt til at bruge fx. stripslashes().
Men det bliver værre endnu: Med magic_quotes_gpc glemmer man alt om addslashes(). Men det er jo ikke alt man putter i databasen, der kommer direkte som GPC-data. Så har man problemet med at det pludselig ikke virker en gang i mellem, og vi værste fald har man et stort, råbende sikkerhedshul.
Det eneste rigtige tidspunkt at bruge addslashes() er mens man bygger en SQL-kommando op. Jeg betragter magic_quotes_gpc som en bjørnetjeneste, og en af de dårligste beslutninger der er taget i PHP’s historie. Den ligger næsten lige så højt som beslutningen om, at det er slået til som standard.

Selv om addslashes() bliver kørt automatisk af PHP, så er der mange der også selv kører addslashes() manuelt. De har lært at det skal man gøre. Men når PHP også gør det, så vil data være “double-escaped“. SQL-serveren klare selv den ene, men den anden gang slashes bliver gemt i databasen. Når man så hiver data ud af databasen, vil det være nødvendigt at køre stripslashes().
Det er nemt at se logikken i, at når man sætter slashes på, så skal de også fjernes igen, men sådan er det altså ikke. Hvis data i databasen er escaped, så er det fordi det har været double-escaped da det blev indsat.

Flere løsninger

Hvis det eneste der kommer i databasen alligevel er rå GPC-data, så vil det virke med magic_quotes_gpc, hvis man helt dropper addslashes() og stripslashes(). Men selv om det er situationen lige nu, så kan det hurtigt ændre sig. Og husker du at tænke på escapes til den tid? Lige nu gør du, så “nu” er et rigtigt godt tidspunkt at få det gjort, selv om det måske ikke er nødvendigt.

Den bedste løsning er efter min mening at slå magic_quotes_gpc fra. Er man selv administrator på serveren kan man rette direkte i PHP’s konfiguration.
Hvis det er en Apache-server, så har man måske mulighed for at konfigurere via en .htaccess-fil. Lav en fil med navnet “.htaccess” med indholdet php_flag magic_quotes_gpc off. Findes filen allerede, så tilføjer du bare linjen et passende sted.
Det kan desværre ikke lade sig gøre at slå funktionen fra med PHP-kode, for når den første linje udføres, er skaden allerede sket. Er man nødt til at bruge PHP-kode, kan man gøre noget i stil med dette:

<?php
function autostrip(&$obj) {
  if (!is_array($obj)) {
    $obj = stripslashes($obj);
    return;
  }
  foreach($obj as $key=>$val) {
    autostrip($obj[$key]);
  }
}
if (get_magic_quotes_gpc()) {
  $GET = $_GET;
  $POST = $_POST;
  $COOKIE = $_COOKIE;
  autostrip($GET);
  autostrip($POST);
  autostrip($COOKIE);
} else {
  $GET =& $_GET;
  $POST =& $_POST;
  $COOKIE =& $_COOKIE;
}
?>

Denne stump kode kan lægges i en fil, som du så kan include øverst i alle dine andre filer. Jeg anbefaler at bruge funktionen require_once() til dette.
Du kan så benytte $GET, $POST og $COOKIE i stedet for $_GET, $_POST og $_COOKIE. De vil indeholde GPC-data uden slashes, uanset om magic_quotes_gpc er slået til eller ej.
Du skal blot huske, at de tre nye variabler ikke er superglobale! Dvs. du skal selv erklære dem for globale, i hver funktion, hvor du skal bruge dem. Fx:

<?php
function foo($bar) {
  global $GET, $POST, $COOKIE;
  ...