Frage

"Der durchschnittliche Mann will nicht frei sein. Er will einfach nur in Sicherheit sein." - - HL Menken

Ich versuche sehr sicher zu schreiben. Im Folgenden liste ich einige der Techniken auf, die ich verwende und frage, dass sie so sicher sind, wie ich denke, dass sie es sind. Bitte zögern Sie nicht, meinen Code/meine Vorurteile in Stücke zu reißen. Jede Antwort, die selbst die trivialste Verwundbarkeit findet oder mir eine neue Idee lehrt, wird sein hoch geschätzt.

Lesen aus einem Stream:

Laut dem GNU C -Programmier -Tutorial Getline:

Die GetLine -Funktion vergrößert den Speicherblock nach Bedarf automatisch über die Realloc -Funktion, sodass es nie Platz mangelt - ein Grund, warum GetLine so sicher ist. [..] Beachten Sie, dass GetLine Ihre Eingabelinie sicher verarbeiten kann, egal wie lange sie dauert.

Ich nehme an, dass Getline sollte, unter allen Eingaben, verhindern a Pufferüberlauf vom Auftreten beim Lesen aus einem Stream.

  • Ist meine Annahme richtig? Gibt es Eingaben und/oder Zuweisungsschemata, unter denen dies zu einem Exploit führen könnte? Zum Beispiel was ist, wenn der erste Charakter aus dem Stream etwas ist bizarrer Kontrollcharakter, vielleicht 0x08 Rückraum (CTL-H).
  • Wurde eine Arbeit geleistet, um Getline als sicher zu beweisen?

Malloc kehrt null zum Versagen zurück:

Wenn Malloc auf einen Fehler stößt, gibt Malloc einen Nullzeiger zurück. Dies stellt ein Sicherheitsrisiko dar, da man noch Zeiger -Arithmetik auf einen Null (0x0) Zeiger, damit Wikipedia Empfohlen

/* Allocate space for an array with ten elements of type int. */
int *ptr = (int*)malloc(10 * sizeof (int));
if (ptr == NULL) {
    /* Memory could not be allocated, the program should handle 
       the error here as appropriate. */
} 

Sichere SSCANF:

Beim Benutzen Sscanf Ich habe mich an die Gewohnheit gemacht, die Größe der Größe der Größe der Eingangszeichenfolge zuzuweisen, die hoffentlich die Möglichkeit einer Überrannung vermeiden. Zum Beispiel:

const char *inputStr = "a01234b4567c";
const char *formatStr = "a%[0-9]b%[0-9]c":
char *str1[strlen(inputStr)];
char *str2[strlen(inputStr)];

sscanf(inputStr, formatStr, str1, str2);

Da Str1 und Str2 die Größe des EingabeSSTR und nicht mehr Zeichen als Strlen (InputStr) aus InputStr gelesen werden können, scheint es unmöglich, gegeben zu sein alle möglichen Werte Damit der InputStr einen Pufferüberlauf verursacht?

  • Hab ich recht? Gibt es seltsame Eckfälle, an die ich nicht gedacht habe?
  • Gibt es bessere Möglichkeiten, dies zu schreiben? Bibliotheken, die es bereits gelöst haben?

Allgemeine Fragen:

Während ich eine große Anzahl von Fragen gepostet habe, erwarte ich nicht, dass jemand alle beantwortet. Die Fragen sind mehr Richtlinien für die Art von Antworten, nach denen ich suche. Ich möchte wirklich die sichere C -Denkweise lernen.

  • Welche anderen sicheren C -Redewendungen gibt es da draußen?
  • Welche Eckfälle brauche ich? Immer überprüfen?
  • Wie kann ich Unit -Tests schreiben, um diese Regeln durchzusetzen?
  • Wie kann ich Einschränkungen in Testbarkeit oder nachweislich korrekter Weise durchsetzen?
  • Irgendwelche empfohlenen statischen/dynamischen Analysetechniken oder Tools für C?
  • Welche sicheren C -Praktiken folgen Sie und wie rechtfertigen Sie sie für sich und andere?

Ressourcen:

Viele der Ressourcen wurden von den Antworten ausgeliehen.

War es hilfreich?

Lösung

  1. Lesen aus einem Stream

Die Tatsache, dass getline() "Vergrößert automatisch den Speicherblock nach Bedarf", bedeutet, dass dies als Denial-of-Service-Angriff verwendet werden kann, da es trivial wäre, einen Eingang zu erzeugen, der so lang war, dass er den verfügbaren Speicher für den Prozess (oder Schlimmer noch das System!). Sobald eine außeremmehende Bedingung auftritt, können auch andere Schwachstellen ins Spiel kommen. Das Verhalten von Code in niedrigem/ohne Speicher ist selten nett und sehr schwer vorherzusagen. IMHO Es ist sicherer, alles angemessene Obergrenzen für alles zu setzen, insbesondere in sicherheitsempfindlichen Anwendungen.

Darüber hinaus (wie Sie erwarten, wenn Sie Sonderzeichen erwähnen), getline() Gibt Ihnen nur einen Puffer; Es gibt keine Garantien für den Inhalt des Puffers (da die Sicherheit vollständig anwendungsabhängig ist). Die Eingabe der Eingabe ist daher immer noch ein wesentlicher Bestandteil der Verarbeitung und Validierung von Benutzerdaten.

  1. Sscanf

Ich würde es vorziehen, eine reguläre Expressionsbibliothek zu verwenden und Regexps für Benutzerdaten sehr eng zu definieren, anstatt sie zu verwenden sscanf. Auf diese Weise können Sie zum Zeitpunkt der Eingabe eine Menge Validierung durchführen.

  1. Allgemeine Kommentare

    • Es stehen Fuzzing -Tools zur Verfügung, mit denen zufällige Eingaben (sowohl gültig und ungültig) generiert werden können, mit denen Sie Ihr Eingangsumgang testen können
    • Pufferverwaltung ist kritisch: Pufferüberläufe, Unterläufe, außerhalb des Memorys
    • Rennbedingungen können in ansonsten sicheren Code genutzt werden
    • Binärdateien können manipuliert werden, um ungültige Werte oder übergroße Werte in Header zu injizieren. Daher muss der Dateiformatcode rockig sein und nicht annehmen, dass Binärdaten gültig sind
    • Temporäre Dateien können häufig eine Quelle für Sicherheitsprobleme sein und müssen sorgfältig verwaltet werden
    • Die Codeinjektion kann verwendet werden, um System- oder Laufzeitbibliotheksanrufe durch böswillige Versionen zu ersetzen
    • Plugins bieten einen riesigen Vektor für den Angriff
    • Als allgemeines Prinzip würde ich vorschlagen, klar definierte Schnittstellen zu haben, an denen Benutzerdaten (oder Daten von außerhalb der Anwendung) ungültig und feindlich angenommen werden, bis sie verarbeitet, sanitiert und validiert werden und die einzige Möglichkeit für Benutzerdaten zur Eingabe der Anwendung

Andere Tipps

Ich denke, Ihr SSCANF -Beispiel ist falsch. Es kann immer noch überlaufen, wenn es so verwendet wird.

Versuchen Sie dies, das die maximale Anzahl von Bytes angibt, die zu lesen sind:

void main(int argc, char **argv)
{
  char buf[256];
  sscanf(argv[0], "%255s", &buf);
}

Schauen Sie sich diesen IBM Dev -Artikel über den Schutz vor Pufferüberläufen an.

In Bezug auf das Testen würde ich ein Programm schreiben, das zufällige Länge von zufälliger Länge generiert und an Ihr Programm füttert und sicherstellen kann, dass sie angemessen behandelt werden.

Ein guter Ort, um dies zu betrachten, ist David Wheelers ausgezeichnete sichere Codierungsstelle.

Sein kostenloses Online -Buch "Sichere Programmierung für Linux und Unix Howto"Ist eine hervorragende Ressource, die regelmäßig aktualisiert wird.

Vielleicht möchten Sie sich auch seinen hervorragenden statischen Analysator ansehen Fehler um einige weitere Hinweise zu bekommen. Aber denken Sie daran, kein automatisiertes Werkzeug ist ein Ersatz für ein gutes Paar erfahrener Augen oder wie David es so farbenfroh ausdrückt.

Jedes statische Analysetool wie Flawfinder ist lediglich ein Werkzeug. Kein Werkzeug kann das menschliche Denken ersetzen! Zusamenfassend, "Ein Dummkopf mit einem Werkzeug ist immer noch ein Dummkopf". Es ist ein Fehler zu glauben, dass Analyse -Tools (wie Flawfinder) ein Ersatz für Sicherheitstrainings und Wissen sind

Ich persönlich habe Davids Ressourcen seit mehreren Jahren persönlich benutzt und finde sie als ausgezeichnet.

Yannick Moy entwickelte während seiner Promotion ein Hoare/Floyd -schwächstes Vorkonditionssystem für C wendete es auf die Cert Managed Strings Library an. Er fand eine Reihe von Fehlern (siehe Seite 197 seiner Memoiren). Die gute Nachricht ist, dass die Bibliothek jetzt für seine Arbeit sicherer ist.

Sie können sich auch die Website von Les Hatton ansehen hier und in seinem Buch Sicherer c was Sie von Amazon bekommen können.

Verwenden Sie nicht gets() Für die Eingabe verwenden Sie fgets(). Benutzen fgets(), Wenn Ihr Puffer automatisch zugewiesen wird (dh "auf dem Stapel"), verwenden Sie dieses Idiom:

char buf[N];
...
if (fgets(buf, sizeof buf, fp) != NULL)

Dies funktioniert weiter, wenn Sie sich entscheiden, die Größe von zu ändern buf. Ich bevorzuge diese Form:

#define N whatever
char buf[N];
if (fgets(buf, N, fp) != NULL)

Weil die erste Form verwendet buf um das zweite Argument zu bestimmen und ist klarer.


Überprüfen Sie den Rückgabewert von fclose().


Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top