IIS mit PHP: file_get_contents bei externen URLs

Bei einem kleinen Web-Projekt wollte ich eine offene API mittels PHP via HTTPS anbinden. Ausgeführt wurde PHP in diesem Fall von den Microsoft Internet Information Services (IIS). Die Daten wurden vom Anbieter klassischerweise im JSON-Format bereitgestellt.

Problemstellung

Die native PHP-Funktion file_get_contents sollte den Datenabruf für mein Projekt erledigen. Allerdings konnte ich die Funktion nicht erfolgreich nutzen, um die Informationen der URL abzufragen. Die gelieferten Daten bzw. das entsprechende Array waren immer leer.

Zu erst hatte ich fehlende PHP-Option in Verdacht. Die Optionen allow_url_fopen und allow_url_include waren jedoch beide aktiviert. Die Optionen können ohne Blick in die php.ini über folgende Funktion direkt in PHP getestet werden. Geprüft wird ebenfalls auf die notwendigen OpenSSL- und Curl- Erweiterung.

$wrapper_test = stream_get_wrappers();
echo 'Curl: ',  extension_loaded  ('curl') ? 'yes':'no', "<br />";
echo 'OpenSSL: ',  extension_loaded  ('openssl') ? 'yes':'no', "<br />";
echo 'HTTP Wrapper: ', in_array('http', $wrapper_test) ? 'yes':'no', "<br />";
echo 'HTTPS Wrapper: ', in_array('https', $wrapper_test) ? 'yes':'no', "<br />";
echo 'Wrappers Array: ', var_export($wrapper_test);

Da an dieser Stelle keine Fehler zu erkennen waren, vermutete ich als nächstes simples Scriptside-Blocking auf der Quellseite. Als ich dieses durch einen netclient (Snoopy – the PHP net client), also einer Webclient Simulation auf Requestseite ausgeschlossen hatte, ermittelte ich weiter. Es musste also an der Verbindung selbst liegen.

Link zum netclient: https://github.com/endroy/Snoopy

Analyse: Deaktivieren der Serververifizierung

Schließlich stellte sich heraus, dass PHP (CURL) die verschlüsselte Verbindung (HTTPS) zum Server der API nicht verifizieren konnte. Keine erfolgreiche Verbindung möglich. Leider wies mich nicht das Error-Reporting von PHP darauf hin. So musste ich den eigentlichen Fehler selber feststellen…

Um zu prüfen ob es an der fehlerhaften Serververifizierung liegt, kann die Verifikation temporär umgangen werden. Sollten die Daten über diese Methode erfolgreich abrufbar sein, dass Array also Daten enthalten, ist der Fehler gefunden.

$url = 'https://myapisourceisencrypted.com/api/json';

$SkipVerify=array(
  "ssl"=>array(
    "verify_peer"=>false,
    "verify_peer_name"=>false,
  ),
);  

$json_data = file_get_contents($url, false, stream_context_create($SkipVerify));

Wichtig: Diese Methode sollte nur für Debugging-Zwecke und nicht im produktiven Umfeld verwendet werden. Das Deaktivieren der Verifikation stellt eine Sicherheitslücke dar! Außerdem wird das Problem nur umgangen und nicht behoben.

Problemlösung: Für CURL und OpenSSL CA-Zertifikate installieren

Da sowohl die CURL-Funktion als auch OpenSSL ihre eigenen Zertifikatsspeicher verwenden, müssen die entsprechenden Root-Zertifizierungsstellen bekannt sein und so entsprechend als vertrauenswürdig eingestuft werden.

Bei PHP auf einem IIS wird nicht der Zertifikatsspeicher des Systems als Quelle für die Root-Zertifikate abgefragt. Daher müssen die Root-Zertifikate separat gespeichert und angegeben werden. Vorzugsweise werden für die Funktion nur die CA-Zertifikate installiert die auch benötigt werden.

Alternativ kann bspw. ein Export des Mozilla-Zertifikatsspeichers installiert werden. Dieser enthält alle relevanten öffentlichen Zertifizierungsstellen und biete daher eine hohe Kompatibilität zu vielen öffentlichen Webdiensten. Dieser Speicher sollte jedoch regelmäßig gepflegt werden, damit es nicht zu unerwarteten Störungen kommt, wenn etwa das Zertifikat auf der Seite des API-Anbieters ausgetauscht wird und der lokale Speicher veraltet ist.

https://wiki.mozilla.org/CA/Included_Certificates

https://curl.se/docs/caextract.html

Wichtig: Grundsätzlich sollten nur vertrauenswürdige Quellen für den Download und die Installation von Zertifikaten öffentlicher CAs in Betracht gezogen werden.

Empfehlenswert für die Ablage der Dateien ist das Default-Verzeichnis der PHP-Installation.

Beispiel: C:\Program Files\PHP\v8.0\extras\ssl

Hinweis: Das Beispiel gilt für PHP 8.0. Die PHP-Version kann entsprechend abweichen.

Sind die Zertifikate heruntergeladen und im System abgelegt, muss nur noch die php.ini mit entsprechenden Pfad-Verweisen angepasst werden.

openssl.cafile = "C:\Program Files\PHP\v8.0\extras\ssl\cacert-2022-04-26.pem"
curl.cainfo = "C:\Program Files\PHP\v8.0\extras\ssl\cacert-2022-04-26.pem" 

Abschließend sollte der IIS einmalig neu gestartet werden. Nun sollte file_get_contents entsprechende Inhalte von der Quelle zurückliefen. Mittels json_decode können die Daten bzw. das Array erfolgreich weiterverarbeitet werden.

$json_data = file_get_contents($url, true);
$json_array = json_decode($json_data, true);

Leon Gawinski

System Engineer Softwarebereich Microsoft

Empfohlene Beiträge

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Schaltfläche "Zurück zum Anfang"