Anmeldeinformationen in PHP sicher aufbewahren

Eines der schwierigsten Dinge in jeder Art von Anwendung (nicht nur in Webanwendungen) ist der Schutz „geheimer“ Werte. Diese Werte können API-Schlüssel, Datenbankkennwörter oder sogar spezielle Umgehungscodes sein. Idealerweise müssen Sie diese nicht direkt in der Anwendung definieren und können sie aus einer anderen Quelle laden.

Während viele der Probleme beim Schutz von Geheimnissen durch eine bessere Handhabung von Geheimnissen beseitigt werden können, scheint es, als ob immer noch eine Art geheimer Wert in einer Anwendung vorhanden sein muss. Von der Verwendung dieser Art von Muster wird natürlich abgeraten. Die Common Weakness Enumeration Database hat sogar einen Eintrag speziell dazu: CWE-798. Hartcodierte Zugangsdaten, insbesondere im Klartext, können ein großes Risiko darstellen, wenn ein Angreifer irgendwie auf den Code zugreifen und ihn direkt lesen könnte.

Was ist also mit PHP?

In PHP-Anwendungen gibt es ein allgemeines Muster, um Konfigurationswerte und Zugriffsdetails in a zu speichern .env Datei, die sich an einem Ort befindet, an dem die PHP-Anwendung darauf zugreifen kann. Da dies eine gängige Praxis ist, wollte ich nicht zu weit davon abweichen. Ich möchte hier etwas Nützliches bereitstellen, das dieses Setup leicht ersetzen kann und die Dinge dennoch so einfach wie möglich hält.

Ich werde verschiedene Methoden der Speicherung von Anmeldeinformationen durchgehen und einige Zeit damit verbringen, darüber zu sprechen, was es ist und was gut und schlecht daran ist. Sie werden alle einfache Speichermethoden verwenden, entweder basierend auf dem Code oder in einer zugehörigen Flatfile (wie einer .env). Wir beginnen mit der denkbar schlechtesten Methode – dem Speichern der Klartext-Anmeldeinformationen im Code.

Anmeldeinformationen in den Code einfügen

Als Entwickler ist es sehr einfach zu glauben, dass es die einfachste und beste Lösung ist, die Anmeldeinformationen so nah wie möglich an diesem Code zu halten, da Sie die Anmeldeinformationen benötigen, um beispielsweise eine Verbindung über einen HTTP-Client zu einer API herzustellen. An diesem Ansatz sind zwei große Dinge falsch:

  1. Diese hartcodierten Anmeldeinformationen sind nun in Ihrer Versionskontrolle vorhanden. Wenn es sich in einem öffentlichen GitHub-Repository befindet, haben Sie diese Anmeldeinformationen im Grunde nur jedem zugänglich gemacht, der dieses Repository klonen kann.
  2. Wenn ein Angreifer jemals auf den Quellcode Ihrer Anwendung zugreifen könnte, hätte er direkten Zugriff auf die Geheimnisse, ohne irgendeine Art von Entschlüsselung oder Umkehrung vornehmen zu müssen.

WICHTIG: Wenn Sie Creds in Ihren Dateien fest codiert haben, refaktorisieren Sie sie sofort. Dies ist eine schlechte Sicherheitspraxis (und wird in beiden erwähnt CWE-256) und die OWASP Top 10 (als A2) in der Defekte Authentifizierung Artikel.

In den letzten Jahren gab es mehrere Fälle, einige mit Webanwendungen und einige mit anderen Arten von Apps/Hardware, bei denen standardmäßige oder fest codierte Passwörter ihr Untergang waren. VERMEIDEN Sie dies um jeden Preis! Es gibt ein sehr, sehr sehr schlanker Anwendungsfall, um jede Art von vertraulichen Informationen direkt in Ihrem Code zu haben. Selbst dann sind normalerweise andere Schutzmethoden eingebaut, um sicherzustellen, dass diese nur in wenigen Fällen verwendet werden können.

.env Datei, die sich im Dokumentstamm befindet

Nachdem wir nun festgestellt haben, dass wir fest codierte Klartext-Anmeldeinformationen in Ihrem Code vermeiden müssen, müssen wir einen anderen Weg finden, sie zu speichern. Hier ist das beliebte .env Datei eingeht. Seit dem moderneren Zeitalter der PHP-Entwicklung (dank Tools wie Komponist) Viele Frameworks und Bibliotheken haben das Muster der Verwendung von a übernommen .env Datei zum Speichern anwendungsspezifischer Einstellungen. Das bedeutete natürlich, dass schließlich Geheimnisse und sensible Informationen dort hineingeraten sind.

All dies in einer separaten Datei zu haben, ist besser als fest codiert, aber es gibt immer noch einige Probleme. Wenn Sie den Titel dieses Abschnitts noch einmal lesen, finden Sie möglicherweise das Problem. Denken Sie daran, dass alles, was sich in Ihrem Dokumentenstamm befindet, von der Außenwelt direkt gelesen werden kann. In diesem Fall wurde die Wahl getroffen, um die zu setzen .env Datei innerhalb des Dokumentstammverzeichnisses. Das heißt, ich könnte treffen und direkt auf diese Datei zugreifen können.

Wir können dies also von der Liste streichen, soweit es sich um eine Methode zum Speichern von allem handelt, was wir schützen müssen. Es gibt jedoch eine andere ähnliche Option, die den direkten Zugriff verhindern kann: das Verschieben der .env Datei aus dem Dokumentenstamm.

.env Datei, die sich außerhalb des Dokumentstammverzeichnisses befindet

In diesem Fall verschieben wir die Datei um eine Ebene nach oben, damit sie nicht direkt über das Internet aufgerufen werden kann. Wenn Ihr Dokumentenstamm beispielsweise /var/www/mycoolsite/public dann können Sie die Datei um eine Ebene nach oben verschieben /var/www/mycoolsite/.env. Dadurch kann PHP immer noch auf die Datei zugreifen, aber sie kann nicht über das Web erreicht werden.

Zum Beispiel könnten wir das beliebte verwenden vlucas/phpdotenv Paket, um die Datei zu lesen und automatisch in die aktuelle Umgebung zu importieren:

<?php
require_once __DIR__.'/../vendor/autoload.php';

$dotenv = new Dotenv\Dotenv(__DIR__.'/../');
$dotenv->load();
?>

In diesem Skript wird dem Code mitgeteilt, dass er laden soll .env Datei von einem Verzeichnis aufwärts (__DIR__.'/../'). Es zieht dann die Schlüssel/Wert-Kombinationen ein und fügt sie in die ein $_ENV superglobal. Diese Methode ist besser, als die Datei öffentlich zugänglich zu machen, aber es gibt auch einige Nachteile zu beachten:

  1. Wenn ein Angreifer in der Lage ist, eine PHP-Datei hochzuladen und Code auszuführen, könnte er die einfach ausdrucken $_ENV Werte und haben direkten Zugriff.
  2. Die Werte sind immer noch im Klartext auf der Festplatte vorhanden, sodass die Datei immer noch gelesen werden kann, wenn ein Problem mit dem Integrieren einer lokalen Datei gefunden wird.

Diese Methode ist also ein Schritt in die richtige Richtung, aber wir könnten immer noch etwas Robusteres verwenden, um unsere Anmeldeinformationen zu schützen.

Verschlüsselte Anmeldeinformationen

Der nächste Schritt zur Verhinderung des direkten Zugriffs auf die geheimen Werte besteht darin, entweder eine Verschleierungs- oder eine Verschlüsselungsmethode zu verwenden, um den Wert zu schützen. Da wir die Klartextversion des Werts benötigen, um ihn tatsächlich zu verwenden, kommt eine Verschleierung nicht in Frage. Mithilfe der Verschlüsselung können wir den Wert verschlüsseln und ihn dann bei Bedarf entschlüsseln.

Wir bauen auf dem vorherigen Beispiel auf und setzen die Werte in a .env Datei, die sich außerhalb des Dokumentstammverzeichnisses befindet. Um den Verschlüsselungs-/Entschlüsselungsprozess zu vereinfachen, verwenden wir die entschärfen/php-verschlüsselung Bibliothek.

Zuerst müssen wir es installieren und einen Schlüssel generieren:

composer require defuse/php-encryption
vendor/bin/generate-defuse-key

Dies ergibt einen Schlüssel, der Groß- und Kleinbuchstaben und Zahlen enthält und über ausreichend Entropie verfügt, um für diese einfache Operation verwendet zu werden. Dieser Schlüssel muss dort gespeichert werden, wo PHP darauf zugreifen kann, aber nicht irgendwo im (oder sogar in der Nähe) des Dokumentenstammverzeichnisses der Anwendung. Eine gängige Praxis ist es, es irgendwo darunter zu platzieren /usr/local in einer flachen Datei. Für diese Datei müssen dann die Berechtigungen und der Besitzer/die Gruppe geändert werden, damit PHP sie lesen kann.

Sobald Sie diese Datei eingerichtet haben, können Sie die Werte aus lesen und entschlüsseln .env Datei. Wir werden dasselbe verwenden vlucas/phpdotenv Bibliothek zum Einlesen der Datei und dann php-encryption um es zu entschlüsseln. Zuerst das Beispiel .env Datei:

test=def502003cbef858698bc40b2b8d0ffb6f365f2cef00009047650910941da72372313c7ce3f9d4ce8ba2cd64f6a5a5a330da47151c5c90124fd4e8ea792d40810d8906b8a888b12db78f1cbb0819825447ce685b1c608dfb1f30
test1=def502005c647492189c68d7f5fec781a0e10bdee8865b23f729b080c7bbadd2204005367ea6464d75609ea48be235886cd2f398bf60eaa0a0bb32e2906ab9b9b1f66c58fdd24f054b5311460fdf8770c5d729b3c296cb5d

Dann das PHP, um es zu entschlüsseln:

<?php
require_once __DIR__.'/../vendor/autoload.php';

$dotenv = new Dotenv\Dotenv(__DIR__.'/../');
$dotenv->load();

$keyContents = file_get_contents('/usr/local/keyfile`);
$key = \Defuse\Crypto\Key::loadFromAsciiSafeString($keyContents);

$secret = \Defuse\Crypto\Crypto::decrypt($ciphertext, $key);
?>

Unser entschlüsselter Geheimwert landet dann in der $secret Variable. Offensichtlich möchten Sie diesen Code nicht überall kopieren und einfügen müssen, sodass es einfacher wäre, ihn in eine Hilfsfunktion oder -klasse einzuschließen, um ihn unabhängiger zu machen.

Dies ist ein weiterer Schritt in die richtige Richtung, um unsere Anmeldeinformationen zu schützen, aber es gibt hier immer noch ein Problem, das alle diese Flat-File-Speichermethoden gemeinsam haben: die lokale Datei include. Wenn ein Angreifer in der Lage ist, Ihren Code zum Lesen zu bringen und Dateiinhalte offenzulegen, bedeutet dies, dass er nicht nur die verschlüsselten Werte auslesen kann .env sondern auch der Inhalt der keyfile da PHP das auch lesen können muss.

Uns gehen hier die Optionen aus, aber lassen Sie mich eine weitere vorschlagen. Diese Methode ermöglicht es Ihnen immer noch, die Werte in einer Flatfile zu speichern, schützt sie aber vor lokalen Datei-Include-Angriffen, da PHP nicht auf die Datei zugreifen muss, in der sie enthalten sind, sondern nur auf den Apache-Webserver. Lass uns anfangen.

Die „Apache Pull“-Methode

Bei dieser Methode verwenden wir einige der gleichen Techniken wie zuvor (Speichern der verschlüsselten Geheimnisse in einer Flatfile), aber es gibt eine neue Wendung: die Verwendung von Apache-Umgebungsvariablen, um diese Werte an PHP weiterzuleiten.

HINWEIS: Dieses Tutorial zeigt, wie man einen Apache-Webserver einrichtet, aber derselbe Ansatz kann wahrscheinlich auch über Nginx durchgeführt werden.

Wenn Sie die schnelle und einfache Version wollen, habe ich sie bereits eingerichtet dieses Depot mit einem Docker-basierten Beispiel, das zeigt, wie die Umgebung konfiguriert werden muss.

Hier sind die Schritte, denen der Code und die Anwendungen folgen werden:

  1. Irgendwo im Dateisystem wird eine Datei mit den verschlüsselten Anmeldeinformationen erstellt (in diesem Fall fügen wir sie einfach ein /tmp)
  2. Diese Datei wird dann in der /etc/apache2/envvars Datei als zusätzliche Quelle, die diese Werte als lokale Umgebungsvariablen einfügt.
  3. Wenn Apache startet, zieht es alle Werte von ein envvars und definiert sie intern neu. Dazu gehören unsere besonderen Werte.
  4. Diese Werte werden über Apache-Umgebungsvariablen über a an PHP übertragen SetEnv Aussage.

Die Frage, die Sie jetzt vielleicht stellen, hat mit diesen lästigen Problemen beim Einschließen lokaler Dateien zu tun. Können die zusätzlichen Einstellungen trotzdem nicht von PHP gelesen werden? Hier kommt das letzte Puzzleteil ins Spiel: die open_basedir Aufbau. Mit PHP können Sie verwenden open_basedir um die Verzeichnisse festzulegen, mit denen PHP interagieren kann, und zu verhindern, dass es diese verlässt. In diesem Fall können wir PHP nur auf den Dokumentenstamm sperren und verhindern, dass es die Datei manuell abruft und erhält.

Bevor ich fortfahre und zeige, wie es funktioniert, möchte ich eines sagen – diese Lösung ist auch nicht perfekt. Wenn ein Angreifer PHP-Code ausführen und auslesen könnte $_ENV superglobal, der Schlüsselwert wäre immer noch exponiert.

Die Geheimnisse

Zuerst richten wir die Geheimnisse ein und sorgen dafür, dass sie korrekt bezogen werden. In unserer /tmp/addl-settings Datei haben wir den Schlüsselwert definiert:

export ENC_KEY=1234567890 // This is just a sample key, obviously

Jetzt beziehen wir diese Datei in die envvars Datei am Ende der Datei:

. /tmp/addl-settings

Wenn dies eingerichtet ist, lädt Apache dann die ENC_KEY Wert in sein internes Umfeld ein und stellen ihn zur Verfügung.

Die Apache-Config

Als nächstes kommt die Apache-Konfiguration. In diesem Fall verwenden wir virtuelle Hosts, aber Sie können dies auch auf der Basisebene tun:

<VirtualHost *:80>
  ServerAdmin webmaster@localhost
  DocumentRoot /var/www/html

  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined

  SetEnv ENC_KEY ${ENC_KEY}
</VirtualHost>

In dieser Konfiguration sieht man das Besondere ${} Begriff, der verwendet wird, um eine Apache-Umgebungsvariable einzulesen und sie dem laufenden Prozess zur Verfügung zu stellen. In PHP bedeutet dies, es zu einem Wert in zu machen $_ENV.

Open_basedir

Der letzte Schritt des Puzzles ist die Einrichtung offen_basiert Schutz, also erstellen wir eine open_basedir.ini Datei und kopieren Sie sie an die richtige Stelle, damit Apache sie als PHP-INI-Konfigurationsdatei lesen kann:

open_basedir=/var/www/html

Mit all dem an Ort und Stelle, die ENC_KEY value – unser Verschlüsselungsschlüssel – ist jetzt für PHP über Apache verfügbar, kann aber nicht direkt als Datei aufgerufen werden.

Aber warte, es gibt noch mehr!

Dieses Setup ist großartig und alle, aber Sie fragen sich vielleicht: “Wie lese ich jetzt meine verschlüsselten Konfigurationseinstellungen?” Nun, mit Hilfe einer praktischen Bibliothek – psecio/secure_dotenv das kümmert sich um einen Großteil der Verarbeitung, es ist viel einfacher. Wir haben bereits den Schlüssel, den wir für die Verschlüsselung und Entschlüsselung in der Umgebung benötigen, also verwenden wir ihn einfach für diese Beispiele wieder. Installieren Sie zuerst das Paket mit Komponist:

composer require psecio/secure_dotenv

Erstellen Sie dann in Ihrer Anwendung die neue Parser Instanz, die die Umgebungsvariable mit dem Pfad zur Schlüsseldatei eingibt:

<?php
$envFile = __DIR__.'/.env';
$parser = new \Psecio\SecureDotenv\Parser($_ENV['ENC_KEY'], $envFile);
?>

Wiederverwendung der .env Datei aus den obigen Beispielen gibt uns Werte für test und test1 was aus dem Ergebnis von a extrahiert werden kann getContent Anruf.

<?php
echo 'test1 is: '.$parser->getContent()['test1'];
?>

Die Bibliothek übernimmt die Entschlüsselung des Werts hinter den Kulissen für Sie (unter Verwendung derselben entschärfen/php-Verschlüsselungsbibliothek) und Sie erhalten ein Nur-Text-Ergebnis.

Zusammenfassung

Das Sichern von Geheimnissen in PHP-Anwendungen ist ein interessantes Problem, das es zu lösen gilt. Bei meiner Recherche vor diesem Artikel habe ich festgestellt, dass es – ähnlich wie bei jedem anderen sicherheitsbezogenen Thema – immer mehr als einen Weg gibt, um eine Aufgabe zu erledigen. PHP macht es noch schwieriger, weil es mit Webservern interagiert. Die PHP-Skripte und die Verarbeitung müssen mindestens Lesezugriff auf jede Datei haben, mit der sie arbeiten müssen. Das macht es sehr Es ist schwierig, lokale Dateieinschlussprobleme zu verhindern, wenn Sie nicht sehr vorsichtig sind.

Es gibt keine 100 % sicheren Optionen für die Speicherung von Anmeldeinformationen, aber diese “Apache-Pull”-Methode, die ich hier vorgestellt habe, ist eine der einfacheren Methoden, die nicht viel mehr als die Technologie erfordert, die Sie bereits verwenden. Wenn Sie eine komplexere Umgebung haben, die mit Chef, Vagrant oder anderen Tools bereitgestellt wird, verfügen diese natürlich über einige zusätzliche Funktionen (wie verschlüsselte Chef-Databags), die auch für die Verarbeitung von Anmeldeinformationen verwendet werden können.

Denken Sie daran, dass es dafür keine Einheitslösung gibt. Dies hängt stark von Ihrer Umgebung und den Risikoanforderungen für Ihre Anwendung ab. Stellen Sie sicher, dass Sie sich hinsetzen und eine genaue erstellen Bedrohungsmodell Ihrer Bewerbung, bevor Sie eine Entscheidung darüber treffen, wie Sie Ihre Geheimnisse schützen werden. Dadurch erhalten Sie ein besseres Gesamtbild Ihrer Bedürfnisse und welche Art (oder Arten) von Schutz Sie benötigen.

Ressourcen

Similar Posts

Leave a Reply

Your email address will not be published.