Aufrufen von CGI-Scripts und PHP-Seiten
Begriffsdefinition
CGIs sind Scripts, die vom WWW-Server ausgeführt werden und die sich an die Common Gateway Interface Spezifikation halten. Im Folgenden geht es um den Aufruf von CGI-Scripts für studentische Homepage-Besitzer des KIT, nicht um das Schreiben von CGIs selbst (hierzu kann man sich die Links unter 'Weiterführende Literatur' zu Gemüte führen).
Verantwortlichkeit
Für Besitzer einer Homepage ist es möglich, eigene CGI-Scripts zu benutzen und anzubieten; hierzu ist keine besondere Berechtigung oder Eintragung erforderlich. Die Scripts werden dabei unter der User-ID des Benutzers ausgeführt, entsprechende Vorsicht ist also geboten, um nicht Dritten unabsichtlich Zugang zu eigenen oder fremden Dateien oder zum Rechner zu ermöglichen. Jegliche Auswirkungen der Scripts liegen in der Verantwortung der Benutzer; externe Aufrufe werden so behandelt, als hätte der Eigentümer der Scripts sie selbst aufgerufen. Bei Missbrauch ist demzufolge auch derjenige verantwortlich, der das Script, über das der Missbrauch erfolgte, auf seinem Account angelegt hat.
Dabei bitte auch beachten, dass CGI-Scripts im Gegensatz zu statischen Web-Seiten jedesmal einen eigenen Prozess aufrufen - das ist sehr aufwendig, man sollte CGIs also nur einsetzen, wenn es wirklich notwendig ist, und die Zahl der Aufrufe darf nicht überhand nehmen. Erfolgen Änderungen des dynamischen Inhalts wesentlich seltener als er abgerufen wird, ist in der Regel ein cron-Job, der regelmäßig eine neue statische Seite erzeugt, effizienter. Wird ein CGI jede Sekunde aufgerufen, belastet das die Rechner bereits erheblich - sowas kann bereits als missbräuchliche Benutzung gewertet werden. Man sollte also insbesondere auf Seiten verzichten, die CGIs einbinden und in kurzen Zeitabständen automatisch refresht werden.
Platzierung
Eine Datei, die im Verzeichnis public_html
(also der Homepage) oder einem Unterverzeichnis davon liegt und die Endung .cgi
hat, wird im Gegensatz zu anderen Dateien nicht übertragen, sondern ausgeführt, und nur die Ausgabe des Programmlaufs wird übermittelt.
Die alte Möglichkeit, Scripts im Verzeichnis .cgi-bin
abzulegen, ist mit dem Umzug auf die neuen Web-Server ganz weggefallen, es muss also auf die Ablage von CGI-Scripts in der normalen Homepage umgestellt werden.
Sicherheit
Da die Scripts unter der User-ID des Eigentümers aufgerufen werden, brauchen die Zugriffsrechte auch nur so gesetzt sein, dass der Eigentümer sie ausführen kann. So können z.B. CGI-Scripts, die Passwörter für Datenbank-Zugriffe enthalten, geschützt werden. Das Verzeichnis, in dem sich das Script befindet, muss allerdings wie bisher für den WWW-Server lesbar sein (in der Regel bedeutet dies Leserechte für alle); es darf allerdings nur für den Eigentümer schreibbar sein, sonst funktionieren die CGI-Scripts nicht!
CGI-Scripts können beliebige ausführbare Programme sein. Aus Sicherheitsgründen empfehlen wir aber, keine Shell-Scripts zu verwenden und in Perl- oder C-Programmen bei der Verwendung von Befehlen wie system()
oder popen()
äußerste Vorsicht walten zu lassen. In Benutzereingaben können sich Befehle (z.B. `rm -rf *`
) verbergen, die beim unvorsichtigen Auswerten der Eingaben ausgeführt werden. Nur wenn man sich absolut sicher ist, dass keine Benutzereingaben ausgewertet werden oder bei diesen Auswertungen keine Interpretation erfolgt, sollte man Shell-Scripts (bzw. Calls, die eine Shell aufrufen) verwenden. Anderenfalls geht man das gleiche Risiko ein, wie wenn man sich beim Verlassen des Rechners nicht ausloggt - nur dass diese Sitzung jetzt weltweiten Zugang hat. Solche Shell-Scripts sollten immer mit der Zeile #!/bin/sh
anfangen - für andere Shells kann nicht garantiert werden, dass sie immer unter dem jetzigen Pfad zu finden sein werden.
Auch in PHP kann man sich beliebig tief in's Knie schiessen - umsichtiges Programmieren ist auch hier angesagt. Guter Lesestoff für den Anfang sind Secure Programming in PHP und PHP Security Notes.
PHP
Für PHP-Seiten gibt es zwei Möglichkeiten:
- Da PHP oft nur für einfache Einfügungen in Web-Seiten verwendet wird und dies nicht viel Zeit verbrauchen soll, ist PHP als Modul im WWW-Server integriert. Solche PHP-Seiten (bzw. Web-Seiten mit eingearbeitetem PHP-Code), die die Endung
.php
haben müssen, werden unter der Benutzer-Nummer des WWW-Servers ausgeführt und haben deswegen nur eingeschränkte Rechte (sog. Safe Mode), sind dafür aber auch schnell. Für die meisten Anwendungsfälle reicht dies völlig aus. (Und weil die Frage immer wieder kommt: Nein, man kann den Safe Mode nicht abschalten, sonst würde es ja gar keinen Sinn machen, ihn erst anzuschalten - auch wenn das praktisch wäre, so wie es praktisch wäre, auch ohne ec-Karte Geld abheben zu können.) - Für den Fall, dass die Einschränkungen des Safe Mode umgangen werden müssen (z.B. um Dateien zu schreiben o.ä.) oder eine andere PHP-Version genutzt werden soll, stehen auch eigenständige PHP-Script-Interpreter zur Verfügung. Solche Scripts sind dann ganz normale CGIs (mit der Endung
.cgi
) wie oben beschrieben. Um den PHP-Script-Interpreter zu laden, muss die erste Zeile#!/usr/bin/php-cgi
lauten (bzw.#!/usr/bin/php-cgiversion
falls explizit eine andere Version genutzt werden soll).
Hinweise:
- Seit der Umstellung auf PHP5 funktioniert nur noch die Endung
.php.
- Bitte beachten Sie, dass Short Tags nicht verwendet werden können, da diese inkompatibel zu XML sind - es muss also
<?php
statt nur<?
geschrieben werden.
- Bitte beachten Sie ebenfalls, dass seit PHP4 die Variable
register_globals
defaultmäßig aufOFF
steht! Genauere Hinweise zur Änderung von bestehendem Code entnehmen Sie bitte der PHP-Dokumentation.
Eigene Einstellungen können Sie entweder über die Datei .htaccess vornehmen, oder besser noch innerhalb der PHP-Seite selber setzen. So können Sie z.B. die Werte post_max_size und upload_max_filesize zum Einstellen der maximalen Größe hochgeladener Dateien setzen.
Aufruf
CGI-Scripts werden genauso aufgerufen wie normale HTML-Seiten oder Download-Dateien - durch einen Link auf die zugehörige URL. Der einzige Unterschied besteht darin, dass der Server an der Endung .cgi
erkennt, dass er nicht den Inhalt dieser Datei übertragen, sondern das Script ausführen und nur dessen Ausgabe übermitteln soll.
In der Regel reichen zum Aufruf relative URLs aus, die sich auf die URL der Seite beziehen, auf der der Link steht, bzw. die maximal den Pfad auf dem Server angeben, also ohne das https://
und den Servernamen. Dies erleichtert einen späteren Umzug, sollte er jemals notwendig sein. Für Links in Seiten auf anderen Rechnern müssen natürlich vollständige URLs verwendet werden; hier sollten immer nur die offiziellen Server-Namen (die in der Regel mit www
beginnen) verwendet werden, niemals der Name eines Rechners selbst oder gar eine IP-Nummer. Nur für die offiziellen Server-Namen garantieren wir Kontinuität bei technisch bedingten Umzügen in neue Netze oder auf neue Hardware. Dies gilt natürlich ganz allgemein und nicht nur für CGIs.
Fehlersuche
Falls ein CGI-Script nicht läuft (die übliche Fehlermeldung lautet "Internal Server Error", da der Server das Ausführen von CGI-Scripts als interne Angelegenheit ansieht, obwohl es um Programme von Benutzern geht), kann man es zunächst einmal lokal testen. Diese Tests sollte man immer auf dem Rechner durchführen, auf dem der WWW-Server läuft, um sicherzustellen, dass man die gleiche Umgebung wie der WWW-Server selbst vorfindet. Ggf. sollte man sich also zuerst z.B. mit ssh www.student.kit.edu
auf dem WWW-Server einloggen.
Der einfachste Test ist, das Programm direkt von der Shell aus aufzurufen (also ./script.cgi
und nicht perl script.cgi
, auch wenn man weiß, dass es ein Perl-Programm ist - das muss die Shell selbst herausfinden können, denn sonst kann es der WWW-Server nachher auch nicht).
Hier sieht man auch gleich, ob die Zugriffsrechte ausreichend gesetzt sind, d.h. ob man als Eigentümer das Script aufrufen kann. Hat man nur Lese-, aber keine Ausführ-Rechte, erscheint je nach Shell eine Fehlermeldung der Art cannot execute
oder permission denied
. In so einem Fall kann man mit chmod u+x script.cgi
die notwendigen Ausführ-Rechte setzen.
Kleine Checkliste für Scripts:
- Ist die Endung tatsächlich
.cgi
? - Sind die ersten beiden Zeichen in dem Script
#!
(ohne Leerzeichen davor)? - Kommt danach der vollständige Pfad zum Interpreter-Programm (z.B.
/usr/bin/perl
)? - Kommen nach dem Programm (und ggf. Optionen) keine weiteren Zeichen mehr?
Der häufigste Fehler an dieser Stelle sind Scripts, die von einem Windows-System stammen und deswegen vor dem Zeilenendezeichen noch ein <Ctrl>-<M> haben. Man sieht diese Zeichen, wenn man die Scripts mitjoe
odervi
anschaut (in einem Windows-Editor sieht man diese Zeichen natürlich nicht, da es ja genau diese Windows-Programme sind, die das überflüssige Wagenrücklauf-Zeichen erzeugen). Beim Versuch, solche Scripts direkt in der Shell aufzurufen, erscheint in der Regel eine Fehlermeldung der Art" not found "...
(man beachte das"
am Zeilenanfang, das eigentlich am Ende stehen sollte, aber durch das überzählige Wagenrücklauf-Zeichen (Carriage Return, CR, -) nach vorne gerutscht ist); manchmal (je nach Shell) auchinterpreter "..." not found
oderNo such file or directory
.
Abhilfe: Mit dem Kommandoperl -pi -e 's/ //' script.cgi
kann man die störenden Zeichen entfernen (wobei für script.cgi natürlich der entsprechende Name des eigenen Scripts stehen muss). Dieses Kommando kann man auch einfach mal 'auf Verdacht' aufrufen, es macht nichts kaputt. Wenn's danach funktioniert, hat man die Fehlerursache gefunden (und sollte sich ggf. überlegen, warum man den Fehler nicht anhand obiger Beschreibung erkannt hat...) - in Zukunft also an dieser Stelle besser aufpassen. - Ist das so aufgerufene Interpreter-Programm auch tatsächlich auf dem WWW-Server installiert?
- Hält sich das Script wirklich an die Common Gateway Interface Spezifikation? Scripts, die keinen korrekten Header liefern, führen immer zu Fehlern.
- Häufig passiert es, dass man sich auf Dateien ohne Pfadangabe oder auf relative Pfade verlässt, was lokal ggf. funktioniert, aber nicht, wenn der WWW-Server das Script aufruft, da dieser das Arbeitsverzeichnis (current directory) anders setzt. Man sollte daher nur absolute Pfade verwenden oder aber explizit das Arbeitsverzeichnis setzen. Dabei bitte nur Pfade verwenden, die mit dem eigenen Home-Verzeichnis anfangen, so wie es in der Variablen gespeichert ist (kann man sich in der Shell mit
echo
anzeigen lassen). Andere Pfade, wie sie z.B./bin/pwd
ausgibt, können nicht garantiert werden, weil sie rechnerspezifisch sind oder dynamisch vom Auto-Mounter generiert werden. - Das oben gesagte gilt gleichermaßen für Programm-Aufrufe, da auch die Variable
PATH
nicht den gewohnten Umfang besitzt - im Zweifelsfall also selber setzen oder aber volle Pfadangaben für Nicht-Systemprogramme verwenden. - Zu guter Letzt: Entsprechen Eigentümer und Gruppe sowohl des Scripts als auch des oder der Unter-Verzeichnisse(s) des Home-Verzeichnisses dem zugehörigen Benutzer? Das Ändern allein der Gruppe eines Verzeichnisses führt bereits zum Fehlschlagen des Aufrufs.
Kleine Checkliste für übersetzte Programme:
- Auch hier: Hält sich das Script wirklich an die Common Gateway Interface Spezifikation, insbesondere an das Ausgabeformat? Scripts, die keinen korrekten Header liefern, führen immer zu Fehlern.
Klappt soweit alles, kann man das Script auch aus der Shell mit dem Kommando cgitest
aufrufen. Dieses Programm bietet dem CGI-Script eine Umgebung, die der beim Aufruf über den WWW-Server entspricht. So kann man meist erkennen, ob man sich in dem Script auf gewissen Gegebenheiten (wie z.B. eine gesetzte PATH
-Variable im Environment) verlassen hat, die zwar in der normalen Shell gegeben sind, aber vom WWW-Server nicht so gesetzt werden. Sieht die Ausgabe beim Aufruf mit cgitest
ordentlich aus, sollte man sicherstellen, dass man keine Fehler-Ausgaben übersehen hat, z.B. durch einen Aufruf der Art cgitest ./script.cgi >/dev/null
.
cgitest
muss übrigens auch auf dem Rechner aufgerufen werden, auf dem der WWW-Server läuft, da es dessen Konfigurationsdateien liest, um die richtige Umgebung erzeugen zu können. Wird es auf einem anderen Rechner aufgerufen, erhält man in der Regel eine Fehlermeldung, dass die Konfigurationsdatei des WWW-Servers nicht gelesen werden konnte.
Zuletzt kann man auch noch einen Blick in die Log-Dateien des Servers werfen: Die Kommandos myerrorlog
und myscriptlog
zeigen die benutzerspezifischen Einträge aus dem generellen Fehler-Log und dem speziellen Script-Log des WWW-Servers an. Auch dazu muss man natürlich auf dem WWW-Server eingeloggt sein, sonst erhält man logischerweise nur die Fehlermeldung, da das Verzeichnis mit den Logdateien des WWW-Servers nicht verfügbar ist. Für WWW-Server, die im Cluster laufen, gibt es die speziellen Cluster-Varianten myclerrorlog
und myclscriptlog
, die alle Log-Files auf allen Cluster-Knoten anzeigen (sonst erhält man nur die Ausgabe des Servers, auf dem man gerade eingeloggt ist).
Die Fehlermeldung couldn't spawn child process
kommt allerdings nicht von einem fehlerhaften CGI-Script, sondern von einem echten Ressourcen-Engpass des WWW-Servers. Hier hilft wirklich nur noch, den Administrator zu verständigen.
Weiterführende Literatur
- CGI Programming on the World Wide Web aus dem O'Reilly Open Books Project.
- CGI leicht gemacht
- CGI 101 online class
- SELFHTML
Wer jetzt immer noch nicht weiterkommt und eine E-Mail schreiben möchte, sollte unbedingt erwähnen, dass die Hinweise auf dieser Seite befolgt wurden (sonst gibt es nur den Hinweis auf eben diese Seite) und um welche URL es geht, damit das Problem nachvollzogen werden kann.
Andreas Ley