Manipulationen erkennen – Bemerken dass man gehackt wurde
In viele Webseiten wird heutzutage eingebrochen und es werden Änderungen an der Webseite durchgeführt. Das kann die selbst programmierte Webseite sein bei der man eine Lücke eingebaut hat, das kann aber auch ein installiertes WordPress oder Joomla sein.
Häufig sind solche Angriffe nicht geziehlt, sondern werden mittels automatischer Scripte durchgeführt, die dann auf allen anfälligen Servern kleine Änderungen an den Dateien durchführen:
– Phishing Webseiten werden auf den Server gelegt
– in die Webseite wird bösartiger Code eingebaut der mittels Flash, Javascript, Windows Media Player oder anderen Techniken versucht einen Drive-By-Download bei den Besuchern zu platzieren
– die Webseite wird „nur“ defaced, sprich ein witziges Bild wird auf die Webseite gesetzt die den Besitzer blossstellen soll
– an strategisch gut platzierte Stellen im Code werden mail() Aufrufe eingefügt die beispielsweise die Klartextpasswörter an den Angreifer senden
– es werden kleine Scripte abgelegt wie beispielsweise PHProxy oder ein Spam-Mail-Script
– heutzutage wird auch gern die .htaccess Datei verändert, mit der der Suchmachinen-Traffic auf eine andere Seite umgeleitet wird, beispielsweise so:
RewriteEngine On RewriteCond %{HTTP_REFERER} .*google.*$ [NC,OR] RewriteCond %{HTTP_REFERER} .*ask.*$ [NC,OR] RewriteCond %{HTTP_REFERER} .*yahoo.*$ [NC,OR] RewriteCond %{HTTP_REFERER} .*altavista.*$ [NC,OR] RewriteCond %{HTTP_REFERER} .*msn.*$ [NC,OR] RewriteCond %{HTTP_REFERER} .*netscape.*$ [NC,OR] RewriteCond %{HTTP_REFERER} .*aol.*$ [NC,OR] RewriteCond %{HTTP_REFERER} .*lycos.*$ [NC,OR] RewriteCond %{HTTP_REFERER} .*search.*$ [NC,OR] RewriteCond %{HTTP_REFERER} .*metacrawler.*$ [NC,OR] RewriteRule .* http://hackerwebseite.de [R,L]
Falls die Angreifer Dateien verändern oder hinzufügen können wir das mit einem geeigneten Programm erkennen. Falls der bösartige Code in die Datenbank geschrieben wird hilft die hier vorstellte Lösung nicht.
Die strategisch beste Lösung ist die Überwachung auf Betriebssystemebene. Dazu gibt es Programme wie die unten aufgeführten die eine Liste von Dateien und Ordnern abarbeiten und die Dateien auf Veränderungen prüfen. Standardmäßig sind das Dateien in denen häufig Rootkits installiert werden wie /usr/bin/sh, /usr/bin/ls, /usr/bin/ps usw. aber auch eigene Ordner können auf Änderungen überwacht werden. Bekannte Programme sind Tripwire, AIDE, Osiris, Samhain, iWatch, und OSSEC.
Was aber macht man wenn man ein Webhosting-Paket hat? In diesen Fällen kann man sein Webverzeichnis nicht von solchen Programmen überwachen lassen, sondern muss sich selbst darum kümmern, mit natürlich einigen Nachteilen.
Ich habe auf GitHub das Projekt FileWatcher gestartet, eine PHP Klasse die Dateien und Ordner auf Änderungen überprüfen kann, und bei Änderungen Alarm schlägt. Wird dann so genutzt:
<?php require_once 'FileWatcher.php'; $fileWatcher = new FileWatcher(); $fileWatcher->readConfig('FileWatcher.config.php') ->checkNow();
Die Konfiguration könnte beispielsweise so aussehen (hier für ein Windows-System):
$config = array( 'password' => 'secretPassword', 'includePaths' => array('C:\\Temp', 'D:\\temp'), 'excludeFolderList' => array('C:\\Temp\\projekt'), 'excludeExtensionList' => array('jpg', 'png', 'pdf'), 'hashMasterFilename' => 'FileWatcher.MasterHashes.txt', 'logFilename' => 'FileWatcher.log', 'overwriteMasterFile' => true, 'alertEmailAddress' => 'alerts@domain.de', 'alertEmailSubject' => 'ALERT: File hashes on your server have changed', 'alertEmailMethod' => 'mail', // mail or smtp 'alertEmailSmtpServer' => '1.2.3.4', 'alertEmailSmtpUser' => 'XXXX', 'alertEmailSmtpPass' => 'XXXX', );
Es ist die erste Version, man kann natürlich noch einige Features hinzufügen wie alternative Benachrichtigungswege (Jabber, SMS?), oder eine Änderungsprüfung anhand des Last-Modified-Dates statt einem SHA1-Hash, das dürfte bei vielen großen Dateien deutlich schneller sein.
Man könnte sich auch an Extensions wie FAM oder Inotify heranwagen, interessant um sehr schnell Änderungen an Dateien mitzubekommen, aber dazu muss man diese Extensions auch installieren können.
In der README sind noch einige weitere ToDos.
Tut ihr irgendetwas in der Richtung, fremde Veränderungen auf euren Servern zu entdecken?
PS: Falls nur ein WordPress Blog betrieben wird hilft evtl. auch der Einsatz des Plugins File Monitor Plus.
Eine einfache Überprüfung könnte man auch über find lösen, sofern man Zugriff auf die Konsole hat.
find -not -wholename ‚./tmp/*‘ -type f -mtime -1
Das Kommando gibt alle Dateien zurueck, die innerhalb der letzten 24h verändert wurden. Die Dateien die sich ändern dürfen (z.B. in temporäre Verzeichnissen) kann man ja ausschließen.
Das wäre aufjedenfall recht flott, aber auch nicht 100%ig sicher, da die Änderungszeit über „touch“ manipuliert werden kann…
Die Methode mit den Prüfsummen wäre also aufjedenfall sicherer. Wobei man auch hier sicherstellen muss, dass der Angreifer das Skript nicht manipulieren kann (z.B. ganz ausschalten oder die MasterHashes-Datei ändern).
Gruß,
Max
Max
17 Aug 11 at 10:26
Wir haben uns vor kurzem auch mit der Problematik beschäftigt und kamen zum Ergebnis, dass es extrem schwierig ist, das ganze verlässlich zu realisieren.
Die von dir vorgestellte Vorgehensweise liefert einen gewissen Komfortfaktor: wenn sie anschlägt, ist sicher etwas passiert. Gleichzeitig gilt aber auch: wenn sie nicht anschlägt, kann trotzdem etwas passiert sein, denn der Angreifer kann genauso die Filewatcher Dateien manipulieren und die Checkfunktion mit einem „return true“ kurzschließen.
Gerade falls das projekt etwas verbreitung finden würde, wäre in jedem entsprechenden Scriptkiddy-Paket eine Suche nach dem FileWatcher mit entsprechendem Kurzschluss enthalten und der Serverbetreiber wiegt sich in falscher Sicherheit.
Fazit: so nützlich wie ein Gartentürchen im Jägerzaun – hält sicher jemanden ab, aber jeder kann drüberspringen. Will man das ganze verlässlich realisieren, um bspw. auch Inhouse Manipulationen aufzudecken, kommt man schnell an ein Henne- und Eiproblem mit der Manipulationssicherheit des Filewatchers selbst. Oder gibt es da verlässliche Methoden?
Steffen
17 Aug 11 at 10:29
Hi,
ein sehr interessantes Projekt zu diesem Thema:
https://phpids.org/
Was PHPIDS abdeckt:
https://phpids.org/faq/#faq1
ezod
17 Aug 11 at 11:09
Naja, eigentlich ist PHPIDS ne ganz andere Baustelle, es übernimmt eher die User Input Filterung und vertraut der PHP Anwendung vollständig, das in diesem Artikel beschriebene Problem kümmert sich gar nicht um User Input und misstraut der PHP Anwendung selbst… 🙂
Steffen
17 Aug 11 at 11:11
Jain. Wenn Du es mit OS Bordmitteln lösen kannst und Dein Content nicht beim Webhoster liegt, dann ist das sicherlich richtig und auch „sicherer“.
In vielen Fällen ist aber ein Defacement schon durch einfaches XSS usw. möglich. PHPIDS ist da sicherlich nicht DIE Lösung des Problems, aber ein weiterer Filter, der sich relativ einfach einbauen lässt.
Und ob ich nun den o.g. Filewatcher einsetze und der Angreifer diesen auch gleich manipuliert oder PHPIDS macht m.E. keinen Unterschied.
ezod
17 Aug 11 at 11:21
@Steffen: Ich habe natürlich auch darüber nachgedacht und in der README 1-2 Dinge angesprochen, zum Beispiel hilft es schon mal wenn der FileWatcher außerhalb des DocumentRoot oder gar außerhalb des open_basedir liegt, dann kommt der potentielle Angreifer da nicht ran (wir nehmen mal an dass er nicht root-Rechte erlangt hat).
Oder man versieht den FileWatcher mit anderen Rechten: keine Schreibrechte für den Webserver-User würde schon mal helfen, oder gar den FileWatcher mit einem ganz anderen User ausführen, Hauptsache der Webserver-User kann an den FileWatcher-Dateien nichts verändern. All das ist aber nicht ganz so trivial auf einem beschränkten Webhosting-Server.
@ezod: PHPIDS hilft dabei dass man nicht gehackt wird. Hier geht es darum wie man erkennt wenn es doch irgendwie passiert ist. Könnte ja auch beispielsweise sein dass dein Apache eine Lücke enthält und PHP gar nicht im Spiel ist beim eigentlichen Hack, oder dein Perl-CGI-Script die Lücke enthält. Oder durch einen Trojaner auf deinem PC die FTP-Daten abhanden gekommen sind, ich meine da auch mal etwas gehört zu haben. Natürlich ist es oberstes Ziel dass es gar nicht erst soweit kommt dass man den FileWatcher braucht.
Da kommt mir gleich noch eine andere Idee: Man könnte den FileWatcher auch auf einem anderen Server laufen lassen und die Dateien mittels NFS oder FTP überprüfen von außen…
Michael Kliewe
17 Aug 11 at 11:27
@Michael: gute Punkte, macht die Sache dann recht nützlich. Die Lösung über FTP wäre natürlich die Luxusvariante…
Steffen
17 Aug 11 at 11:40
In Zeile 60 und 66 fehlt das isset(). Zur Zeit kriegt man dort Notices.
Alles andere: Top!! Genauso so ein simples Tool hab ich immer gesucht.
Christoph
17 Aug 11 at 13:40
Hey vielen Dank für dieses gute Skript, allerdings hätte ich noch eine Ergänzung, damit die E-Mail auf den ersten Blick aussagekräftiger wird.
Zeile 93 – 97 ergänzen:
foreach ($intersectKeys as $intersectKey) {
if ($masterHashes[$intersectKey] != $currentHashes[$intersectKey]) {
$changedFiles[$intersectKey] = $masterHashes[$intersectKey];
}
}
Maximilian
17 Aug 11 at 13:56
Ich schau eigentlich nur, ob ich nicht gehackt gehackt werde. Die einzig sichere Variante wäre z. B. ein Bashscript, dass sich anmeldet und einmal alle Dateien durchrödelt. Es müsste aber von einem Rechner sein, auf den man gar keinen Zugriff bekommen kann, damit es nicht angreifbar ist.
Oliver
17 Aug 11 at 14:16
Aide: http://www.cyberciti.biz/faq/debian-ubuntu-linux-software-integrity-checking-with-aide/
Basti
17 Aug 11 at 17:26
Wäre sicherlich auch eine nette Idee für einen Webservice. Man meldet seine Seite an und der Webservice überprüft die Seite in regelmäßigen Abständen.
Was vielleicht noch eine Idee wäre, wenn man ein Archiv mit den Original-Dateien hinterlegen könnte die im Fall der Fälle automatisch entpackt werden und die im verdacht der Manipulation stehende Datei überschreiben würde. Wäre dann quasi eine automatische Urlaubsvertretung.
Für WordPress nutze ich übrigens seit sehr langer Zeit WordPress AntiVirus von Sergej Müller.
Ralf
17 Aug 11 at 22:01
@Ralf:
Also ich würde das Keinem anvertrauen wollen. Ausserdem was passiert, wenn die mal gehackt werden und überall wird ne Backdoor eingebaut? Und was ist, wenn man irgendwas im System ändern muss, weil ein Sonderfall X eintritt und der Webservice schreibt es dann wieder zurück?
Oliver
17 Aug 11 at 22:05
Super Thema! Mache ich mir auch schon seit geraumer Zeit Gedanken.
Der Einwand von Oliver hat mir eine spontane Idee in den Kopf gesetzt. Fände es interessant dazu Meinungen zu hören:
1) Rsync
Mittels rsync wird ein Sync des zu schützenden Rechners bzw. der zu schützenden Ordner (/etc, /var/www, …) gemacht. Der Output der gesicherten Daten wird an ein Skript (per Mail, scp, whatever) übermittelt.
2) Das Skript
Schaut im Rsync-Log, ob Dateien in „verbotenen“ Ordnern gesynct und damit geändert wurden.
Vorteil an der Vorgehensweise ist, dass ein Sync in den meisten Fällen eh gemacht wird. Das Scannen nach Änderungen, damit also „gratis“ ist.
Wie gesagt, spontaner Einfall. Vielleicht auch noch zu wenig Kaffee …
RennerC
18 Aug 11 at 07:36
@Oliver:
Es gibt Bankschließfächer in denen man seine Wertsachen deponieren kann. Und es gibt Tresore die man zu Hause aufstellen kann. Wenn du eine teure Uhr und zwei Goldketten hast, würdest du dir dafür einen eigenen Tresor kaufen oder würdest du dir ein Bankschließfach mieten?
Mit deiner Argumentation würdest du dir einen Tresor kaufen weil ja jemand in die Bank einbrechen könnte. Und dann würdest du vielleicht feststellen das man auch bei dir zu Hause einbrechen und den Tresor knacken könnte.
Im letzteren Fall wärst du auf dich alleine gestellt. Im ersten Fall würde die Versicherung der Bank einspringen und dich entschädigen.
Ein Webservice wäre natürlich nur für kleine Webseiten interessant die selber keine solche Infrastruktur aufbauen wollen oder können. Unter Umständen wäre es auch noch für Hoster interessant die ihren Kunden so einen Zusatznutzen anbieten können.
Auch die automatische Wiederherstellung wäre nicht pauschal für alle eine gute Lösung. Wenn man aber eh eine OpenSource Software (WordPress, Drupal, Joomla, usw.) einsetzt, läge es nahe das Originalarchiv quasi als Backup heranzuziehen. Denn gerade die Nutzer solcher Software würden oft wie der berühmte Ochs vorm Berg dastehen wenn sie eine Meldung bekommen das sich was verändert hat.
Man muss auch nicht jede einzelne Datei gleich stark überwachen. Die Core-Dateien könnten den Status „Unveränderlich“ bekommen, Themes und Plugins einen Status „kann sich ändern“. Core-Dateien werden automatisch wiederhergestellt, bei allen anderen gibt es nur eine Meldung (und ggf. die Option zur automatischen Wiederherstellung)
Ralf
18 Aug 11 at 09:05
Also, es gibt doch jetzt schon einen Haufen Tools, die alle unglaublich viel können und schön viele Abhängigkeiten haben (sind ja alle oben aufgezählt). Da wärs doch schon, wenn dieses Tool rein auf PHP basiert und schön schlank bleibt.
Wäre also gegen Rsync (ich z.B. verwende auf den meisten Servern Arbeitskopien mit SVN), fände aber eine Lösung schön, mit der man zentral X Server überwachen kann. So muss auch nur sichergestellt sein, dass z.B. auf dem Server der Mail-Versand richtig klappt.
Vielleicht eine Lösung, bei der man auf dem Zielserver nur eine Art Adapter hat, der die Hashes zur Verfügung stellt?
Christoph
18 Aug 11 at 09:54
Die Rsync Lösung setzt natürlich voraus, dass Du entweder einen eigenen Server hast oder der Webhoster rsync unterstützt…
ezod
18 Aug 11 at 09:59
Müsste man nicht auch ne schlanke Lösung mit GIT oder SVN hinbekommen? Gerade wenn man das sowieso schon zum deployen im Einsatz hat?
Am besten natürlich von einem zweiten Server aus.
Das müsste doch eigentlich schon mit einem kurzen Bash-Einzeiler zu machen sein?
Florian Heinze
18 Aug 11 at 10:03
Hi Michael,
ich hätte ne‘ kleine Ergänzung zu deinem Code:
Die FileWatcher.config.php macht ein $config = array();
Besser wäre: return array(/* config */);
Und in deiner readConfig ein $config = include($file);
Wobei ich jetzt mal im Raum so stehen lasse, dass die readConfig absolut gefährlich ist, da die Variable einfach so übernommen wird, wie sie kommt 🙂
PS. Magst du mal UnitTests zum FileWatcher auf Github ziehen? 🙂
Lg
Markus Handschuh
18 Aug 11 at 10:33
@Markus: Wußte garnicht dass das funktioniert mit dem include und dem return, hab ich so noch nie verwendet.
Was ist daran gefährlich? Es sind keine User-Eingaben (GET, POST etc) involviert, alles was da drinsteht ist vom Entwickler geschrieben. Soll man dem etwa nicht vertrauen? 😉
Unittests könnte man schreiben wenn es sich auch lohnt und es jemand verwendet, hätte ja sein können dass das eine total doofe Idee ist so ein Script. Aber ihr könnt natürlich auch helfen es zu erweitern etc, ein Fork ist nur 1 Klick entfernt.
Michael Kliewe
18 Aug 11 at 11:21
@Markus und Michael:
Kann ich auch noch nicht, finde ich aber für configs ziemlich praktisch! 🙂
Christoph
18 Aug 11 at 11:24
Was ist daran gefährlich? Es sind keine User-Eingaben (GET, POST etc) involviert,
filewatcher.php?password=boeseszeugs
Und schon sind „User-Inputs“ involviert. Wenn GET und/oder POST-Daten übergeben werden, sind sie nun einmal da. Da interessiert es nicht woher sie kommen.
Du nutzt zwei ungesicherte Tore über die Daten rein kommen. Die Frage ist also weniger ob sie genutzt werden, sondern wann jemand einen Weg findet diese Tore zu nutzen.
Ralf
18 Aug 11 at 11:37
Ähm, nö!
Oliver
18 Aug 11 at 11:40
@Ralf: Es ging gerade um das Laden der config Datei. Markus hat angemerkt dass die $config Variable in der readConfig Methode einfach so übernommen wird. Die Frage war warum das gefährlich ist, wenn der Entwickler meint ein system(‚rm -Rf /‘); in die Config schreiben zu müssen soll er das tun, da hilft auch die return-Lösung nicht. Da keine externen Eingaben (von nicht vertrauenswürdigen Stellen) involviert sind sehe ich die Gefährlichkeit nicht.
Bzgl. des Passwortes: Egal was du da reinschreibst, es wird nur für einen Stringvergleich genutzt (siehe _checkPassword() Methode). Wie will man das anders machen, bzw. wie soll jemand da etwas böses mit anstellen können?
Michael Kliewe
18 Aug 11 at 11:53
@Michael:
Markus ging es um die Zeile „include $configFilename“:
$fileWatcher->readConfig(‚FileWatcher.config.php‘)
Da ist also doch eine Benutzer-Eingabe vorhanden die blind übernommen wird. Woher kannst du dir sicher sein das immer eine Datei eingebunden wird die lediglich ein Array enthält? Die example.php wird wohl kaum jemand 1:1 übernehmen.
DU kannst dir sicher sein das in DEINEM Script alles so ist wie es sein soll. Aber kannst du auch sicher sein das später beim Endanwender alles noch so ist?
Beim POST/GET in etwa das gleiche. DU verwendest die Daten lediglich für einen String-Vergleich. Aber was ist wenn der Angreifer es schafft anstatt eines Passwort-Strings Binärdaten einzuschleusen? Bist du dir sicher das jeder Server auf dem das Script läuft damit richtig umgehen kann?
Sicherlich sind das oft weit hergeholte Sonderfälle. Aber was ist an einem
(string) $_GET['password']
so aufwendig? Mit ein paar Zeichen mehr Code holt man sich mehr Sicherheit ins Script.Bevor wir aneinander vorbei reden, es sind Grundsatzdinge. Include, POST und GET sind Einfallstore über die man jegliche Art an Daten übermitteln kann. Deswegen sollte man in PHP zumindest mal schauen ob auch das ankommt was man erwartet. Gerade wenn du ein Script schreibst das einem vor einem Hackerangriff schützen soll, ist es etwas merkwürdig das Daten vollkommen ungeprüft übernommen werden.
Die Config würde ich z.B. nicht als PHP-Datei anlegen, sondern als XML. Damit würde man schon mal verhindern das bösartiges Script über den Include eingeschleust werden kann. Das ist wirklich minimaler Aufwand. Anstatt
include $configfile
schreibt man$this->_config simplexml_load_file( $filename );
Ich habe mal einen Fork mit einer XML als Config angelegt. Da kann man sehen das es wirklich nur sehr wenig Aufwand ist eine anderes Format zu nehmen und dem Angreifer somit eine Tür vor der Nase zu zuwerfen.
Ralf
18 Aug 11 at 14:42
Nein, lies nochmal!
Das kann man doch nie, oder? So gesehen müsste WordPress sofort morgen dicht machen oder die nächsten 2 Jahre Sonderfälle bearbeiten.
Das bringt Dir aber sicherheitstechnisch überhaupt nichts. PHP fällt eh auf String zurück, alle anderen Formate sind restriktiver. Wenn überhaupt musst Du schon md5 oder was ähnliches nehmen.
Nehmen wir mal an, dass PHP es in den letzten 10 Jahren geschafft hat, einfache Vergleichsoperatoren sicher und binary safe zu gestalten.
Ja, aber hier ist ja durch die Config genau festgelegt, was man erwartet.
1. Was ist sicherer daran, die Datei über den Webserver lesbar zu machen?
2. Was soll das bringen? Wenn ich Dir eine manipulierte XML unterjubel hast Du genau den gleichen Effekt.
3. Woher weißt Du denn bitte, dass simplexml nicht allergisch auf irgendwelche UTF8 Sonderzeichen reagiert?
Oliver
18 Aug 11 at 15:17
@Ralf:
Wenn du von Benutzer redest meinst du also denjenigen, der mein Script benutzt. Müssen wir uns vor dem auch schützen? Wenn er meint böses tun zu wollen muss er nur system(‚rm -Rf /‘); einfügen. Wir müssen uns lediglich vor nicht vertrauenswürdigen Benutzern und deren Eingaben schützen, sprich Besuchern einer Webseite oder so. Einem Entwickler, der mein Script nutzt bzw. verändert und Zugriff auf den Quelltext hat nicht zu vertrauen finde ich wie gesagt krass, das geht gar nicht. Wenn er die Klasse mutwillig falsch benutzen will oder sie kaputtmachen will kann er das tun, dagegen kann sich kein Script der Welt wehren. Wogegen man sich wehren kann sind Dinge „von außerhalb“, sprich GET, POST und CLI Parameter. Da könnte man zwar noch Filter oder Validatoren einfügen, aktuell ist das aber nicht nötig da ich es nur für einen Vergleich nutze, das ist ungefährlich. Wenn jemand das Script erweitert und das Passwort gegen eine Datenbank prüft, dann muss derjenige sich selbst drum kümmern dass er es mysql_real_escaped etc.
Die $_GET Variablen sind IMMER Strings, ein Cast nach String bringt da absolut gar nichts.
include() ist ein Einfallstor wenn der Pfad aus einer nicht vertrauenswürdigen Quelle stammt oder zusammengesetzt wird. Tut er hier aber nicht, die Quelle ist hier der Entwickler dem man vertrauen kann (bzw. muss, siehe oben). Ansonsten dürfe niemand include(), require() etc. nutzen. Und schon garnicht wenn man einen Autoloader hat, der nimmt ja auch Klassennamen vom Entwickler entgegen und versucht eine Datei zu laden.
Woher soll das bösartige Script denn kommen? Den Pfad zur Konfigurationsdatei legt doch der Entwickler fest, ebenso den Inhalt der Konfigurationsdatei, beides ist nicht von außen veränderbar (über GET, POST, CLI-Parameter oder sonstwas). Wo ist da das Sicherheitsproblem?
Michael Kliewe
18 Aug 11 at 15:26
Ich möchte nur kurz etwas einwerfen:
> Die $_GET Variablen sind IMMER Strings, ein Cast nach > String bringt da absolut gar nichts.
Es können auch Arrays sein.
Ansonsten bin ich voll bei euch, Michael und Oliver 🙂
Christoph
18 Aug 11 at 15:31
Es ist eine Klasse die andere in ihre Scripte einbauen können? Gut.
Jemand schreibt ein Plugin/Erweiterung für eine OpenSourceSoftware. Und dann steht dort plötzlich solche eine Zeile im Script:
$options = array( 'configFilename' => $_GET['config'] );
$filewatcher = new FileWatcher( $options );
Wenn du immer nur bis zur nächsten Codezeile denkst, gebe ich dir recht. Ich würde mich aber nicht darauf verlassen das andere Programmierer sauber arbeiten.
Und weil es eh nix bringt, machen wir generell nichts mehr in Sachen Sicherheit. Zum Beispiel sicher gehen das man den Datentyp bekommt den man erwartet.
Der Typecast bringt an dieser Stelle vielleicht nicht viel. Jedoch müsste man, wenn man etwas konsequent ist, die Daten auch noch mal intensiver überprüfen und z.B. Null Bytes entfernen.
WordPress hat ja z.B. seine DB-API nicht weil es mit so vielen verschiedenen DBs zusammen arbeitet, sondern weil die API sich um das prüfen und desinfizieren der DB-Anfragen kümmert. Und diese DB-Anfragen kommen alle vom ach so vertrauenswürdigen Entwickler. Könnte man allen Entwicklern so blind vertrauen, bräuchte man den ganzen Aufwand nicht.
In WordPress wird mittlerweile jeder Pups validiert. Warum wohl? Weil alle Plugin- und Theme-Entwickler so vertrauenswürdig sind??
Ralf
19 Aug 11 at 00:58
@Ralf: OK, einigen wir uns darauf dass man natürlich mehr machen könnte, aber nicht muss. Um aber für Erweiterungen anderer, die eventuell etwas vergessen oder übersehen könnten, eine sichere Grundlage zu bauen könnte man an einigen Stellen etwas ändern.
Es ist die erste Version, ich habe es für diesen Artikel hingezimmert, und es sind keine akuten Sicherheitslücken drin. Man muss also nicht handeln, aber man könnte. Soweit gebe ich dir natürlich Recht. Wir könnten einige dutzend Zeilen mehr einbauen, ein PHPIDS davorschalten, Filter, Validatoren, Casts, Typprüfungen usw usw. Für eine Diskussion, ob ein solches Script überhaupt nützlich ist und eingesetzt werden könnte oder würde nicht zwingend nötig. Sonst müßte ich alle Code-Snippets hier im Blog überarbeiten und damit rechnen dass sie jemand einsetzt und erweitert und dabei Fehler macht, und ich dann für die Fehler des anderen verantwortlich gemacht werde.
Ich freue mich natürlich gern auf Pull-Requests 😉 Die XML-Config hast du ja schon fertig wie ich sehe, ich überlege gerade ob man dem Benutzer die Wahl lassen sollte ob er eine Array-Konfiguration oder eine XML-Konfiguration nutzen möchte, sprich eine kleine Abfrage reinbaut: Wenn Extension von $configFilename .xml ist dann deinen Loader, sonst den alten (bzw. den von Markus mit dem return).
Michael Kliewe
19 Aug 11 at 10:12
Persönlich fände ich es dann ja am elegantesten das Laden der Konfiguration aus der Klasse rauszuhalten: Aus ->readConfig() wird dann:
->setConfig(include(‚file.php‘)) // da drin dann mit return …
->setConfig(array(…))
->setConfig(parse_ini_file(‚file.ini‘))
->setConfig(json_decode(file_get_contents(‚file.json‘)))
->setConfig(yaml_parse_file(‚file.yaml‘))
(XML ist dann etwas komplizierter, weil man es erst in ein Array konvertieren muss und das nicht mit einer einzelnen Methode geht(?). Aber das zeigt dann auch gleich das XML aus Speicher/Performance-Sicht vielleicht nicht das optimalste ist.)
So hat man keine Abhängigkeiten, es ist Modular und jeder kann es an die persönlichen Anforderungen anpassen. Und wenn einem das nicht schön genug aussieht kann man immer noch eine Wrapper-Methode als ->readConfig() oder ->readXml() anfügen.
Florian Heinze
19 Aug 11 at 11:11
@Ralf:
WordPress sorgt dafür, dass die Entwicklung von Plugins und Themes möglichst sicher ist. Die DB Api und andere Hilfsmittel muss man aber nicht benutzen. Wenn ich das will, kann ich in jedes Plugin rm -fr / oder DROP DATABASE wordpress einbauen. Deshalb sagte ich ja, wenn die das so gestalten wollten, dass es 100%ig sicher ist, dann wären sie die nächsten 2 Jahre allein damit beschäftigt.
Ja, aber Du könntest den Code in Deinen Variabeln haben und damit z. B. PHP Code in die Logdateien schreiben lassen, um sie dann auszuführen. Nur jetzt als einfaches Beispiel, ohne genauer alle Möglichkeiten ausgelotet zu haben. Ich bin ja einverstanden, dass man externe Eingaben entsprechend absichert, aber bei internen ist das unnötig und die zwei externen, die es hier gibt sind abgesichert, weil entweder es stimmt oder es stimmt nicht.
@Ralf/Michael/Florian
Das Problem, dass ich bei xml sehe ist, dass man es nicht ausreichend nach aussen absichern kann. Dann hast Du zwar minimal sichere Variabeln, aber die Config ist übers Web lesbar. Dann muss man erstmal jedem erklären, dass man das ausserhalb docroot ausführen muss oder in einem geschütztem Ordner, aber ich würde nicht darauf vertrauen, dass jemand die readme liest oder jeder in der Lage ist ne htaccess aufzusetzen. Mit parse_ini_file könnte man es wenigstens noch vor direktem Zugriff schützen, aber so oder so sehe ich da keinen Vorteil zur PHP Version.
Oliver
19 Aug 11 at 12:33
Sehe ich genauso. Bei so einem kleinen Tool, bei welchem man die Config eh nicht für etwas anderes braucht, lohnt sich kein anderes übergreifendes Format wie XML.
Mittlerweile versuche ich alles low-level zu halten, weil man alles andere wieder lernen müsste. Der eine Entwickler kennt sich gut mit JSON aus, der andere mit XML, der andere mit YAML, und jedes Tool benutzt etwas anderes. Aber PHP können sie alle. 🙂
Mittlerweile krieg ich auch Würgreiz, wenn ich dann so etwas wie Typo-Script sehe. Gebaut, um es einfacher zu machen… aber was dort herausgekommen ist 🙁
Christoph
19 Aug 11 at 12:46
@Christoph
Stimme Dir zu. Auch mein Motto ist „Back to the Roots“! Was man weg lassen kann, weg lassen. Daher ja auch mein Vorschlag. Denn der Großteil würde so ein Script im Ende wohl so verwenden das er die Konfiguration einfach in der Datei in ein Array schreibt und übergibt. Das ist einfach, übersichtlich, stabil und zumindest die Sicherheitsdiskussionen zu diesem Beitrag erübrigen sich im groben 😉
Florian Heinze
19 Aug 11 at 13:12
Wie wäre es den wenn Filewatcher, verschiedene Config typen (yaml|xml|ini|php) unterstützt? Wäre doch genial, oder? Natürlich sollte der Entwickler, der das Skript benutzt, die Config – Datei via htaccess schützen. Und es wäre auch praktisch wenn die Datei als Phar Archiv verfügbar wäre, dann wäre es für den Endanwender viel komfortabler. Und wir könnten auch verschiedene Libraries einbinden.
Markus Bachmann
23 Aug 11 at 08:37
Die Variante über FTP klingt ganz gut, allerdings hab ich teilweise kein FTP oder WebDav oÄ zur Verfügung.
Daher meine Frage: Angenommen man lagert auf dem Webspace ein Script, dass einen Hash der zu überwachenden Dateien erstellt, diesen Hash aber kryptographisch so absichert, dass er eine Zeitbeschränkung hat. Der Angreifer könnte jetzt nicht einfach die Ausgabe kopieren, da der Hash abläuft. Und der Angreifer kann das Script auch nicht ändern, da sich dann der Hash auch ändern würde. Hab ich was übersehen?
Chris
23 Aug 11 at 20:06
@Chris
Ja, hast Du. Du musst ja dann ein Verfahren vorschreiben, dass ein Angreifer ggf. auch kopieren kann. Dieses Verfahren muss ja irgendwo hinterlegt sein. Du kannst auch das ganze Verfahren komplett aushebeln, indem Du einfach nicht mehr prüfen lässt. Der einzige Weg, das zu umgehen wäre ein entfernter Login, so dass ein Angreifer beide Server hacken muss.
FTP ist Mist, weil unverschlüsselt, aber SFTP o. ä. hat man ja eigentlich immer.
Oliver
23 Aug 11 at 20:34
Die Idee ist gut.
Angst vor nachträglicher Modifikation ist denke ich nicht angebracht. Zumindest nicht, wenn man nicht mit den Original-Dateinamen arbeitet. Es reicht ja auch, wenn man die Klasse irgendwo in das System integriert, wo sie keiner erwartet. Angreifer haben keine Zeit alle Dateien zu durchforsten. Meist sind das Bots, die einfach nur vorfertigte Routinen abarbeiten z.B. in dem sie Templates bearbeiten.
Was mir nicht gefällt ist die Vergleichsfunktion. Hier wird ein Array genutzt, das entsprechend groß wird, umso größer das Projekt ist. Vielleicht lohnt hier auch ein Regex, wie hier zu sehen:
http://www.php.net/manual/de/class.recursivedirectoryiterator.php#97228
So könnte man nur die Dateien abgleichen, die bestimmte Endungen besitzen (.php, .txt, .tpl, .htm, .html, .inc, etc.)). Wobei man natürlich alles includieren kann und die Endung im Grunde Wurst ist, aber da ist auch wieder das Problem: Wenn man Verzeichnisse ausschließt, z.B. weil da Fileuploads zulässig sind, warum sollte der Schadecode nicht genau in diesem Verzeichnis landen, das man nicht prüft. Daher macht es schon Sinn sich nur auf „gefährliche“ Dateiendungen zu konzentrieren.
In jedem Fall ist es eine Idee, die man ausbauen kann.
Was ich auch noch vorschlagen würde ist ein Filter auf ausführbaren Code innerhalb von Dateien, wo er nicht hingehört. Wie z.B. der Trick im JPG-Kommentarbereich PHP-Code einzuschleusen. Allerdings erfordert das wiederum noch mehr Performance und eine solche Lücke erfordert eine weitere Lücke, nämlich das Einbinden von egal welcher lokalen Datei. So einen groben Fehler sollte es ja eh nie geben.
Marc Gutt
28 Mrz 12 at 17:58