Archive for the ‘system’ tag
Gefährliche PHP-Funktionen ausschalten
PHP ist mächtig, man kann nahezu alles umsetzen, zur Not auch mit Zugriff auf die Kommandozeile. Doch die Macht hat auch immer eine böse Seite, sie kann missbraucht werden. Nicht nur auf shared Webservern sollte man aus Sicherheitsgründen einige Funktionen ausschalten, auch auf einem einzelnen Server, auf der nur die eigene Webseite liegt, sollten die gefährlichsten Funktionen im Normalfall abgeschaltet werden (können). Falls ein Eindringling es schafft, eigenen PHP-Code hochzuladen, wäre er sehr eingeschränkt in seinen Möglichkeiten, viel kaputt zu machen, Daten auszuspähen oder sonstige böse Sachen auf unserem Server zu veranstalten.
Natürlich gibt es noch viele weitere Sicherheitsmaßnahmen, die man ergreifen sollte. Der Safe-Mode wird ja bald abgeschafft, kann und sollte also nicht mehr genutzt werden. Stattdessen sollten open_basedir, disable_classes und disable_functions genutzt werden.
Natürlich ist es auch ein guter Rat, immer eine aktuelle Version von PHP zu verwenden, seine Scripte abzusichern (Directory-Traversal, Code-Injection, Dateiuploads auf PHP-Code zu überprüfen, Remote-File-Inclusion etc. etc.), um einen Eindringlich erst gar nicht reinzulassen, aber das soll hier heute nicht das Thema sein.
Hier geht es also um disable_functions. Wir können in der php.ini PHP-Funktionen abschalten, die wir nicht benötigen und evtl. einem Eindringlich helfen, Schaden anzurichten. Welche Funktionen sollten deaktiviert werden? Ich habe hier mal eine Liste, über die wir diskutieren können:
disable_functions = „apache_child_terminate, apache_get_modules, apache_get_version, apache_getenv, apache_note, apache_setenv, curl_exec, curl_multi_exec, define_syslog_variables, disk_free_space, diskfreespace, dl, error_log, escapeshellarg, escapeshellcmd, exec, ftp_connect, ftp_exec, ftp_get, ftp_login, ftp_nb_fput, ftp_put, ftp_raw, ftp_rawlist, ini_alter, ini_get_all, ini_restore, link, mysql_pconnect, openlog, passthru, pfsockopen, php_uname, phpinfo, popen, posix_getpwuid, posix_kill, posix_mkfifo, posix_setpgid, posix_setsid, posix_setuid, posix_uname, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, set_time_limit, shell_exec, symlink, syslog, system, tmpfile, virtual“
shell_exec verbietet auch gleich das Ausführen von Systembefehlen via Backticks (´).
Einige davon sind sicherlich gefährlicher als andere, und einige werden sogar vielleicht im ein oder anderen Projekt dringend gebraucht, sodass sie aus der Liste rausgenommen werden müssen. Als ich diese Liste erstellt habe musste ich feststellen dass ich mehr als ein Dutzend der Funktionen nicht kannte, es ist also sehr zu empfehlen sich mal ein paar davon anzuschauen.
Welche Funktionen verbietet ihr in der php.ini?
Asynchron Aufgaben erledigen (lassen)
Was könnte der Autor damit meinen, werdet ihr euch fragen. Naja, das ist schnell erklärt: hier und da hat man mal kleine Aufgaben, die evtl. etwas länger dauern, von dessen Ergebnis der User aber nicht wissen muss. Warum also soll man damit seine PHP-Scripte ausbremsen, und den User warten lassen? Ihn interessiert das Ergebnis ja (erstmal) nicht.
Beispiel: Nehmen wir an, das Versenden eine Informations-Email würde 1 Sekunde dauern (wenn man über einen externen SMTP versendet und nicht über den lokalen, kann das sogar sein). Dann bemerkt der User auf der Webseite eine Verzögerung, die Seite wird langsam.
Es gibt sicherlich noch bessere Beispiele, wie z.B. das Berechnen eines Vorschaubildes. Der User lädt ein Bild hoch (Avatar), und wir brauchen davon eine verkleinerte Version. Wenn dieser Algorithmus nun langsam wäre (was er im Normalfall nicht ist), würde der User vor seinem Bildschirm Däumchen drehen.
Für all solche Aufgaben (ihr findet sicherlich bessere) ist es sinnvoller, diese Aufgaben asynchron erledigen zu lassen. Da gibt es mindestens 2 Möglichkeiten:
- Man trägt die Aufgabe in eine Task-Tabelle in der Datenbank ein, und lässt ein weiteres Script (Cronjob?) diese Task-Liste abarbeiten.
- Man lässt einen Kindprozess diese Arbeit erledigen, welcher geforkt wird, und non-blocking ist
In beiden Fällen bekommt der User direkt die nächste Seite zu sehen und kann weiterarbeiten, der asynchrone Prozess kann aber erst einige Sekunden/Minuten später fertig sein.
Wie setzt man das nun um? Ich glaube, die erste Möglichkeit sollte jedem von euch bekannt sein, da es keine Schwierigkeit ist, eine neue Zeile in einer Datenbank-Tabelle einzufügen, und auch ein Script, das periodisch diese Zeilen liest und abarbeitet, sollte kein Hexenwerk sein.
Die zweite Möglichkeit haben wahrscheinlich deutlich weniger Leute genutzt. Ich finde sie auch ehrlich gesagt unschöner, aber möchte sie trotzdem vorstellen (da sie schneller umzusetzen ist, und häufig auch zum Ziel führt). Als erstes fällt einem da vielleicht der system() Aufruf ein, mit dem man einen weiteren Prozess starten kann. Das Problem daran ist, dass er blocking ist, sprich der nächste Befehl in PHP erst ausgeführt wird nachdem der System-Aufruf beendet ist. Genau das wollen wir ja nicht. Aber unter jedem Betriebssystem gibt es da Möglichkeiten, den Prozess in den Hintergrund zu schieben. Der system() Aufruf ist dann sofort beendet und PHP läuft weiter, parallel läuft ein weiterer Prozess mit der Aufgabe.
function execInBackground($cmd) { if (substr(php_uname(), 0, 7) == "Windows") { pclose(popen("start /B ". $cmd, "r")); } else { system($cmd . " > /dev/null &"); } }
In Maugrim The Reaper’s Blog gibt es eine sehr ausführliche Serie zu dem Thema, mit vielen weiteren Beispielen und Code. Auf jeden Fall lesenswert.
Eine andere interessante Möglichkeit ist Gearman.
http://toys.lerdorf.com/archives/51-Playing-with-Gearman.html
Wenn man etwas andere Voraussetzung hat, könnten die curl_multi_* Funktionen interessant sein. Diese kann man nutzen, wenn man viele Dinge zu erledigen hat, aber auf die Ergebnisse warten muß (weil man sie dem User anzeigen muss). Man möchte also nicht asynchron, sondern simultan arbeiten. Das ganze ist in einem Blogartikel zum Thema „Simultaneuos HTTP requests in PHP with cURL„ super beschrieben.