wie Sicherheit eines Scriptes erhöhen
Andreas Cloos
- php
Hallo Zusammen,
ich mal wieder und gleich mit einer Frage ;-) Da ich jetzt mal wieder was in PHP schreiben müßte, was ich aber länger schon nicht mehr getan habe, habe ich als Fingerübung zum "Wiederwarmwerden" mal ein kleines Gästebuchscript geschrieben.
Selbiges kann ich mangels FTP-Zugang gerade nirgendwo online stellen und will auch nicht die rund 200 Zeilen Code hier pasten (kann es aber auf Wunsch gerne tun), aber mich würde mal _grundsätzlich_ interessieren, was man so sicherheitshalber alles bedenken sollte.
Also: Pflichtfelder-Check erfolgt mit Javascript (klar, kann man abschalten, aber dann werden im Zweifelsfall eben nur leere Werte in die DB geschrieben - das ist kein Sicherheitsproblem). Die Prüfung zusätzlich mit PHP zu realiseren wäre einfach.
Auf Benutzereingaben wende ich folgendes an (außer Punkt 2, den nur da wo notwendig):
1. Leerzeichen am Anfang und am Ende entfernen (trim())
2. Falls die URI der Homepage ohne führendes "http://" eingegeben wurde, wird selbiges vor die URI eingefügt. Sollte man die angegebene URI auf Erreichbarkeit prüfen?
3. HTML-Tags entfernen (strip_tags())
4. Über die IP-Adresse wird ein erneutes Absetzen eines Eintrages (F5-Taste im IE oder Absicht) für einen frei definierbaren Zeitraum gesperrt (derzeit 300 Sekunden) - wenn zwei Benutzer die hinter ein und denselben Proxy sitzen posten wollen, habe ich natürlich ein Problem - kennt einer eine bessere Lösung? Session-Handling fände ich fast übertrieben und ist doch auch durch "Browser schließen, Browser öffnen" zu umgehen, oder?
Da das komplette Script in einer Datei enthalten ist, gebe ich in der URL einen Parameter mit um kenntlich zu machen, ob beim Aufruf etwas in die DB geschrieben werden soll oder nicht (?insert=yes). Der Parameter kann nat. einfachst manipuliert werden, allerdings gibt es nur diesen einzigen erlaubten Wert, weicht er ab, wird nichts in die DB geschrieben und es werden nur die bisherigen Einträge gezeigt.
Weiterhin gebe ggf. ich zum Blättern zwei Parameter in der URL mit (start und limit in der SQL-Query), sollten diese manipuliert werden, kann dann etas außer einer evtl. ungültigen Abfrage Passieren?
Also: Habe ich grundsätzlich 'was vergessen, gibt es etwas an an das man unbedingt noch denken sollte?
Habe noch nie ein Script für den "Masseneinsatz" in der bösen weiten Welt geschrieben, daher habe ich mich noch nie mit solchen Sicherheitsproblemem auseinandersetzen müssen - dies nur als pauschale Vorabentschuldigung falls ich was ganz selbstverständliches vergessen haben sollte ;-)
Hallo Andreas,
Auf Benutzereingaben wende ich folgendes an (außer Punkt 2, den nur da wo notwendig):
- Leerzeichen am Anfang und am Ende entfernen (trim())
Immer.
- Falls die URI der Homepage ohne führendes "http://" eingegeben wurde, wird selbiges vor die URI eingefügt. Sollte man die angegebene URI auf Erreichbarkeit prüfen?
Würde ich so machen.
- HTML-Tags entfernen (strip_tags())
Auf jeden Fall. Evntl. am Ende die Zeilenumbrüche beibehalten...
- Über die IP-Adresse wird ein erneutes Absetzen eines Eintrages (F5-Taste im IE oder Absicht) für einen frei definierbaren Zeitraum gesperrt (derzeit 300 Sekunden) - wenn zwei Benutzer die hinter ein und denselben Proxy sitzen posten wollen, habe ich natürlich ein Problem - kennt einer eine bessere Lösung? Session-Handling fände ich fast übertrieben und ist doch auch durch "Browser schließen, Browser öffnen" zu umgehen, oder?
Was auch geht: Nach dem Submit das Script verlassen und auf eine Antwortseite umleiten - da kann jeder F5 drücken wie er will.
Weiterhin gebe ggf. ich zum Blättern zwei Parameter in der URL mit (start und limit in der SQL-Query), sollten diese manipuliert werden, kann dann etas außer einer evtl. ungültigen Abfrage Passieren?
Ich mach das immer so:
jeder Parameter like ?new=1 wird über eine Kontrollstruktur (if -elsif)abgefragt. Am Ende dieser Kontrollstruktur steht ein else "Parameter nicht erlaubt" o.ä.
Also: Habe ich grundsätzlich 'was vergessen, gibt es etwas an an das man unbedingt noch denken sollte?
hmm...,der Benutzer sollte auswählen können ob seine eMailAddr sichtbar sein soll oder nicht.
Viele Grüße, Rolf
Hallo Zusammen,
- Falls die URI der Homepage ohne führendes "http://" eingegeben wurde, wird selbiges vor die URI eingefügt. Sollte man die angegebene URI auf Erreichbarkeit prüfen?
Würde ich so machen.
Problem ist dann immer noch, dass es ja gar nicht seine eigene Domain sein muß, auf die er da verlinkt.
- HTML-Tags entfernen (strip_tags())
Auf jeden Fall. Evntl. am Ende die Zeilenumbrüche beibehalten...
HTML-Tags sollen grundsätzlich nicht zugelassen werden, genauso wenig wie diese blöden Smilies, die einen aus jedem BB-Gästebuch anglotzen... Es gibt nur ein mehrzeiliges Eingabefeld, dessen Inhalt speichere ich ohne Tags aber ansonsten unbearbeitet in die DB, erstze dann aber für die Anzeige \nl durch <br>. Dünkt mir ok.
Was auch geht: Nach dem Submit das Script verlassen und auf eine Antwortseite umleiten - da kann jeder F5 drücken wie er will.
Dann habe ich das rumgefrickele mit der 2. Seite, die IP-Sperre will ich verwenden um "Spammer" ein wenig abzuschrecken.
Ich mach das immer so:
jeder Parameter like ?new=1 wird über eine Kontrollstruktur (if -elsif)abgefragt. Am Ende dieser Kontrollstruktur steht ein else "Parameter nicht erlaubt" o.ä.
Guter Plan.
hmm...,der Benutzer sollte auswählen können ob seine eMailAddr sichtbar sein soll oder nicht.
Ist eh kein Pflichtfeld. Nur wenn überhaupt etwas drin steht, wird auf das "@" und mind. einen "." geprüft. Das war die einfachste JS-basierte email-Adressen-Prüfung die mir einfiel.
Jow,
da fällt mir nochwas ein:
Ganz sicher gehst du, wenn der Eintrag nicht ins GB erfolgt sondern dir als Mail geschickt wird. Dann brauchst du noch ein kleines Script was dir im *Compliant Case* den Eintrag in die DB tut...
Weiteres: REFERER prüfen (nur von deiner URI möglich), ist aber nicht sonderlich sicher. Zeitsperre ist besser(damit dich keiner zumüllt).
Rolf
hi,
Was auch geht: Nach dem Submit das Script verlassen und auf eine Antwortseite umleiten - da kann jeder F5 drücken wie er will.
Dann habe ich das rumgefrickele mit der 2. Seite, die IP-Sperre will ich verwenden um "Spammer" ein wenig abzuschrecken.
du kannst auch nach dem eintragen in die DB wieder auf exakt die gleiche seite weiterleiten, die dann wieder in den einträge-anzeigen modus wechselt.
erfordert kein weites script, schützt aber trotzdem vor doppeleinträgen durch [F5].
gruss,
wahsaga
Moin!
- Leerzeichen am Anfang und am Ende entfernen (trim())
Immer.
Warum? Das ist nichts, was irgendeine Sicherheit erhöht, das ist ausschließlich dazu da, "schöne" Strings zu haben - wobei "schön" eben definiert ist als "keine Leerzeichen drumherum". Andere Schönheitsideale führen zu anderen Anforderungen (beispielsweise: Postleitzahlen bestehen nur aus Ziffern - was in Deutschland funktioniert, international aber Probleme machen wird).
- Falls die URI der Homepage ohne führendes "http://" eingegeben wurde, wird selbiges vor die URI eingefügt. Sollte man die angegebene URI auf Erreichbarkeit prüfen?
Würde ich so machen.
Würde ich nicht so machen. Also die Prüfung, ob die URI erreichbar ist. Kostet nur Zeit, verursacht Traffic und ist nichtssagend. Letzt war über einen Tag (warum auch immer) eine meiner Domains nicht erreichbar, weil der Server platt war. Dürfte ich dann nicht die Domain eintragen, nur weil der Schaden nicht sofort behoben war?
- HTML-Tags entfernen (strip_tags())
Auf jeden Fall. Evntl. am Ende die Zeilenumbrüche beibehalten...
HTML-Tags _nicht zulassen_ wäre die bessere Alternative. Aber dann doch bitte nicht mit dieser überaus _dummen_ Funktion strip_tags(). Die ist nämlich so strohdumm, die löscht aus dem String alles raus, was zwischen den Zeichen "<" und ">" steht - auch wenn dazwischen absolut kein HTML-Tag steht.
Besser: Die Zeichen "<", ">" und "&" in Entities umwandeln mit htmlspecialchars(). Dann kann man diese Zeichen (und beispielsweise auch HTML-Quellcode) ins Gästebuch eintragen, ohne dass sie als HTML wirksam werden.
Als Gimmick kann man BB-Code anbieten. Christian Seiler hat dafür eine nette Klasse geschrieben: http://www.christian-seiler.de/projekte/php/bbcode/
- Über die IP-Adresse wird ein erneutes Absetzen eines Eintrages (F5-Taste im IE oder Absicht) für einen frei definierbaren Zeitraum gesperrt (derzeit 300 Sekunden) - wenn zwei Benutzer die hinter ein und denselben Proxy sitzen posten wollen, habe ich natürlich ein Problem - kennt einer eine bessere Lösung? Session-Handling fände ich fast übertrieben und ist doch auch durch "Browser schließen, Browser öffnen" zu umgehen, oder?
Was auch geht: Nach dem Submit das Script verlassen und auf eine Antwortseite umleiten - da kann jeder F5 drücken wie er will.
Wer Gästebücher fluten will, der wird nicht F5 drücken, sondern ein Skript einsetzen, was das für ihn übernimmt.
Die Weiterleitung auf eine Antwortseite ist aber dennoch gut, weil unabsichtliches Neuladen vermieden wird. Also unbedingt machen.
Sessions bringen hier keine Abhilfe gegen böse Skripte, denn die eröffnen einfach pro Request eine neue Session. Und wenn es notwendig ist, zuerst die Formularseite abzurufen, damit in der Session die Postingfähigkeit freigeschaltet wird, werden sie auch dies tun. Bringt also kein Mehr an Sicherheit, nervt nur die Benutzer und ist aufwendig herzustellen.
Weiterhin gebe ggf. ich zum Blättern zwei Parameter in der URL mit (start und limit in der SQL-Query), sollten diese manipuliert werden, kann dann etas außer einer evtl. ungültigen Abfrage Passieren?
Aufpassen, wie diese Parameter dann in den SQL-Query gelangen. Du mußt sicherstellen, dass nur die Zahlenparameter weiterkommen, aber keinerlei Text. Eine gute Idee wäre, intern eine Konstante "Postings pro Seite" zu haben und extern nur die fortlaufende Seitennummer aufzurufen (1, 2, 3,...), welche intern dann in entsprechende LIMIT-Parameter umgesetzt werden.
Ich mach das immer so:
jeder Parameter like ?new=1 wird über eine Kontrollstruktur (if -elsif)abgefragt. Am Ende dieser Kontrollstruktur steht ein else "Parameter nicht erlaubt" o.ä.
Was spricht gegen Ignorieren? Ist ja nun kein großer Unterschied, ob ein Parameter mit einer Fehlermeldung bedacht wird, oder ob er in so einem Fall einfach unwirksam ist. Ist zumindest für den Angreifer dann nicht sofort ersichtlich, weil er trotzdem eine nette Seite erhält.
- Sven Rautenberg
Hallo Zusammen,
- HTML-Tags entfernen (strip_tags())
Auf jeden Fall. Evntl. am Ende die Zeilenumbrüche beibehalten...HTML-Tags _nicht zulassen_ wäre die bessere Alternative. Aber dann doch bitte nicht mit dieser überaus _dummen_ Funktion strip_tags(). Die ist nämlich so strohdumm, die löscht aus dem String alles raus, was zwischen den Zeichen "<" und ">" steht - auch wenn dazwischen absolut kein HTML-Tag steht.
Besser: Die Zeichen "<", ">" und "&" in Entities umwandeln mit htmlspecialchars(). Dann kann man diese Zeichen (und beispielsweise auch HTML-Quellcode) ins Gästebuch eintragen, ohne dass sie als HTML wirksam werden.
Habe das mal mal gegenübergestellt und mir angeguckt, was ich eher mag:
Dies hier:
<?php
$test = "<a href='http://www.gmx.de'>GMX</a>";
$test1 = $test;
echo $test;
echo "<br>";
$test = htmlspecialchars($test);
echo $test."<br>";
$test1 = strip_tags($test1);
echo $test1;
?>
führt zu:
GMX
<a href='http://www.gmx.de'>GMX</a>
GMX
Wobei das erste ein Link ist, das letzte nur Text und das zweite einfach bescheiden ausschaut, aber dafür erkennen läßt, was der Poster wollte (verlinken, ein anderes Hintergrundbild einbauen, usw...). Hm, ich weiß noch nicht genau, was mir am besten gefällt.
Als Gimmick kann man BB-Code anbieten. Christian Seiler hat dafür eine nette Klasse geschrieben: http://www.christian-seiler.de/projekte/php/bbcode/
Das schaue ich mir mal an, weiß aber nicht ob ich das will.
Wer Gästebücher fluten will, der wird nicht F5 drücken, sondern ein Skript einsetzen, was das für ihn übernimmt.
Die Weiterleitung auf eine Antwortseite ist aber dennoch gut, weil unabsichtliches Neuladen vermieden wird. Also unbedingt machen.
Also nach dem Submit auf Gültigkeit prüfen, Umwandlungen vornehmen, eintragen und sofort neuen Header senden?
Weiterhin gebe ggf. ich zum Blättern zwei Parameter in der URL mit (start und limit in der SQL-Query), sollten diese manipuliert werden, kann dann etas außer einer evtl. ungültigen Abfrage Passieren?
Aufpassen, wie diese Parameter dann in den SQL-Query gelangen. Du mußt sicherstellen, dass nur die Zahlenparameter weiterkommen, aber keinerlei Text. Eine gute Idee wäre, intern eine Konstante "Postings pro Seite" zu haben und extern nur die fortlaufende Seitennummer aufzurufen (1, 2, 3,...), welche intern dann in entsprechende LIMIT-Parameter umgesetzt werden.
Klingt nachvollziehbar und simpel. Danke soweit.
hi,
Habe das mal mal gegenübergestellt und mir angeguckt, was ich eher mag:
Dies hier:
<?php
$test = "<a href='http://www.gmx.de'>GMX</a>";
$test1 = $test;
führt zu:
GMX
Wobei das erste ein Link ist
Hm, ich weiß noch nicht genau, was mir am besten gefällt.
die erste möglichkeit gefällt dir garantiert nicht (der thread ging ja ums thema sicherheit).
sonst sorge ich durch meine eingabe dafür, dass $text ein javascript enthält, dass die seite schliesst, oder gar in einer schleife 500 popups öffnet.
gruss,
wahsaga
Hallo Zusammen,
die erste möglichkeit gefällt dir garantiert nicht (der thread ging ja ums thema sicherheit).
ACK. Ich hätte dazu schreiben sollen, daß es eigentlich nur eine Entscheidung zw. 2 und 3 sein kann, wobei die Kriterien Sicherheit, Funktionalität und Optik sind.
Dabei spricht außer der Arbeit bei der Einbindung der von Sven verlinkten Klasse eigentlich alles für BB, die Optik gegen Möglichkeit (Links etc. im Klartext) und die Sicherheit gegen 1 (HTML-Quellcode einbinden). Hm... Mal sehen, wieviel Arbeit ich mir damit machen will.
Moin!
ACK. Ich hätte dazu schreiben sollen, daß es eigentlich nur eine Entscheidung zw. 2 und 3 sein kann, wobei die Kriterien Sicherheit, Funktionalität und Optik sind.
Du hast dir einen "passenden" Text herausgesucht. Aber nimm mal folgenden String und lasse ihn durch dein Testprogramm laufen:
"Für die Werte < 5 und > 2 soll ein Hinweis ausgegeben werden."
Ergebnis wird sein:
strip_tags() löscht den Bereich zwischen < und > raus. Und das kannst du nicht gewollt haben.
Deshalb besser: Informiere den Benutzer, dass HTML nicht erlaubt ist, oder gib ihm vor dem endgültigen Abschicken eine Voransicht - dann sieht er, dass sein toller Link im Klartext ausgegeben wird, weil du htmlspecialchars() benutzt.
Dabei spricht außer der Arbeit bei der Einbindung der von Sven verlinkten Klasse eigentlich alles für BB, die Optik gegen Möglichkeit (Links etc. im Klartext) und die Sicherheit gegen 1 (HTML-Quellcode einbinden). Hm... Mal sehen, wieviel Arbeit ich mir damit machen will.
Die Klasse selbst ist schnell heruntergeladen und eingebunde. Außerdem ist ganz unten auf der Erklärungsseite ein Beispiel, welches IIRC Fettschrift und Links ermöglicht. Zumindest das ist ja schon mal ganz nett, oder?
- Sven Rautenberg
Mahlzeit Andreas,
hab noch eine Idee:
Jeder neue Eintrag in dein GB setzt zum Datensatz ein weiteres Feld:
visible=0
Du schaust nun von Zeit zu Zeit mit einem weiteren CGI(passwortgeschützt) in das GB ob sich da irgendwelche Schmutzfinken verewigt haben oder ob die Einträge compliant sind... hier kannst du entweder löschen oder den flag visible=1 setzen. GGf. wird dir bei einem neuen Eintrag eine Mail geschickt.
So werde ich das in zukunft mit meinem GB auch machen.
Rolf