IPC 2014

25 gefährlichste Programmierfehler

Wahrscheinlich programmiert ihr genauso gern wie ich. Allerdings ist nicht alles, was auf den ersten Blick funktioniert, wirklich gute Software. Nicht umsonst benehmen sich Software-Tester oft besonders rüde, denn fehlerhafte Eingaben, überlange Strings, nicht initialisierte Variablen können unvorhersehbare Zustände hervorrufen – und diese sind nicht selten von großer wirtschaftlicher Bedeutung.

Das SANS Institute hat nun mal mehr als 30 Firmen befragt, die sich mit der Sicherheit von Software beschäftigen. Herausgekommen ist eine Top 25 der häufigsten Programmierfehler. Damit ihr euch nicht durch den riesigen Artikel wühlen müsst (es sei denn, ihr wollt das), habe ich die Liste hier kurz übersetzt und auf die Webentwicklung übertragen:

  1. Unsaubere / keine Input-Validierung: Eingaben müssen geprüft werden (und nicht nur Formulare sondern auch $_GET-Parameter), damit sie korrekt verarbeitet werden können.
  2. falsche Kodierung / kein Escapen bei der Ausgabe: fast genauso schlimm wie 1., denn dadurch werden vor allem XSS- und CSRF-Angriffe möglich.
  3. fehlendes Escaping bei SQL-Abfragen: jede Variable in einer SQL-Abfrage sollte vorher mit mysql_real_escape_string() behandelt werden, damit SQL Injections vermieden werden.
  4. XSS-Angriffe nochmal – habe ich bei 2. schon mit abgefrühstückt
  5. Validierung von Aufrufen von OS-Befehlen: wenn von Programm 1 ein anderes Programm aufgerufen wird, geschieht dies normalerweise auch mit den Rechten von Programm 1. Wer bei der Rechtevergabe sehr großzügig war und / oder den Aufruf über (ungeprüfte) Variablen parameterisiert, kann dem Betriebssystem und auch dem Server schnell Schaden zufügen.
  6. Klartext-Übertragung sensibler Informationen: Sollte man nicht tun, dafür gibts im WWW Dinge wie SSL und TLS. Aber es sei trotzdem darauf verwiesen, dass auch heute noch viele Klartextprotokolle im Einsatz sind. Ein Beispiel dafür ist FTP – immer, wenn ihr euch über normales FTP mit eurem Server verbindet, kann jedermann die Zugangsdaten mitlesen.
  7. CSRF nochmal – hatte ich ja auch bei 2. schon
  8. Race Conditions: wenn das Ergebnis einer Operation von mehreren Einzeloperationen, die (pseudo-)parallel ausgeführt werden, abhängig ist, besteht eine Wettbewerbssituation (engl. Race Condition). Fehler dieser Art sind sehr schwer zu beheben, da die Symptome manchmal auftreten und ein anderes Mal nicht. Gerade dieser Umstand macht sie so gefährlich.
  9. informationsreiche Fehlermeldungen: Wenn in einem Produktivsystem Fehlermeldungen zu viele Informationen enthalten, kann dies Hackern helfen, eure Architektur bzw. euer Script zu rekonstruieren und so auch auf Schwachstellen zu treffen. Während der Entwicklung ist ein Debug-Modus deshalb hilfreich, in der Produktion sollte er aber dringend deaktiviert werden. Überhaupt sollte einem Angreifer möglichst wenig verraten werden. Dazu zählt beispielsweise die eingesetzte Server-Software (Apache, PHP, MySQL), eingesetzte Verschlüsselungsverfahren usw. Je mehr ein Angreifer weiß, desto leichter ist ein Angriff. Wenn ein Angreifer eine liegen gelassene Seite mit dem Befehl phpinfo() findet, sollte man sich Sorgen machen.
  10. Buffer Overflow: Ein Speicherüberlauf geschieht dann, wenn für eine Variable weniger Speicher reserviert wurde, als ihr zugewiesener Inhalt benötigt. Das ist das Grundproblem von Anwendungen, die auf der Von-Neumann-Architektur basieren (da dort per Definition Variableninhalte und Befehle im gleichen Speicher sind – vgl. Harvard-Architektur)
  11. Speicherort für kritische Daten: sensible Daten sollten immer dort gespeichert werden, wo ein Angreifer möglichst schwer herankommt. Dies kann zum Beispiel ein Verzeichnis sein, das über dem htdocs-Verzeichnis des Apache liegt – also auf jeden Fall außerhalb des öffentlich zugänglichen Bereiches.
  12. parameterisierter Dateiname: Wenn eine Datei eingelesen wird und der Dateiname über eine Variable gesteuert wird, kann ein Angreifer diesen möglicherweise verändern. Noch schlimmer ist dies bei Schreiboperationen. Wenn der Dateiname einer Schreiboperation (neue Datei erstellen) anfällig ist, kann ein Angreifer leicht Tausende solcher Dateien erstellen – bis die Festplatte voll ist und erstmal gar nichts mehr geht.
  13. relativer, parameterisierter Verzeichnis-Pfad: wenn man eine Datei über einen relativen Pfad einliest und diesen Pfad mit einer Nutzereingabe parameterisiert, kann das dazu führen, dass der Nutzer zu jedem Pfad gelangen kann, wo er hin möchte (und die Rechte der Anwendung es nicht einschränken). Wenn man also so etwas hat:
    include($username."/datei.xyz");

    Wenn der Angreifer nun einen Weg findet, den Usernamen beispielsweise "../../" zu nennen, ist er auf einmal ganz woanders. Hier spielt also auch die Input-Validierung wieder mit hinein.

  14. Dynamisch generierter Code: Aus Bequemlichkeit (SANS nennt es "weil es cool ist") bauen manche Entwickler dynamisch generierten Code in ihre Anwendungen ein und führen ihn dann beispielsweise per eval() aus. Darüber freuen sich natürlich auch Angreifer, denn nun müssen sie nur noch versuchen den auszuführenden Code in ihrem Sinne zu verändern.
  15. Programme / Code downloaden ohne Integritätscheck: jeder Download, der besonders tolle Dinge verspricht, sollte geprüft werden – auf Viren sowieso, aber auch aber per Hash-Code der Datei. Dieser muss übereinstimmen mit dem vom Anbieter angegebenen Hash-Wert. Tut er dies nicht, wurde die Software verändert und ihr solltet die Finger davon lassen.
  16. unsauberes / fehlendes Freigeben von Ressourcen: wenn Ressourcen nicht mehr benötigt werden, sollten sie ordnungsgemäß wieder freigegeben werden. Dazu gehören Filehandles, Datenbank-Verbindungen, temporäre Variablen usw. Heutzutage kümmert sich meistens die Programmiersprache selbst darum (Garbage Collection), allerdings sollte man vorher prüfen, ob dem auch wirklich so ist.
  17. fehlende Initialisierung von Variablen: Ein großes Problem bei PHP-Anwendungen. Deshalb während der Entwicklung immer auch Fehler vom Typ E_NOTICE anzeigen lassen (in der Produktion dann natürlich nicht mehr – siehe Punkt 9).
  18. falsche Berechnungen: Berechnungsfehler können weitreichende Folgen haben. Die Ursachen sind oft Rundungsfehler, nicht selten durch die Wahl des falschen Datentyps. SANS zählt hierunter auch nicht abgefangene Division-durch-Null-Fehler und zu geringe Speicherreservierung. Blöd werden solche Fehler dann, wenn sie von Hackern ausgenutzt werden. Dann entstehen schon mal negative Preise oder es werden tausende Überweisungen hin und her getätigt, um Rundungsfehler auszunutzen.
  19. Unsaubere Rechtevergabe: Rechte sind eine tolle Sache, um ein System abzusichern. Allerdings müssen sie auch korrekt gesetzt und gepflegt werden! Ansonsten hilft das schönste Rechtekonzept nichts.
  20. Nutzen eines unsicheren Verschlüsselungsalgorithmus: Nunja, das fällt wohl eher in die Richtung hochsensibler Anwendungen wie Banken- oder Regierungssoftware. Wenn nachgewiesen wird, dass ein Algorithmus mit sonstwievielen Tausend Rechnern geknackt werden kann, braucht man sich wohl erst Sorgen machen, wenn man seeehr bekannt ist und es für die Angreifer auch einen Nutzen gibt. Lieschen Müllers Privatseite wird deswegen wohl nicht angegriffen. Allerdings sollte man es unbedingt vermeiden, einen eigenen Algorithmus zu nutzen, nur weil man den selbst nicht knacken kann. Moderne Kryptosysteme wurden jahrelang von Experten auf Schwachstellen durchleuchtet und gelten nur deshalb als sicher.
  21. Festes Passwort: Wer für alle User ein und dasselbe Passwort im Code fest verdrahtet, weil es ja aus Sonderzeichen und Zahlen besteht und jeder Wörterbuchattacke standhält, hat etwas falsch verstanden. Ein nicht wechselndes Passwort ist nicht viel besser als kein Passwort. Übrigens sollte man das auch beherzigen, wenn man mal Fremden Zugriff zum System gewährt hat – beispielsweise einem Programmierer seine FTP-Zugangsdaten gegeben hat. Man sollte nicht darauf vertrauen, dass er diese nach getaner Arbeit wieder vergisst…
  22. nochmal, dass sensible Programme und Dateien mit korrekten Rechten versehen werden sollten – sodass eben nicht irgendwelche Kontodaten von zig Tausend Leuten übers Web abgerufen werden können.
  23. Nutzung unbefriedigender Zufallszahlen: Zufallszahlengeneratoren im Computer sind nie wirklich zufällig, aber immerhin schaffen sie meist eine Gleichverteilung im gewählten Zahlenbereich. Sie dürfen also gern dazu genutzt werden, einen bestimmten Werbebanner zufällig zu rotieren. Für kritischere Dinge sollte man aber vorsichtig sein. Wenn dazu noch der Seed-Wert beeinflusst werden kann und somit eine Vorhersagbarkeit der nächsten Zufallszahl besteht, nutzt die schönste Zahl nix mehr.
  24. Berechtigungen pflegen: Es kann ja durchaus passieren, dass der Admin ein einziges Mal einem Programm mehr Rechte geben muss, um eine bestimmte Operation durchzuführen. Wichtig ist es dann, dass er nach getaner Arbeit diese Rechte auch wieder zurücksetzt. Wenn man sein Auto aufschließt, um damit zu fahren, muss man es nach der Fahrt ja auch wieder zuschließen…
  25. clientseitige Input-Validierung: Ein Problem vieler AJAX-Anwendungen ist es, dass die Validierung von Eingaben per JavaScript auf dem Clientsystem gemacht wird. Das ist sehr problematisch, denn wenn ein Angreifer den HTML-Code der Seite speichert, verändert (beispielsweise den Event-Handler onsubmit entfernt) und dann erneut die gewünschte Eingabe tätigt, wird sie diesmal an den Server gesendet. Validiert dieser nicht noch einmal, kann das böse ausgehen! Ich schreibe mir deshalb bei AJAX-Anwendungen immer eine Prüffunktion und rufe diese sowohl per AJAX auf als auch nochmal nach dem endgültigen Abschicken eines Formulars oder sonstigem.

So, ist ein schöner Roman geworden, aber ihr solltet die Dinge wirklich Schritt für Schritt mal durchgehen. Wen das Thema Sicherheit im Zusammenhang mit PHP noch mehr interessiert, dem empfehle ich das Buch PHP-Sicherheit: PHP/MySQL-Webanwendungen sicher programmieren (von dem ich damals leider nur die erste Auflage gekauft habe – also wer einem eifrigen Blogger mal etwas Gutes tun möchte…). Darin werden die häufigsten Security-Probleme von Webanwendungen analysiert und konkrete Empfehlungen zur Vermeidung dieser Sicherheitslecks gegeben.


Schlagwörter: , , , ,

22 Kommentare bisher »

  1. Die 25 gefährlichsten Programmierfehler – Devbloggers Blog sagt

    am 14. Januar 2009 @ 12:55

    [...] Nur als Notiz. Guckstdu hier: http://phpperformance.de/25-gefaehrlichste-programmierfehler/ [...]

  2. Stefan sagt

    am 14. Januar 2009 @ 13:26

    Bin noch nicht ganz durch (mit dem Original), daher nur ein paar erste Hinweise:

    zu 1) JEDER Input muss validiert werden. $_POST, $_GET und $_COOKIE verstehen sich von selbst, aber eigentlich auch externe Daten, die man z.B. aus einer angebundenen DB zieht. Wenn man paranoid ist, sogar die Daten aus der eigenen DB.

    CSRF ist mit 2) nicht abgefackelt, im Gegenteil. CSRF entsteht, wenn der Angreifer eine eigene Seite anlegt, auf der er z.B. mit einem versteckten iframe einen Aufruf auf eine Seite einbindet, die gewisse Aktionen durchführt (z.B. Anlegen eines neuen Administrationsusers mit bestimmten Passwort). Dann lockt er einen berechtigten Benutzer auf seine Seite, und schon führt dieser unwissentlich die Aktion durch.

    Dagegen hilft kein Encoding der eigenen Seite, vielmehr sollte man z.B. Formularen / Anfragen eindeutige, nicht ratbare Tokens mitgeben, sodass nur Aktionen ausgeführt werden, die auch über das eigene Interface angefragt wurden.

  3. Danilo sagt

    am 14. Januar 2009 @ 13:51

    Danke für die Liste, sehr hilfreich.

  4. Nils sagt

    am 14. Januar 2009 @ 14:42

    Vielen dank fuer dden artikel. Als hobby programmierer sollte man sich das schonmal zu herzen nehmem :-)

    Gruß nils

  5. SeeeD sagt

    am 14. Januar 2009 @ 16:38

    Vielen Dank für die Übersetzung :)
    Die englische Version wollte ich nicht lesen.

    Was mit hier nun hervorgestochen ist, ist die Manipulation von Zufallswerten.
    Kannst jemand eine kurze erklärung machen wie man auf sichere weiße Zufallszahlen erzeugt?
    Ich wüsste gerade keinen Fall wie man dies NICHT tut. Es sei denn man gibt den Seed über Get an das Script weiter. Aber ich geh mal davon aus, dass dies niemand tut ;)

  6. Jan sagt

    am 14. Januar 2009 @ 16:47

    Mit einer Software kann man nie echte Zufallszahlen generieren, da diese eben immer das macht, was man ihr sagt und nichts zufällig.

    Gute Zufallszahlen können nur Hardware-Zufallszahlengeneratoren erstellen.
    Wikipedia sagt dazu:
    "Echte Zufallszahlen werden mit Hilfe physikalischer Phänomene erzeugt: Münzwurf, Würfel, Roulette, Rauschen elektronischer Bauelemente, radioaktive Zerfallsprozesse oder quantenphysikalische Effekte. Diese Verfahren nennen sich physikalische Zufallszahlengeneratoren, sind jedoch zeitlich oder technisch recht aufwändig."

    Im Gegensatz dazu:
    "In der realen Anwendung genügt häufig eine Folge von Pseudozufallszahlen, das sind scheinbar zufällige Zahlen, die nach einem festen, reproduzierbaren Verfahren erzeugt werden. Sie sind also nicht zufällig, da sie sich vorhersagen lassen, haben aber ähnliche statistische Eigenschaften (gleichmäßige Häufigkeitsverteilung, geringe Korrelation) wie echte Zufallszahlenfolgen. Solche Verfahren nennt man Pseudozufallszahlengeneratoren."

  7. Ralph sagt

    am 14. Januar 2009 @ 19:47

    Das ist DER Artikel, den ich schon lange gebraucht habe. Kompakt, übersichtlich und für mich noch einige Dinge zu tun, wie ich feststelle.

  8. Andreas Auberg sagt

    am 14. Januar 2009 @ 20:32

    Hi, echt toller Beitrag, doch was mir ein wenig fehlt sind Code-Beispiele wie man das am besten umsetzt ;)

    viele grüße Andreas A.

  9. Jan sagt

    am 14. Januar 2009 @ 20:40

    @Andreas: Wobei hast Du denn Probleme? Eigentlich ist das Problem bei Sicherheitslücken eher sie zu entdecken als sie zu beheben. Und für alles möchte ich keine Beispiele liefern – z.B. wie man eine Variable initialisiert, sollte nicht so knifflig sein.

    Aber wenn Bedarf besteht an Lösungsansätzen für einzelne Punkte, würde ich diese gern liefern und mit euch Lesern diskutieren. Also, wo ist die Lösung schwierig / unklar?

  10. Sören sagt

    am 15. Januar 2009 @ 09:51

    @Stefan

    weiteren Ergänzug zu 1) und deinen Ergänzungen:

    Diverse $_SERVER-Variablen müssen auch geprüft werden. Auf jeden Fall der User-Agent und der Referer sowie natürlich die Variablen, welche dir URL und somit GET-Variablen enthalten. Ob auch die Accept-Variablen hängt vermutlich vom Server ab, da es gut sein kann, dass Anfragen, die nicht HTTP konform sind, von vorne herein abgelehnt bzw. mit Defaultwerten belegt werden.

  11. Markus sagt

    am 16. Januar 2009 @ 09:12

    Die Liste kannte ich bereits (heise sei dank) aber in der übersetzt lässt sie sich doch einiges besser lesen, Danke dafür!

  12. workerholic sagt

    am 21. Januar 2009 @ 00:48

    das Buch habe ich auch im Schrank liegen, ganz ehrlich hat es nicht so viel gebracht, ich glaube wenn man genug in Blogs und auf den Seiten unterwegs ist wo entwickelt wird, ist man mehr auf dem laufenden! vielleicht eher ne kurze zusammenfassung!

  13. Jan sagt

    am 21. Januar 2009 @ 07:54

    @workerholic: Richtig, nur welche Blogs beschäftigen sich hauptsächlich mit der Sicherheit von PHP-Anwendungen und -Umgebungen? Wäre schön, wenn Du da einige nennen könntest, dann würde mein Feedreader mal wieder etwas voller. Danke schonmal.

  14. Michael sagt

    am 28. Januar 2009 @ 20:39

    Man sollte mal die ganzen Open Source Programme (z.B. von Sourceforge und Co.) automatisiert nach solchen Problemen durchsuchen. mich würde mal interessieren, wie viele Programme Fehlerfrei durchkommen.

  15. Die WEB-Architektin bloggt... sagt

    am 23. Oktober 2009 @ 14:51

    10 Punkte für eine sichere Website…

    Jochen Weiland hat im T3N-Magazin 15 eine Checkliste für sichere Typo3-Anwendungen veröffentlicht. Davon habe ich mich zu einer allgemeinen Checkliste für sichere Websites inspirieren lassen (und dabei ist es egal, ob und welches CMS-, B…

  16. Michael sagt

    am 13. Februar 2010 @ 16:12

    Danke für die hilfreiche Liste!

    Warum allerdings Punkt 17 (fehlende Initialisierung von Variablen) ein Problem sein soll, verstehe ich nicht.

    Siehe dazu auch http://de.wikibooks.org/wiki/Websiteentwicklung:_PHP:_Variablen#Initialisierung

  17. Stefan sagt

    am 13. Februar 2010 @ 17:38

    Das stammt aus der Zeit, als register_globals noch aktiv war – und ist auch heute noch relevant, da es teilweise noch aktiv ist. Durch diese Einstellung werden sämtliche Parameter (get, post, cookie) direkt auf Variablen abgebildet.
    Nehmen wir als Beispiel, dass du eine Variable $admin hast, die true ist, falls ein Administrator eingeloggt ist. Du checkst also den login, falls du einen user finden kannst checkst du ob er ein Admin ist und setzt dann $admin = true;
    Nehmen wir jetzt den Fall, dass kein user eingeloggt ist – dann kommst du an dem $admin = true nie vorbei, deine Applikation funktioniert aber nach wie vor, da beim Testen (also, if ($admin) // give a lot of rights) der undefinierte Wert als false gewertet wird.
    Alles wunderbar.
    Jetzt komme ich böser Mensch, und rufe deine Seite mit einem zusätzlichen Get-Parameter auf: http://www.cool-site.com/Delete-All.php?admin=1
    Dank register global steht damit eine Variable $admin mit Wert 1 zur Verfügung, niemand ist eingeloggt, also wird diese 1 nie verändert (da die Variable nicht mit false initialisiert wird) – und prompt führe ich die Operation im Admin-Modus aus.

  18. 25 Programmierfehler | CodersX Webblog sagt

    am 12. August 2010 @ 14:43

    [...] nicht über die Liste berichten, als ich davon auf heise.de gelesen hab. Allerdings hat sich der phpperformance Blog doch einige Mühe gegeben, das ganze auf PHP und [...]

  19. Brix sagt

    am 12. Dezember 2010 @ 16:37

    Hab mich beim Durchlesen ein paar mal selbst ertappt :D
    gut zu Denken gegeben…
    Danke für die "Übersetzung" :)

  20. Mike sagt

    am 14. Juli 2012 @ 16:13

    Könnte man das mit dem Ajax noch einmal genauer ausführen?

  21. GhostGambler sagt

    am 14. Juli 2012 @ 17:43

    Na, wenn du nur per Javascript validierst, die Daten dann zum Server geschickt werden und dort keine Validierung mehr stattfindet, dann kann man halt falsche Daten an den Server schicken, indem man die JS-Validierung einfach ausschaltet. Denn der Server selbst validiert ja nicht.

  22. 25 Programmierfehler - codesprint GmbH sagt

    am 23. Mai 2013 @ 13:21

    [...] nicht über die Liste schreiben, als ich davon auf heise.de gelesen habe. Allerdings hat sich der phpperformance Blog doch einige Mühe gegeben, das ganze auf PHP und [...]

Komentar RSS · TrackBack URI

Hinterlasse einen Kommentar

Name: (erforderlich)

eMail: (erforderlich)

Website:

Kommentar: