Suchen
Inside Wiki
Nützliche Links




 
phpforum.de bei Facebook
 
phpforum.de bei Twitter
 

Zurück   PHP Forum: phpforum.de > phpforum.de Wiki > phpforum.de Wiki

PHP Wiki Dieses Wiki sammelt Lösungen, zu Problemen, welche immer wieder im Forum auftauchen.

 
 
Artikel-Optionen Ansicht
  #1  

Standard Schutz vor PHP-Injection mit XML-Whitelist

 

Inhalte

Schutz vor PHP-Injection mit XML-Whitelist



Einführung


Warum eine Whitelist?


Spätestens wenn man selbst betroffen ist, greift man ein. In diesem Fall hier die Rede von PHP Code Injection. Es gibt viele Arten sich vor diesem Angriff zu schützen. Die meiner Meinung nach am sinnvollste Lösung ist eine Whitelist. Wird also ein Link per GET übergeben, wird geprüft ob der Wert des GET-Parameters in der Whitelist steht. Eine sichere Sache also und eine einfache dazu. Jedenfalls dann, wenn es um ein Projekt geht, dass nicht unendlich viele Seiten laden soll. Für ein Webprojekt mit bis zu 20 verschiedene Seiten Inhalt scheint mir eine Whitelist die beste Lösung. In dem obenstehendem Link steht eine Whitelist, die mit Hilfe eines assoziativen Arrays realisiert wurde.

Kommt nun eine neue Seite zu diesem Projekt hinzu, muss man es in das Array eintragen. Die Bearbeitung dieses Arrays ist durch die Syntax allerdings gefährlich: Vergisst man auch nur ein Komma, ist die gesamte Arrayzuweisung fehlerhaft. Besonders ungeübte Coder sowie Kunden, denen man die Aufgabe der Whitelistverwaltung in die Hand gibt können hier leicht einen Fehler machen. Das Ergebnis ist eine fast blanke Seite mit einer PHP-Information, die kein Besucher der Seite sehen möchte.

In meinem Beispiel greife ich deshalb auf eine XML-Whitelist zurück. Der Grund: Die XML-Whitelist kann separat gespeichert werden und ist sehr leicht editierbar. Wer einmal das Prinzip von öffnenden und schließenden Tags begriffen hat, wird keine Probleme mit der Bearbeitung einer XML-Whitelist haben.

Vorraussetzung für dieses Tutorial sind Grundkenntnisse in Klassen in PHP sowie grundlegendes Verständnis für den Aufbau von XML-Dateien. Ausserdem muss PHP >= Vers. 5 installiert sein.

Die Planung der Whitelist


Aus XML-Sicht könnte ein Whitelist-Eintrag nun so aussehen:

HTML Quellcode:
<?xml version="1.0" encoding="utf-8"?>
<whitelist>
  <entry alias="welcome">
    <url>seiten/willkommen.php</url>
  </entry>
</whitelist>


Das Laden einer Seite kann also nun so erklärt werden:
Ein Link sieht beispielsweise so aus:
HTML Quellcode:
<a href="index.php?load=welcome" target="_self">Startseite</a>

In der index.php, die bei jedem neuen Seitenaufruf sich selbst lädt, wird nun der GET-Parameter "load" abgefangen und ausgewertet. Ist der Alias "welcome" also in der Whitelist vorhanden, dann ist die PHP zu inkludieren, die in <url> festgelegt ist. Injektion ist hier ausgeschlossen!

Für einen weiteren Whitelist-Eintrag würde man nun einfach ein weiteres <entry>-Tag beginnen, dabei wird der Seitenalias festgelegt sowie die dazugehörige URL, die bei Aufruf dieses Alias geladen wird.

Die XML-Datei


Zusätzliche Informationen


Als kleines Schmankerl bietet es sich nun auch an, neben der URL für die eigentliche .php-Seite nun auch eigene Titel zu vergeben, die dann später im <title>-Tag verwendet werden. Auch eigene Meta-Informationen werden für <meta name="description" /> definiert. So etwas sehen Suchmaschinen bekanntlich gern.

Die Beispiel-XML-Datei


Unser Beispiel-XML-Dokument sieht nun so aus:
content.xml
HTML Quellcode:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<whitelist>
  <entry alias="welcome">
    <url>seiten/willkommen.php</url>
    <title>Willkommen auf der Seite der Hauptseite von Max Mustermann</title>
    <meta>Startseite der Webseite von Max Mustermann</meta>
  </entry>
  
  <entry alias="news">
    <url>seiten/neues.php</url>
    <title>Neuigkeiten - Max Mustermann</title>
    <meta>Neuigkeiten im Leben von Max Mustermann</meta>
  </entry>
  
  <entry alias="impressum">
    <url>seiten/impressum.php</url>
    <title>Impressum - Max Mustermann</title>
    <meta>Impressum von www.maxmustermann.de</meta>
  </entry>
  
  <entry alias="error">
    <url>seiten/error.php</url>
    <title>Fehler - Max Mustermann</title>
    <meta>Es ist ein Fehler aufgetreten...</meta>
  </entry>
</whitelist>


Die ersten drei entry's stellen unsere Seiten dar, die der Benutzer per Navigation erreichen kann. Die letzte Definition (alias="error") ist eine Seite, die aufgerufen wird, wenn ein Fehler aufgetreten ist - wenn also ein Alias-Name in der XML-Datei nicht gefunden wird. Es wird nun offensichtlich, dass mit einer solchen Liste neue Möglichkeiten offenstehen - Etwa die Erweiterung eines <content>-Tags, dass für statische Seiten sicherlich eine Option darstellt. Auch könnte man eine content_en.xml anlegen, die geladen wird wenn der Besucher eine andere Sprache wählt. Dazu aber später mehr.

Die Whitelist in das Web-Projekt einbinden


Um die XML-Whitelist in unsere Seite einzubinden, wird eine Klasse erstellt. Diese Klasse kann dann für jedes Web-Projekt benutzt werden.

Überlegungen zur Klasse


Die Klasse muss folgendes leisten: Der aktuelle Alias, der per $_GET['load'] abgerufen wird, muss ausgewertet werden, mit der XML-Whitelist abgeglichen werden und im Erfolgsfall werden die Alias-Unterpunkte (URL, Titel und Meta-Informationen) abgerufen werden können. Zunächst brauchen wir einmal vier Variablen:

PHP Quellcode:
class C_ContentManager {

    private $PageDetail = array();
    private $ErrorPageAlias = 'error';
    private $StartPageAlias = 'welcome';
    private $XML_Data;
}

Die Variable $PageDetail zu einem Array gemacht. Hier werden dann später die Informationen über die aktuelle Seite gespeichert (URL, Titel und Meta-Informationen).
$ErrorPageAlias und $StartPageAlias sind unsere Seiten, die für den ersten Start der Seite 'welcome' bzw. im Fehlerfalle 'error' dargestellt werden sollen.
$XML_Data wird später unsere XML-Daten beinhalten.
Da alle vier Variablen nur in dieser Klasse benutzt werden, sind sie als private eingestuft.

Einbinden der XML-Datei in die Klasse


Damit die XML-Datei direkt bei einer Instanz der Klasse geladen wird, schreiben wir die notwendigen Zeilen dafür in den Konstruktor:

PHP Quellcode:
class C_ContentManager {

// [...] (aus Platzgründen werden die bereits besprochenen Elemente der Klasse weggelassen)

function __construct($XMLPageIndexFile = 'content.xml') {
      // XML einbinden
        if(file_exists($XMLPageIndexFile)) {
          if(!@$this->XML_Data = simplexml_load_file($XMLPageIndexFile)) {
            die ($XMLPageIndexFile . ' ist keine gültige XML-Datei!');
          }
        } else {
          die ('Die Datei ' .$XMLPageIndexFile . ' existiert nicht!');
        }
    }
}

Hier gibt es die kleine Besonderheit, dass standardmäßig die 'content.xml' geladen wird, sofern bei der Instanziierung unserer Klasse kein Parameter übergeben wird, der den Pfad zur XML-Datei angibt. simplexml_load_file ist eine Funktion, die in PHP 5 eingeführt wurde. Enthält die XML-Datei einen Fehler, gibt sie FALSE zurück. Ansonsten wandelt die Funktion unsere XML-Datei in ein Objekt um.

Wenn unsere XML-Datei existiert und well-formed (valid) ist, wird sie also als Objekt in $XML_Data geladen.

Abgleichen vom übergebenem Alias mit der Whitelist


Damit unsere Klasse anfängt zu arbeiten, müssen wir dieser den momentanen Alias übergeben. Das geschieht mittels der public-Funktion SetPageAlias(). Ausserdem gibt es eine weitere Funktion, die prüft ob der eingegebene Alias in der XML-Liste auftaucht. Ist dies der Fall, so werden alle Informationen in das Array $PageDetail geschrieben.

PHP Quellcode:
class C_ContentManager {

// [...]

public function SetPageAlias($alias) {
        if(!$this->SetPageDetailsByAlias($alias)) {
          // Alias in XML nicht gefunden
          $this->SetPageDetailsByAlias($this->ErrorPageAlias);
        }
    }
   
private function SetPageDetailsByAlias($alias) {
       
        $Alias_XPath = '/whitelist/entry[@alias=\''.$alias.'\']';
        if(!$result = $this->XML_Data->xpath($Alias_XPath)) {
          return FALSE;
        } else {
          $this->PageDetail['url'] = $result[0]->url;
          $this->PageDetail['title'] = $result[0]->title;
          $this->PageDetail['meta'] = $result[0]->meta;
          $this->PageDetail['alias'] = $alias;
         
          return TRUE;
        }
    }
}

Erklärung: Die Funktion SetPageAlias nimmt den Alias entgegen, und prüft ihn mittels der Funktion SetPageDetailsByAlias: Gibt diese FALSE zurück (Alias in XML-Datei nicht gefunden), wird die anfangs deklarierte Error-Datei eingebunden ($ErrorPageAlias).
Etwas ungewohnt sehen auch die ersten beiden Zeilen der Funktion SetPageDetailsByAlias aus: In $Alias_XPath ist der "Pfad" gespeichert, der in der XML-Datei auf unser Attribut "alias" mit dem Wert des angefragten Alias zeigt. Im Falle des Aliases 'impressum' sieht der Pfad beispielsweise so aus:
Code:
/whitelist/entry[@alias='impressum']

xpath() liefert uns dann die Werte der Nodes zurück. Wie geschaffen für unser Vorhaben. xpath liefert FALSE, wenn das Element nicht gefunden wurde. Ist das der Fall, liefert SePageDetailsbyAlias also auch FALSE zurück.

Ausgabe angefragter Seiteninformationen


Wir sind fast fertig. Es fehlt lediglich eine Funktion, die Anfragen über Seiteninformationen (URL, Meta-Infos etc.) entgegennimmt und die entsprechenden Werte zurückgibt. Die heisst hier GetPageDetail:

PHP Quellcode:
class C_ContentManager {

// [...]
   
public function GetPageDetail($detail) {
      empty($this->PageDetail) ? $this->SetPageAlias($this->StartPageAlias) : 0;
      return $this->PageDetail[$detail];
    }
}

GetPageDetail empfängt die angefragte Information zur aktuellen Seite. Doch zuvor muss geprüft werden, ob im Vorfeld bereits einmal die Funktion SetPageAlias aufgerufen wurde. Denn wenn nichts gesetzt wurde, kann auch (normalerweise) nichts empfangen werden. Ist dies der Fall, so wird automatisch die Startseite gesetzt. All das wird in der ersten Zeile der Funktion mit einer verkürzten if-Abfrage geregelt.

Die ganze Klasse im Überblick


Und das war es schon. Hier nochmal die Klasse im Überblick:

PHP Quellcode:
class C_ContentManager {

    private $PageDetail = array();
    private $ErrorPageAlias = 'error';
    private $StartPageAlias = 'welcome';
    private $XML_Data;
   
    function __construct($XMLPageIndexFile = 'content.xml') {
      // XML einbinden
        if(file_exists($XMLPageIndexFile)) {
          if(!@$this->XML_Data = simplexml_load_file($XMLPageIndexFile)) {
            die ('Fehler beim Laden von ' . $XMLPageIndexFile);
          }
        } else {
          die ('Die Datei ' .$XMLPageIndexFile . ' existiert nicht!');
        }
    }

    public function SetPageAlias($alias) {
        if(!$this->SetPageDetailsByAlias($alias)) {
          // Alias in XML nicht gefunden
          $this->SetPageDetailsByAlias($this->ErrorPageAlias);
        }
    }
   
    private function SetPageDetailsByAlias($alias) {
       
        $Alias_XPath = '/whitelist/entry[@alias=\''.$alias.'\']';
        if(!$result = $this->XML_Data->xpath($Alias_XPath)) {
          return FALSE;
        } else {
          $this->PageDetail['url'] = $result[0]->url;
          $this->PageDetail['title'] = $result[0]->title;
          $this->PageDetail['meta'] = $result[0]->meta;
          $this->PageDetail['alias'] = $alias;
         
          return TRUE;
        }
    }
   
    public function GetPageDetail($detail) {
      empty($this->PageDetail) ? $this->SetPageAlias($this->StartPageAlias) : 0;
      return $this->PageDetail[$detail];
    }
}


Die Whitelist in der Praxis


Jetzt geht's um die Handhabung der Klasse. Wir bauen jetzt eine fiktive Seite (index.php), die die praktische Handhabung der Klasse deutlich macht:

PHP Quellcode:
<?php
  ini_set('display_errors', '1');
 
  error_reporting(E_ALL | E_STRICT);
 
  require_once('include/class_CM.inc.php'); //Unsere Klasse
  $content = new C_ContentManager();
 
 
  // Wenn load gesetzt ist und einen Wert (Alias) enthält, wird er per SetPageAlias übergeben
  if(isset($_GET['load']) && $_GET['load']) {
    $content->SetPageAlias($_GET['load']);
  }
 
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" />
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title><?php echo $content->GetPageDetail('title'); ?></title>
<meta name="description" content="<?php echo $content->GetPageDetail('meta'); ?>" />
</head>
<body>
<div id="navi">
<ul>
  <li><a href="index.php?load=welcome">Startseite</a></li>
  <li><a href="index.php?load=news">News</a></li>
  <li><a href="index.php?load=impressum">Impressum</a></li>
</ul>
</div>
<div id="content">
    <?php    
      include($content->GetPageDetail('url'));
      // Wenn kein Alias gesetzt ist, bekommt man von GetPageDetail() die Startseite zurück
    ?>
    
  </div>
</body>
</html>

Es wird deutlich, dass die Klasse sehr einfach zu bedienen ist und trotzdem sehr flexibel ist.

Weitere Tipps und Informationen


Weitere Möglichkeiten

  • Ich habe in meine error.php (die geladen wird, wenn ein Alias nicht gefunden wurde) eine Funktion gesetzt, die den Versuch, einen nichtvorhandenen Alias aufzurufen, in eine MySQL-Datenbank loggt. So kann ich einerseits sehen, ob man durch einen Link nicht auf eine bestimmte Seite kommt bzw. ob jemand versucht hat, eine Code Injection vorzunehmen.
  • Wie anfangs angeschnitten, kann man nun verschiedene Sprachen mit diesem System verwalten. Das würde dann in der Praxis beispielsweise so aussehen:
    PHP Quellcode:
    <?php
    if(isset($_SESSION['lang'])) {
      switch($_SESSION['lang']) {
        case 'en':
          $xml = 'content_en.xml';
          break;
       
        case 'fr':
          $xml = 'content_fr.xml';
          break;
         
        default:
          $xml = 'content.xml';
      }
    $content = new C_ContentManager($xml);
    } else {
    $content = new C_ContentManager();

    }

    ?>

Links


Hier einige Links, die evntl. hilfreich sind:
Wiki zum Thema XML
simplexml auf php.net
Ausführliche Einführung in simplexml


Mitwirkende: dsentker
Erstellt von dsentker, 06.03.2009 am 15:14
Zuletzt bearbeitet von dsentker, 11.03.2009 am 13:41
1 Kommentare , 6842 Betrachtungen

Dieser Text steht unter der GNU-Lizenz für freie Dokumentation


 

Lesezeichen

Artikel-Optionen
Ansicht

Forumregeln
Es ist Ihnen nicht erlaubt, neue Themen zu verfassen.
Es ist Ihnen nicht erlaubt, auf Beiträge zu antworten.
Es ist Ihnen nicht erlaubt, Anhänge hochzuladen.
Es ist Ihnen nicht erlaubt, Ihre Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.

Gehe zu
Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
SQL-Injection gh1234 PHP 5 14.08.2008 18:13
SQL Injection Benjamin Such PHP 7 04.02.2008 11:16
E-Mail Whitelist: Anmelden Innocentus Sonstiges 3 08.11.2007 19:51
schutz gegen SQL - Injection? reaperchef PHP 1 04.02.2007 15:59
eval() mit whitelist BurninLeo PHP 2 29.09.2005 11:05


Alle Zeitangaben in WEZ +2. Es ist jetzt 08:57 Uhr.


Powered by vBulletin® Version 3.8.8 (Deutsch)
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Powered by NuWiki v1.3 RC1 Copyright ©2006-2007, NuHit, LLC