Skip to main content

5 Möglichkeiten, PHP-Code-Injection zu verhindern

Artikel von
Headshot of DeveloperSteve Coochin

DeveloperSteve Coochin

social php security

29. Juni 2021

0 Min. Lesezeit

Anknüpfend an meinen vorherigen Beitrag zum Testen auf PHP-Composer-Sicherheitslücken möchte ich Ihnen mit diesem Beitrag einige hilfreiche Tipps geben, wie Sie sicherere Anwendungen entwickeln und PHP-Code-Injection verhindern können. Als Entwickler entwickeln wir Apps, die den Alltag der Endnutzer erleichtern. Ob Unterhaltungs-, Arbeitsplatz- oder soziale Netzwerkanwendung: Unser oberstes Ziel ist es, die Nutzer zu schützen, für die wir entwickeln, indem wir die Sicherheit direkt in den Code integrieren. Sichere Programmierpraktiken schützen nicht nur die Endnutzer, sondern sparen auch Zeit und Entwicklungskosten, da Nacharbeiten vermieden werden.

Was ist Code-Injection?

Code-Injection ist ein Angriff, bei dem eine schädliche Code-Payload über einen anfälligen Angriffsvektor eingeschleust wird. Ziel ist es, die Integrität der vorgesehenen Zielanwendung zu kompromittieren. Angreifer können ausführbaren PHP-Code oder JavaScript einschleusen, der entweder zur Laufzeit der Anwendung oder im Browser des Endnutzers ausgeführt wird. Wichtig ist, dass PHP keinen speziell für JavaScript vorgesehenen Escaper bietet, was ebenfalls problematisch sein kann. Die Schwachstelle wird in der PHP-Laufzeitumgebung verarbeitet und der Code entweder serverseitig oder im Browser des ahnungslosen Nutzers ausgeführt.

So verhindern Sie Code-Injection in PHP

Sichere Softwareentwicklung beginnt direkt beim Code mit der Einführung einer Secure-Coding-Konvention. Achten Sie auf öffentlich zugängliche Eingaben und deren Verarbeitung. Bereinigen Sie sämtliche Eingaben und behalten Sie im Blick, wie sie in Ihrer Anwendung gespeichert werden.

Als Grundregel gilt: Dynamische Codeausführung sollte in keiner Anwendung erlaubt sein. Vermeiden Sie beispielsweise nach Möglichkeit zentrale PHP-Funktionen wie shell_exec() und exec(), die auf Betriebssystemebene ausgeführt werden.

Es wird dringend empfohlen, stets ein Open-Source-Sicherheitsscanning-Tool wie Snyk Open Source zu verwenden. Es scannt Ihren Paketmanager, um potenzielle Schwachstellen in verwendeten Bibliotheken und Abhängigkeiten zu identifizieren. Außerdem prüfe ich täglich die Composer-Datenbank für Snyk Vulnerability Database, um mich mit den verwendeten Techniken vertraut zu machen. Auf den ausführlichen Berichtsseiten können Sie bis auf Codeebene zu den Repositories zurückverfolgen, wie die Schwachstellen erkannt wurden.

5 Möglichkeiten, Code-Injection bei der PHP-App-Entwicklung zu verhindern

  1. Vermeiden Sie exec(), shell_exec(), system() und passthru()

  2. Verwenden Sie strip_tags() nicht zur Bereinigung

  3. Code-Serialisierung

  4. Verwenden Sie einen PHP-Security-Linter

  5. Erkennen Sie Code-Injection-Probleme mit einem SAST-Tool

1. Vermeiden Sie exec(), shell_exec(), system() und passthru()

Wie man so schön sagt: „Hier lauern Drachen.“ Grundsätzlich sollten Sie nach Möglichkeit alles vermeiden, was direkt aus PHP die Betriebsumgebung aufrufen kann. Aus Sicht der Angriffsvektoren eröffnen sich dadurch zahlreiche Möglichkeiten, direkt in Ihrem Stack verschiedenste Probleme zu verursachen.

Traditionell wurden Funktionen wie exec(), shell_exec(), system(), und passthru() für Aufgaben wie das Komprimieren oder Dekomprimieren von Dateien, das Erstellen von Cron-Jobs und sogar das Durchsuchen von Betriebssystemdateien und -ordnern verwendet. Sie werden alle ohne integrierte Codebereinigung ausgeführt. Genau hier beginnen die Probleme, besonders wenn direkte Nutzereingaben weder validiert noch bereinigt wurden.

PHP bietet einige Funktionsoperatoren mit integrierter Eingabe-Escapierung, nämlich escapeshellcmd() und escapeshellarg(). Diese escapen und bereinigen Eingaben bis zu einem gewissen Grad im Rahmen des Funktionsaufrufs. Dieselbe Funktionalität lässt sich jedoch auch auf sicherere Weise umsetzen.

Seit PHP 7.4 kann die Archivierung mit der Klasse ZipArchive erfolgen, die beim Kompilieren von PHP mit dem Flag --with-zip aktiviert wird. Dabei ist besondere Vorsicht geboten, da dies weiterhin einen Directory-Traversal-Angriff ermöglichen kann.

Setzen Sie bei der Interaktion mit dem Dateisystem auf programmatische Ansätze und minimieren Sie die Möglichkeiten, direkt über Eingaben darauf zuzugreifen. PHP bietet Dateifunktionen, die Sie ohne direkten Aufruf des Betriebssystems verwenden können. Eine Liste der Dateien in einem bestimmten Verzeichnis lässt sich beispielsweise mit der Funktion scandir() abrufen. Sie gibt ein Array mit den verfügbaren Dateien zurück, die anschließend im Code geöffnet oder referenziert werden können. Achten Sie darauf, nicht auf Dateien zuzugreifen, die durch offengelegte Eingaben erstellt wurden. Seien Sie auch bei Funktionen für dateibasierte Berechtigungen wie chmod() und chown() vorsichtig. Vertrauen Sie außerdem nicht dem Content-Header, da dieser gefälscht werden kann, und validieren Sie den Dateityp.

Erstellen Sie Cron-Jobs dynamisch mit einer Composer-Bibliothek wie cron. Diese wird tatsächlich über einen Cron-Job ausgeführt, der dann im Rahmen der gesamten Cron-Laufzeit seine eigenen Cron-Jobs startet. Achten Sie weiterhin darauf, was darin ausgeführt wird. Dieser Ansatz erstellt Prozesse jedoch auf programmatischere Weise, ohne den Zugriff auf das Betriebssystem offenzulegen.

2. Verwenden Sie strip_tags() nicht zur Bereinigung

Bei der Bereinigung und Verarbeitung von Nutzereingaben ist stets besondere Vorsicht geboten. Ziel ist es, gültige Nutzereingaben anzunehmen und sie anschließend korrekt zu speichern und zu verarbeiten, damit Ihre Anwendung nicht anfällig wird. Denken Sie daran: Eingaben sind ein offener Angriffsvektor, über den böswillige Akteure mit Ihrer Anwendung interagieren können. Nehmen Sie sich daher immer die Zeit, Eingabedaten sorgfältig zu verarbeiten.

Die Funktion strip_tags() wurde in erster Linie dafür entwickelt, HTML und PHP aus bereitgestellten Eingaben zu entfernen. JavaScript, SQL und andere potenziell schädliche Eingaben werden von der Funktion daher als gültig angesehen. htmlentities() ist eine weitere häufig verwendete Option zur Bereinigung von Eingaben. Dabei lassen sich UTF-Zeichensätze festlegen. Beachten Sie jedoch, dass auch damit Eingaben nicht vollständig bereinigt werden.

Daten lassen sich auch mit einer Regex-Funktion wie preg_replace('/[^a-zA-Z0-9]+/', '', $text) reduzieren. Für UTF-8-Zeichensätze gibt diese Funktion nur Buchstaben und Ziffern aus der Eingabe zurück. Bei der Bereinigung von Nutzereingaben sollten Sie auch mit Funktionen wie mb_strtolower() vorsichtig sein. In der Vergangenheit sind bei einigen Multibyte-String-Funktionen (mb_) Schwachstellen aufgetreten, beispielsweise CVE-2020-7065: eine Out-of-Bounds-Schreibschwachstelle, die durch UTF-32LE-Kodierung verursacht wird. Dies kann zu einem Stack-Buffer-Overflow und einem Absturz führen und die Ausführung von Code ermöglichen.

Verwenden Sie stattdessen beispielsweise filter_var(), um Eingaben anhand der festgelegten Filteroptionen zu validieren und zu bereinigen. Werden etwa FILTER_SANITIZE_STRING und FILTER_FLAG_STRIP_HIGH zusammen mit der Nutzereingabe an die Funktion filter_var() übergeben, werden alle HTML-Tags und alle Zeichen mit einem ASCII-Wert von > 127 entfernt.

Für die Bereinigung von Eingaben gibt es auch einige häufig verwendete Composer-Bibliotheken. Eine Bibliothek wie HTML purifier bietet eine HTML-konforme Bereinigung mit einer recht umfangreichen Positivliste, die Sie an die Art der Eingabedaten anpassen können, die Ihre Anwendung akzeptieren soll.

3. Vermeiden Sie unserialize() in PHP

Dieses Thema könnte ohne Weiteres einen eigenen Blogbeitrag füllen. Es wurde im Laufe der Jahre intensiv diskutiert, auch unter den Entwicklern, die an PHP arbeiten. Das PHP-Handbuch weist ausdrücklich auf die Gefahren der Funktion unserialize() hin. Insbesondere sollte ihr „keine nicht vertrauenswürdige Nutzereingabe übergeben werden“, da dies dazu führen kann, dass „Code geladen und ausgeführt wird“.

Unserialize() dient dazu, eine Klasse in einen String umzuwandeln, der gespeichert, an andere Funktionen übergeben oder zur späteren Verwendung zwischengespeichert werden kann. Das klingt zunächst relativ harmlos. Doch sobald man versteht, wie der zugrunde liegende C-Code beim Speichern von unserialize()-Daten im Arbeitsspeicher funktioniert, wird klar, wo die meisten Probleme entstehen können.

Es wird dringend empfohlen, ein standardisiertes Datenformat wie JSON mit json_decode() und json_encode() zu verwenden. So können serialisierte Daten sicher und bereinigt an Nutzer gesendet und von ihnen empfangen werden.

4. Verwenden Sie einen PHP-Security-Linter

Die richtigen Tools in Ihrem Entwicklungs-Workflow tragen von Anfang an zu sicherem und funktionsfähigem Code bei. Mit Lintern lassen sich Fehler und potenzielle Probleme bei der Entwicklung von PHP-Anwendungen reduzieren und Schwachstellen im Quellcode vermeiden.

Es wird außerdem dringend empfohlen, die Anzeige von Fehlern standardmäßig in den PHP.ini-Konfigurationen zu deaktivieren. Durch das Deaktivieren von error_reporting = E_ALL, ~E_NOTICE und ~E_WARNING werden Fehlermeldungen unterdrückt, die andernfalls Informationen zu Umgebung und Konfiguration Ihrer Anwendung preisgeben könnten.

Die PHP-Sprache verfügt über einen integrierten Linter, der bei der Validierung sehr ausführliche Fehlermeldungen ausgibt. Sie können ihn über die CLI (oder als Teil eines Test-Frameworks) aufrufen, indem Sie PHP -l gefolgt von der zu prüfenden Datei ausführen. Der Nachteil: Es kann jeweils nur eine Datei geprüft werden. Mit einer Schleife lassen sich jedoch mehrere Dateien in einem Durchlauf prüfen.

PHPlint ist eine leistungsstarke und bekanntere Option, um schnell mehrere Dateien zu prüfen. PHPLint lässt sich über die CLI oder als über Composer eingebundene Bibliothek verwenden. Alternativ kann es ganz einfach in ein Docker-Image integriert werden. PHPLint kann PHP 7 und PHP 8 prüfen. Die Ausgabe ist zwar recht ausführlich, doch lassen sich damit mithilfe mehrerer Linting-Verfahren gleichzeitig Probleme erkennen.

Ein weiterer beliebter Linter ist PHP-Parrallel-Lint. Er unterstützt PHP 5.3 bis PHP 8.0 und kann mehrere Linting-Prozesse schneller ausführen, als Sie echo "hello world" eingeben können. PHP-Parrallel-Lint liefert detailliertere Ausgaben als die zuvor genannten Optionen, unterstützt derzeit jedoch weder eine CLI-Option noch eine sofort einsatzbereite Docker-Lösung.

5. Erkennen Sie Code-Injection-Probleme mit einem Tool für statische Anwendungssicherheitstests

Eine der am häufigsten verwendeten IDEs für die PHP-Entwicklung ist PHPStorm. Sie bietet praktische Funktionen zur statischen Codeanalyse. Zusätzlich zu den oben vorgestellten Lintern lässt sich PHPStorm mit Xdebug integrieren. Das eignet sich hervorragend für Linting, Profiling und – wie der Name schon sagt – Debugging, um Fehler zu reduzieren. Für sichere Entwicklungs-Workflows können Sie in PHPStorm bestimmte Programmierregeln und PSR-Standards durchsetzen. Das ist besonders bei der Arbeit an PCI-konformen Anwendungen hilfreich.

Um die Codesicherheit zu verbessern, bietet Snyk ein Plugin für PHPStorm, mit dem sich Codequalitätsprobleme und Schwachstellen im Code und in den verwendeten Bibliotheken erkennen lassen. Es lässt sich einfach installieren und schnell in Betrieb nehmen und ist im Plugin-Bereich der Anwendung verfügbar.

Der PhpStorm-Plugins-Marktplatz mit Suchergebnissen für Snyk Vulnerability Scanner und der Beschreibung der Sicherheitslücken

Ein weiteres Entwicklungstool, das immer häufiger von Entwicklern – und insbesondere von PHP-Entwicklern – genutzt wird, ist VSCode. Wie PHPStorm verfügt es über einen integrierten Linter und ermöglicht die Durchsetzung von PSR-Standards.

Mit Snyk in VSCode nach Schwachstellen suchen ist ganz einfach: Öffnen Sie ein neues VSCode-Terminal und verwenden Sie die Snyk CLI mit snyk test. Alternativ können Sie mit snyk monitor fortlaufende Warnmeldungen zu Ihrer Codebasis erhalten. Weitere Informationen zum Einstieg in die Snyk CLI mit PHP finden Sie in meinem vorherigen Blogbeitrag zum Testen auf PHP-Composer-Sicherheitslücken mit Snyk.

Update: Vor Kurzem haben wir auch PHP-Unterstützung für Snyk Code veröffentlicht. Weitere Informationen und die Möglichkeit, es selbst auszuprobieren, finden Sie im Blogbeitrag Snyk Code unterstützt PHP: Beta für das Scannen auf Schwachstellen startet.

Zusammenfassung

Als Entwickler entwickeln wir Anwendungen, die Endnutzer in ihrem Alltag unterstützen. Wenn Sie diese Anwendungen schützen, stellen Sie nicht nur sicher, dass die Daten der Nutzer sicher sind, sondern stärken auch deren Vertrauen in Sie.

Code-Injection-Schwachstellen bereits beim Entwickeln einer Anwendung direkt im Code zu erkennen, ist einfach – vorausgesetzt, Sie verfügen von Anfang an über die richtigen Tools und Workflows.

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.

Weiterlesen

blog feature snyk container custom base recommendations
Blog

Evo Agentic AppSec: Sicherheit, die mit der KI-Entwicklung Schritt hält

KI-gestützte Entwicklung überholt herkömmliche Security-Workflows. Snyk Agentic AppSec setzt Security-Agenten ein, die Schwachstellen kontinuierlich finden, beheben und überprüfen – während Menschen die Maßstäbe festlegen.

blog feature ai violet
Blog

VulnBump: Können KI-Agenten anfällige Abhängigkeiten beheben, ohne den Build zu beschädigen?

VulnBump testet KI-Agenten anhand von 307 realen Dependency-Upgrades. Eine Lösung zählt nur, wenn die Sicherheitslücke behoben ist und Build sowie Tests weiterhin erfolgreich sind.

Blog

Frontier-Modelle fanden die Schwachstellen. Nur der Angreifer fand die Angriffsketten.

Die statische Analyse fand die Schwachstellen, doch erst Live-Angriffstests bewiesen, wie sie sich zu Angriffen verketten lassen. Ein Vergleich von Evo COS, Claude Security und Claude Code Security.