Sehr geehrte Damen und Herren, der o.g. Artikel lässt einen Aspekt der Sicherung von Programmen gegen Pufferüberlauf-Attacken ausser acht, der leider von vielen Programmierern allgemein übersehen wird. Typischer Weise treten Puffer-Überschreitungen bei String-Operationen auf, wie dem Einlesen von Zeilen aus Dateien oder dem Entgegennehmen von Strings aus der Standardeingabe. Das Problem besteht darin dass sich der Programmierer zwischen folgenden drei Optionen entscheiden muss: (1) Geringer Implementationsauswand und Gottvertrauen "Ich weiss, dass es keine Sinn hat, Werte anzugeben, die länger als Zeichen sind, also gebe ich + Bytes für den Puffer. ist meistens 1 oder eine andere kleine Zahl" "Ich glaube, dass dieser Funktionsaufruf nur Parameter erhält, die bereits auf Gültikgkeit überprüft sind." (2) Grosser Resourcebedarf und verbesserte Zuverlässigkeit "Ich gebe 16kB Puffer fuer den Text und gehe davon aus, dass kein Angreifer soviele Daten sendet weil er bereits bei 257 Zeichen aufgibt. Er wird annehmen, dass mein Programm sicher gegen Überlauf-Attacken ist. Darum muss ich mir kaum Sorgen machen" (3) Hoher Entwicklungsaufwand und geringe Performance "Jede Eingabe wird auf ihre Länge und anderes Gefährdungspotential überprüft. Meisten reicht zwar eine Längenüberprüfung, aber man weiss ja nie. Laufzeitprobleme ordne ich der Sicherheit unter." Es gibt allerdings noch eine Bonusoption, die meistens ungenannt bleibt und mit denkbar geringem Aufwand verbunden ist. Man verwendet eine objektorientiere Loesung. Diese Lösung bekommt man für NULL, denn sie ist bei jedem C++ - Kompiler als natürlicher Bestandteil enthalten. Sie heisst: STL - Standard Template Library. Die STL befreit den Entwickler gleichzeitig von einer Reihe von Problemen. (a) Resourceproblem Die STL ist resourceschonend programmiert. String-Kopier-Operationen entfallen meist. String werden nur bei Bedarf kopiert, ansonsten werden Referenzen verwendet. Es wird eine optimale Speicherallokation vorgenommen. (b) Performanceprobleme Durch das minimieren der String-Kopier-Operationen wird nicht nur Speicherplatz gespart, sondern auch CPU-Zeit, denn "keine Speicherallokation" braucht auch keine CPU-Zeit. Das Zeitverhalten der einzelnen Operationen in der STL ist genau dokumentiert. "Keine Kopieroperation" benötigt ebenfalls keine CPU-Zeit. (c) Überlaufproblem Erforderliche Speicherallokationen werden immer in ausreichender Grösse vorgenommen. Bei einer kopierenden String-Zuweisung wird immer genügend Speicher alloziert. Unter der Annahme, dass "sText" ein STL-string ist, stellt das Lesen aus aus einer Textquelle mittls cin >> sText; niemals eine Überlaufgefährdung dar, weil diese Funktion sicherstellt, dass "sText" genügend Speicher erhält _bevor_ der Eingabewert zugewiesen wird. ------- Mit anderen Worten, das Pufferüberlaufproblem ist bereits seit Jahren gelöst, es ist also langsam an der Zeit, dass sich die Programmierer der fertigen Lösungen bedienen. Nach viele Worten hier nun ein passender Quellcode: --------------------------------------- #include #include using namespace std; int foo(const string& sIn) { string s = sIn; return s.find("@"); } // int foo(const string& sIn) main() { cout << "Ihr Einsatz: "; string s; cin >> s; return foo(s); } // main() --------------------------------------- Der Witz an der Geschichte ist, dass man so nicht nur ein überlaufhartes Programm erhält, der Code ist in aller Regel auch noch einfacher. Bei der Zuweisung "s=sIn" wird nicht nur nichts kopiert (Effizienz), sondern das Programm verhält sich so, als wäre kopiert worden (Transparenz). Um der Sache die Würze zu geben und zu zeigen, dass sich Auseinandersetzung mit der STL lohnt, hier der Beweis, dass dieses Codebasis wirklich dem täglichen Einsatz durch bequeme Programmierer gewachsen ist: --------------------------------------- #include using namespace std; int foo(const string& sIn) { string s = sIn; return s.find("@"); } // int foo(const string& sIn) main() { return foo("Hallo@Welt"); } // main() --------------------------------------- Will sagen, man kann an die Funktion "foo" nach wie vor normale konstante Zeichenketten übergeben, als wäre nichts gewesen. Diese Variante ist allerdings nicht mehr ganz so performant. Der Funktionsaufruf "foo(.:" ist gezwungen ein temporäres Objekt zu erzeugen, damit "sIn" innerhalb der Funktion "foo(.." alle erwarteten Eigenschaften besitzt. Mittels objektorientierter Programmierung, sogar wie hier, mit den einfachsten Mitteln von C++, lassen sich fast ohne zusätzlichen Aufwand eine Reihe Sicherheitsprobleme lösen. Dies betrifft nicht nur die Überlaufproblematik. Portabel ist dieser Weg auch noch und zwar nicht nur von einem Unix-Derivat zum einem anderen. Die Portabilität ersteckt sich genau so weit, wie die Zielsysteme C++ unterstützen. Mit freundlichen Grüssen, Manfred Morgner