<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/security.database.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'de',
  ),
  'this' => 
  array (
    0 => 'security.database.sql-injection.php',
    1 => 'SQL-Injection',
    2 => 'SQL-Injection',
  ),
  'up' => 
  array (
    0 => 'security.database.php',
    1 => 'Datenbank-Sicherheit',
  ),
  'prev' => 
  array (
    0 => 'security.database.storage.php',
    1 => 'Verschl&uuml;sseltes Speichermodell',
  ),
  'next' => 
  array (
    0 => 'security.errors.php',
    1 => 'Fehlerbehandlung',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'de',
    'path' => 'security/database.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="security.database.sql-injection" class="sect1">
    <h2 class="title">SQL-Injection</h2>
    <p class="simpara">
     SQL-Injection ist eine Technik, bei der ein Angreifer Schwachstellen im
     Anwendungscode ausnutzt, mit dem dynamische SQL-Abfragen erstellt werden.
     Ein Angreifer kann sich Zugang zu besonders geschützten Bereichen der
     Anwendung verschaffen, sämtliche Informationen aus der Datenbank abrufen,
     vorhandene Daten manipulieren oder sogar gefährliche Befehle auf der
     Systemebene des Datenbank-Hosts ausführen. Die Schwachstelle entsteht,
     wenn Entwickler in ihren SQL-Anweisungen beliebige Eingaben miteinander
     verknüpfen oder einfügen.
    </p>
    <p class="para">
     <div class="example" id="example-1">
      <p><strong>Beispiel #1 
       Die Ergebnisliste in mehrere Seiten aufsplitten ... und Superuser anlegen
       (PostgreSQL)
      </strong></p>
      <div class="example-contents"><p>
       Im folgenden Beispiel wird die Benutzereingabe direkt in die
       SQL-Abfrage eingefügt, wodurch der Angreifer ein Superuser-Konto für
       die Datenbank erlangen kann.
      </p></div>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$offset </span><span style="color: #007700">= </span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'offset'</span><span style="color: #007700">]; </span><span style="color: #FF8000">// Vorsicht, keine Validierung der Eingabe!<br /></span><span style="color: #0000BB">$query  </span><span style="color: #007700">= </span><span style="color: #DD0000">"SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET </span><span style="color: #0000BB">$offset</span><span style="color: #DD0000">;"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">pg_query</span><span style="color: #007700">(</span><span style="color: #0000BB">$conn</span><span style="color: #007700">, </span><span style="color: #0000BB">$query</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

     </div>
     Normale Benutzer klicken auf die &#039;nächste&#039;- bzw. &#039;vorige&#039;-Links, wo der
     <var class="varname">$offset</var> in der <abbr title="Uniform Resource Locator">URL</abbr> enthalten ist.
     Das Skript erwartet, dass der ankommende <var class="varname">$offset</var> eine
     Zahl enthält. Was aber, wenn jemand versucht einzubrechen, indem er das
     Folgende an die <abbr title="Uniform Resource Locator">URL</abbr> anhängt:
     <div class="informalexample">
      <div class="example-contents">
<div class="sqlcode"><pre class="sqlcode">0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
    select &#039;crack&#039;, usesysid, &#039;t&#039;,&#039;t&#039;,&#039;crack&#039;
    from pg_shadow where usename=&#039;postgres&#039;;
--</pre>
</div>
      </div>

     </div>
     In diesem Fall würde das Skript dem Angreifer einen Superuser-Zugang zur
     Verfügung stellen. Beachten Sie, dass <code class="literal">0;</code> einen
     gültigen Offset zur ursprünglichen Abfrage liefert, und sie beendet.
    </p>
    <blockquote class="note"><p><strong class="note">Hinweis</strong>: 
     <p class="para">
      Es ist eine übliche Technik, den SQL-Parser mit dem SQL-Kommentarzeichen
      <code class="literal">--</code> zu zwingen, den Rest der vom Entwickler
      geschriebenen Abfrage zu ignorieren.
     </p>
    </p></blockquote>
    <p class="para">
     Eine gangbare Methode, an Passwörter zu gelangen, ist, Ihre Seiten mit
     den Suchergebnissen zu umgehen. Der Angreifer braucht nur zu probieren,
     ob irgendeine übertragene Variable, die in dem SQL-Statement verwendet
     wird, nicht richtig gehandhabt wird. Diese Filter können gewöhnlich in
     einem vorausgehenden Formular gesetzt werden, indem <code class="literal">WHERE-,
     ORDER BY-, LIMIT-</code> und <code class="literal">OFFSET</code>-Klauseln in
     <code class="literal">SELECT</code>-Statements angepasst werden. Wenn Ihre
     Datenbank das <code class="literal">UNION</code>-Konstrukt unterstützt, kann der
     Angreifer versuchen, eine komplette Abfrage an das Original anzuhängen,
     um Passwörter aus einer beliebigen Tabelle aufzulisten. Es wird dringend
     empfohlen, nur sichere Hashes von Passwörtern zu speichern und nicht die
     Passwörter selbst.
     <div class="example" id="example-2">
      <p><strong>Beispiel #2 
       Artikel auflisten ... und ein paar Passwörter (beliebiger Datenbankserver)
      </strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$query  </span><span style="color: #007700">= </span><span style="color: #DD0000">"SELECT id, name, inserted, size FROM products<br />           WHERE size = '</span><span style="color: #0000BB">$size</span><span style="color: #DD0000">'"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">odbc_exec</span><span style="color: #007700">(</span><span style="color: #0000BB">$conn</span><span style="color: #007700">, </span><span style="color: #0000BB">$query</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

     </div>
     Der statische Teil der Abfrage kann mit einem anderen
     <code class="literal">SELECT</code>-Statement kombiniert werden, welches alle
     Passwörter preisgibt
     <div class="informalexample">
      <div class="example-contents">
<div class="sqlcode"><pre class="sqlcode">&#039;
union select &#039;1&#039;, concat(uname||&#039;-&#039;||passwd) as name, &#039;1971-01-01&#039;, &#039;0&#039; from usertable;
--</pre>
</div>
      </div>

     </div>
    </p>
    <p class="para">
     Auch <code class="literal">UPDATE</code>- und <code class="literal">INSERT</code>-Anweisungen
     sind für derartige Angriffe anfällig.
     <div class="example" id="example-3">
      <p><strong>Beispiel #3 
       Vom Zurücksetzen eines Passworts ... zum Erlangen von mehr Rechten
       (beliebiger Datenbankserver)
      </strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br />$query </span><span style="color: #007700">= </span><span style="color: #DD0000">"UPDATE usertable SET pwd='</span><span style="color: #0000BB">$pwd</span><span style="color: #DD0000">' WHERE uid='</span><span style="color: #0000BB">$uid</span><span style="color: #DD0000">';"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

     </div>
     Wenn ein böswilliger Benutzer den Wert <code class="literal">&#039; or uid
     like&#039;%admin%</code> an <var class="varname">$uid</var> übermittelt, um das
     Administrator-Passwort zu ändern, oder einfach <var class="varname">$pwd</var>
     auf <code class="literal">hehehe&#039;, trusted=100, admin=&#039;yes</code> setzt, um mehr
     Rechte zu erhalten, dann wird die Abfrage verdreht:
     <div class="informalexample">
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #FF8000">// $uid: ' or uid like '%admin%<br /></span><span style="color: #0000BB">$query </span><span style="color: #007700">= </span><span style="color: #DD0000">"UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%';"</span><span style="color: #007700">;<br /><br /></span><span style="color: #FF8000">// $pwd: hehehe', trusted=100, admin='yes<br /></span><span style="color: #0000BB">$query </span><span style="color: #007700">= </span><span style="color: #DD0000">"UPDATE usertable SET pwd='hehehe', trusted=100, admin='yes' WHERE<br />...;"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

     </div>
    </p>
    <p class="simpara">
     Obwohl es offensichtlich ist, dass ein Angreifer zumindest ein wenig
     Kenntnis der genutzten Datenbankarchitektur besitzen muss, um einen
     erfolgreichen Angriff durchzuführen, ist es oft sehr einfach, diese
     Informationen zu erhalten. Der Code kann z. B. Teil einer
     Open-Source-Software sein und öffentlich zugänglich sein. Diese
     Informationen können auch durch Closed-Source-Code preisgegeben werden -
     selbst wenn dieser kodiert, verschleiert oder kompiliert ist - und sogar
     durch Ihren eigenen Code durch die Anzeige von Fehlermeldungen. Andere
     Methoden beinhalten die Nutzung typischer Tabellen- und Spaltennamen. Ein
     Login-Formular etwa, dass eine Tabelle &#039;users&#039; mit den Spaltennamen &#039;id&#039;,
     &#039;username&#039; und &#039;password&#039; nutzt.
    </p>
    <p class="para">
     <div class="example" id="example-4">
      <p><strong>Beispiel #4 Angriff auf das Betriebssystem des Datenbank-Hosts (MSSQL-Server)</strong></p>
      <div class="example-contents"><p>
       Ein erschreckendes Beispiel, wie der Zugriff auf Befehle auf
       Betriebssystemebene bei manchen Datenbankservern erfolgen kann:
      </p></div>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$query  </span><span style="color: #007700">= </span><span style="color: #DD0000">"SELECT * FROM products WHERE id LIKE '%</span><span style="color: #0000BB">$prod</span><span style="color: #DD0000">%'"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">mssql_query</span><span style="color: #007700">(</span><span style="color: #0000BB">$query</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

     </div>
     Wenn ein Angreifer den Wert <code class="literal">a%&#039; exec master..xp_cmdshell &#039;net
     user test testpass /ADD&#039; --</code> auf <var class="varname">$prod</var>
     überträgt, wird <var class="varname">$query</var> zu:
     <div class="informalexample">
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$query  </span><span style="color: #007700">= </span><span style="color: #DD0000">"SELECT * FROM products<br />           WHERE id LIKE '%a%'<br />           exec master..xp_cmdshell 'net user test testpass /ADD' --%'"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">mssql_query</span><span style="color: #007700">(</span><span style="color: #0000BB">$query</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

     </div>
     Der MSSQL-Server führt die SQL-Statements im Batch aus, inklusive eines
     Kommandos um einen neuen Benutzer zur Datenbank der lokalen Konten
     hinzuzufügen. Würde diese Anwendung als <code class="literal">sa</code> und der
     MSSQLSERVER-Service mit genügend Rechten laufen, hätte der Angreifer nun
     ein Konto, mit welchem er Zugriff auf diese Maschine hätte.
    </p>
    <blockquote class="note"><p><strong class="note">Hinweis</strong>: 
     <p class="para">
      Manche der obigen Beispiele sind an einen spezifischen Datenbankserver
      gebunden, aber das bedeutet nicht, dass ein ähnlicher Angriff auf andere
      Produkte unmöglich wäre. Ihr Datenbankserver könnte auf andere Weise
      genauso verwundbar sein.
     </p>
    </p></blockquote>
    <p class="para">
     <div class="mediaobject">
      
      <div class="imageobject">
       <img src="images/cecce51a7b8d511715c8589036b98463-xkcd-bobby-tables.png" alt="Ein lustiges Beispiel für die Problematik der SQL-Injection" width="666" height="205" />
      </div>
      <div class="caption">
       <p class="simpara">
        Bild mit freundlicher Genehmigung von
        <a href="http://xkcd.com/327" class="link external">&raquo;&nbsp;xkcd</a>
       </p>
      </div>
     </div>
    </p>

    <div class="sect2" id="security.database.avoiding">
     <h3 class="title">Techniken zur Vermeidung</h3>
     <p class="para">
      Um das Risiko einer SQL-Injection zu verringern, wird empfohlen, alle
      Daten über vorbereitete Anweisungen zu binden. Die Verwendung von
      parametrisierten Abfragen ist zwar nicht ausreichend, um SQL-Injections
      vollständig zu verhindern, aber es ist der einfachste und sicherste Weg,
      um Daten für SQL-Anweisungen bereitzustellen. Alle dynamischen
      Datenliterale in <code class="literal">WHERE</code>-, <code class="literal">SET</code>- und
      <code class="literal">VALUES</code>-Klauseln müssen durch Platzhalter ersetzt
      werden. Die eigentlichen Daten werden bei der Ausführung gebunden und
      getrennt vom SQL-Befehl gesendet.
     </p>
     <p class="para">
      Die Parameterbindung kann nur für Daten verwendet werden. Andere
      dynamische Teile der SQL-Abfrage müssen gegen eine bekannte Liste
      zulässiger Werte gefiltert werden.
     </p>
     <p class="para">
      <div class="example" id="example-5">
       <p><strong>Beispiel #5 Ein sicherer Weg, eine Abfrage zu erstellen</strong></p>
       <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #FF8000">// Der dynamische SQL-Teil wird anhand der erwarteten Werte validiert<br /></span><span style="color: #0000BB">$sortingOrder </span><span style="color: #007700">= </span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'sortingOrder'</span><span style="color: #007700">] === </span><span style="color: #DD0000">'DESC' </span><span style="color: #007700">? </span><span style="color: #DD0000">'DESC' </span><span style="color: #007700">: </span><span style="color: #DD0000">'ASC'</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$productId </span><span style="color: #007700">= </span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'productId'</span><span style="color: #007700">];<br /></span><span style="color: #FF8000">// Das SQL wird mit einem Platzhalter vorbereitet<br /></span><span style="color: #0000BB">$stmt </span><span style="color: #007700">= </span><span style="color: #0000BB">$pdo</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">prepare</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT * FROM products WHERE id LIKE ? ORDER BY price </span><span style="color: #007700">{</span><span style="color: #0000BB">$sortingOrder</span><span style="color: #007700">}</span><span style="color: #DD0000">"</span><span style="color: #007700">);<br /></span><span style="color: #FF8000">// Der Wert wird mit LIKE-Wildcards versehen<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">([</span><span style="color: #DD0000">"%</span><span style="color: #007700">{</span><span style="color: #0000BB">$productId</span><span style="color: #007700">}</span><span style="color: #DD0000">%"</span><span style="color: #007700">]);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
       </div>

      </div>
     </p>

     <p class="simpara">
      Vorbereitete Anweisungen werden von
      <a href="pdo.prepared-statements.php" class="link">PDO</a>,
      <a href="mysqli.quickstart.prepared-statements.php" class="link">MySQLi</a> und
      anderen Bibliotheken angeboten.
     </p>

     <p class="simpara">
      Angriffe durch SQL-Injection basieren hauptsächlich darauf, Code
      auszunutzen, der nicht unter dem Aspekt der Sicherheit geschrieben
      wurde. Vertrauen Sie niemals einer Eingabe, insbesondere nicht von der
      Clientseite, selbst wenn sie von einer Auswahlbox, einem versteckten
      Eingabefeld oder einem Cookie kommt. Das erste Beispiel zeigt, dass eine
      so einfache Abfrage zu einer Katastrophe führen kann.
     </p>

     <p class="para">
      Eine umfassende Verteidigungsstrategie umfasst mehrere bewährte
      Programmierpraktiken:
      <ul class="itemizedlist">
       <li class="listitem">
        <span class="simpara">
         Stellen Sie nie als Superuser oder Eigentümer einer Datenbank eine
         Verbindung zur Datenbank her. Verwenden Sie immer speziell angelegte
         Benutzer mit sehr limitierten Rechten.
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         Prüfen Sie, ob die gegebene Eingabe dem erwarteten Datentyp
         entspricht. <abbr title="PHP: Hypertext Preprocessor">PHP</abbr> bietet eine große Auswahl an
         Funktionen zum Validieren der Eingaben, von den einfachsten unter
         <a href="ref.var.php" class="link">Variablenfunktionen</a> und
         <a href="ref.ctype.php" class="link">Character-Type-Funktionen</a> (z. B.
         <span class="function"><a href="function.is-numeric.php" class="function">is_numeric()</a></span>, bzw.
         <span class="function"><a href="function.ctype-digit.php" class="function">ctype_digit()</a></span>), bis hin zu den
         <a href="ref.pcre.php" class="link">Perl-kompatiblen regulären Ausdrücken</a>.
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         Wenn die Anwendung eine numerische Eingabe erwartet, sollten Sie die
         Daten mittels <span class="function"><a href="function.ctype-digit.php" class="function">ctype_digit()</a></span> überprüfen, den Typ
         stillschweigend mittels <span class="function"><a href="function.settype.php" class="function">settype()</a></span> ändern oder
         deren numerische Darstellung mittels <span class="function"><a href="function.sprintf.php" class="function">sprintf()</a></span>
         verwenden.
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         Unterstützt die Datenbank-Ebene das Binden von Variablen nicht, so
         maskieren Sie jede nichtnumerische Benutzereingabe, welche zur
         Datenbank weitergereicht werden soll, mit der jeweiligen
         datenbankspezifischen Maskierungsfunktion für Zeichenketten (z. B.
         <span class="function"><a href="function.mysql-real-escape-string.php" class="function">mysql_real_escape_string()</a></span>,
         <span class="function"><strong>sqlite_escape_string()</strong></span> usw.). Generische Funktionen
         wie <span class="function"><a href="function.addslashes.php" class="function">addslashes()</a></span> sind nur in einer sehr
         spezifischen Umgebung nützlich (z. B. MySQL mit einem
         SingleByte-Zeichensatz bei deaktiviertem
         <var class="varname">NO_BACKSLASH_ESCAPES</var>), sodass es besser ist, diese
         zu vermeiden.
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         Geben Sie um keinen Preis irgendwelche datenbankspezifischen
         Informationen aus, speziell über das Schema. Siehe auch
         <a href="security.errors.php" class="link">Fehlerbehandlung</a> und
         <a href="ref.errorfunc.php" class="link">Fehlerbehandlungsfunktionen</a>.
        </span>
       </li>
      </ul>
     </p>

     <p class="simpara">
      Abgesehen davon profitieren Sie von einer Protokollierung der Abfragen
      entweder in Ihrem Skript oder durch die Datenbank selbst, falls sie
      Protokollierung unterstützt. Klar, die Protokollierung kann keinen
      schädlichen Versuch verhindern, aber sie kann helfen herauszufinden,
      welche Anwendung umgangen wurde. Das Protokoll ist nicht von sich aus
      nützlich, aber durch die in ihm enthaltenen Informationen, und je mehr
      Details es enthält, desto besser ist es im Allgemeinen.
     </p>
    </div>
   </div><?php manual_footer($setup); ?>