====== Sichere Programmierung ======
FIXME (Entwurf)
===== Anhaltspunkte: =====
FIXME
===== Pufferüberlauf: =====
Ursache eines Pufferüberlaufs ist das ungeprüfte Kopieren von vom Benutzer gelieferten Daten in Puffer fester Größe. Um einen Pufferüberlauf zu verhindern, müssen also "nur" alle von außen kommenden Daten vor dem Schreiben auf ihre Größe geprüft werden. Und zwar wirklich alle Daten, unabhängig davon, wie sie an das Programm übergeben wurden. Nur die innerhalb des eigenen Programms geprüften Daten sind sicher.
==== strcpy(), strncpy(), strlcpy() ====
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
char *strlcpy(char *dest, const char *src, size_t n);
In C sollte man z.B. beim Kopieren eines Strings statt der Funktion **strcpy()** die Funktion **strncpy()** verwenden. Während strcpy() alle übergebenen Daten an die Zieladresse kopiert und dabei bei einem zu kleinen Puffer einen Überlauf verursacht, kann bei strncpy() die Größe der zu kopierenden Daten angegeben werden.
:!: Bei Verwendung von strncpy() sollte man unbedingt dafür sorgen, daß der resultierende String explizit mit **%%'\0'%%**-Zeichen terminiert wird!
Weitere Alternative ist die **strlcpy()**, die garantiert einen NULL-terminierten String als Ergebnis liefert (es werden dabei maximal "size - 1" Zeichen kopiert). Diese Funktion ist jedoch nicht der Bestandteil der GNU C Library. (Sie ist in den FreeBSD, OpenBSD und SUN Solaris Libraries enthalten.)
==== sprintf(), snprintf() ====
sprintf(char *dest, const char *fmt, ... );
snprintf(char *dest, size_t n, const char *fmt, ... );
Aus dem selben Grund sollte man an potentiell anfälligen Stellen statt **sprintf()** die sicherere **snprintf()** verwenden, die beim Schreiben die maximal erlaubte Größe des Buffers berücksichtigt. Das heißt, es werden in den Buffer **dest** maximal **n** Zeichen (samt '\0') geschrieben.
Die **snprintf()** gibt es aber nicht überall, z.B. Sourcen, die den MS Compilern (z.B. VisualStudio) beiliegen. Dort gibt es **_snprintf()**, was aber nicht so wie snprintf() funktioniert. Man könnte mit folgenden Zeilen den Code mehr oder weniger übergreifen machen:
#ifdef _WIN32 // Oder eine andere treffende Kennung
#define snprintf _snprintf
#endif
:!: Im Unterschied zu snprintf() gibt _snprintf() %%-1%% zurück (wenn der übergebene Speicher nicht ausreicht), während snprintf() die Länge des Strings (also ohne '\0') zurück gibt, der geschrieben worden wäre, wenn genug Platz vorhanden gewesen wäre.
==== strcat(), strncat(), strlcat() ====
strcat(char *dest, const char *src);
strncat(char *dest, const char *src, size_t n);
strlcat(char *dest, const char *src, size_t n);
Eine weitere gefährdete Funktion ist die **strcat()** - Anhängen eines Strings am Ende eines anderen Strings. Das etwas sicherere Gegenstück dafür heißt **strncat()**. Die strncat() bekommt im dritten Parameter die Anzahl von Zeichen, die aus dem Quell-String in den Ziel-String kopiert werden dürfen. Dazu (also, zusätzlich zu der o.g. Anzahl!) kommt das terminierende **%%'\0'%%**-Zeichen.
:!: Der dritte Parameter **n** der **strncat()** darf also nicht mit der maximal erlaubten Größe des Ziel-Strings (**dest**) verwechselt werden! Die %%"n"%% Zeichen werden zu den sich in %%"dest"%% bereits befindenden dazu geschrieben und dann noch das %%'\0'%%-Zeichen!
Der sichere Aufruf würde z.B. in etwa wie folgt aussehen:
strncat(dest, src, sizeof(dest) - strlen(dest) - 1);
Weitere Infos zu strncat() sind in [[http://www.kernel.org/doc/man-pages/online/pages/man3/strcat.3.html|Linux Programmer's Manual]] zu finden.
Funktion, die "wie erwartet" funktioniert, heißt **strlcat()** - sie bekommt im dritten Parameter die Größe des Ziel-Speichers, damit dieser nicht überschrieben wird und terminiert den Ziel-String selbstständig. Die Funktion ist aber kein Bestandteil der Standard C-Library.
==== sonst ====
Für Linux gibt es auch die gcc-Erweiterung **StackShield**. Dabei wird bei jedem Funktionsaufruf die Rücksprungadresse am Anfang des Datensegments gesichert und vor dem Rücksprung mit der auf dem Stack gespeicherten Version verglichen. Stimmen die Adressen nicht überein, wird das Programm beendet oder die ursprüngliche Adresse restauriert. Der dazu notwendige Code wird am Anfang und Ende jedes Funktionsaufrufs eingefügt.
Siehe auch: [[EDV:OS:UNIX:SHELL:Restricted Shell]]
----
Stand: 15.11.2012 - **in Arbeit**\\
--- //[[feedback.jk-wiki@kreick.de|: Jürgen Kreick]]//
EOF