Warum secure, wenn’s schon safe ist? Security von Software durch Einhaltung von Programmier-Richtlinien

Von Frank Büchner*

Anbieter zum Thema

Security hat viele Aspekte. Um sichere („secure“) Software zu erstellen, bietet sich die Einhaltung von einschlägigen Codier-Richtlinien an. Was sind solche Richtlinien, wie ist Ihre Beziehung zu Richtlinien für „safety“, und inwieweit können diese Richtlinien durch statische Analysewerkzeuge geprüft werden?

Safety und Security gehen Hand in Hand
Safety und Security gehen Hand in Hand
(Bild: gemeinfrei / Pixabay)

Die vielen Aspekte von Security beginnen bei „secure boot“ und Hardware Security Modulen (HSM), gehen weiter mit Kryptographie (neueste Algorithmen nutzen!) bis zu Passwörtern (lange Passwörter mit Sonderzeichen verwenden!). Und selbst wenn man ein ausreichend sicheres Passwort benutzt, darf man es sich nicht durch Social-Engineering-Attacken (z.B. einen überzeugenden Telefonanruf) entlocken lassen. Im Folgenden soll es um sichere („secure“) Software gehen und auch inwieweit diese sich von sicherer („safe“) Software unterscheidet.

Bild 1: Der Unterschied zwischen Safety und Security
Bild 1: Der Unterschied zwischen Safety und Security
(Bild: Hitex GmbH)

Denn Safety und Security sind eigentlich unterschiedlich: Safety ist der Schutz des Menschen vor dem System, beispielsweise sollte ein Roboter einen Menschen nicht verletzen. Security ist das Umgekehrte, nämlich der Schutz des Systems vor dem Menschen (siehe Bild 1). Beispielsweise soll ein Mensch mit böser Absicht (mit „schwarzem Hut“, üblicherweise als „Hacker“ bezeichnet) nicht in der Lage sein, ein (Computer-) System zu übernehmen und darauf beliebige Aktionen auszuführen.

Die drei Ziele von Security

  • 1. Confidentiality (Vertraulichkeit): Vertraulichkeit bedeutet, dass Daten, beispielsweise Bankkontodaten oder medizinische Befunde nicht frei im Internet einsehbar sind.
  • 2. Integrity (Integrität, Intaktheit): Integrität bedeutet, dass Daten, beispielsweise der Bankkontostand, nicht unbefugt verändert werden.
  • 3. Availability (Verfügbarkeit): Verfügbarkeit bedeutet, dass das System jederzeit (von geplanten Wartungen abgesehen) berechtigt benutzt werden kann, beispielsweise, dass in einem Banksystem jederzeit Kontostände abgefragt und Überweisungen vorgenommen werden können.

Die drei Schutzziele werden nach den Anfangsbuchstaben der englischen Begriffe auch als CIA-Schutzziele bezeichnet.

Nicht „safe“ und trotzdem „secure“?

Es ist einleuchtend, dass ein System, das nicht „secure“ ist, auch nicht „safe“ sein kann, denn wenn ein Hacker ein Kraftfahrzeug übernehmen kann, dann kann er dieses auch in den Graben lenken, wie es beispielsweise mit einem Jeep Cherokee geschehen ist.

Aber wenn ein System nicht „safe“ ist, kann es dann u.U. trotzdem „secure“ sein? Man könnte sich ein System vorstellen, dessen Software keine angreifbaren Schnittstellen hat, sondern beispielsweise nur einen manuellen Bedienknopf, weswegen die interne Software nicht manipulierbar ist. Aber meines Erachtens ist auch ein solches System nicht „secure“, denn eines der Ziele von Security kann verfehlt werden, nämlich das Ziel Verfügbarkeit. Wenn das System nicht „safe“ ist, kann die Software in ihm abstürzen (z.B. bei zu schnell aufeinanderfolgender Betätigung des Bedienknopfs) und daraufhin würde das System seinen Dienst einstellen.

Richtlinien für Security

Es gibt Standards und Richtlinien, deren Vorgaben darauf abzielen, durch ihre Einhaltung Software „secure“ zu machen.

ISO/IEC TS 17961:2013

Dieser Standard [17961] trägt im Untertitel die Bezeichnung „C secure“ und enthält 46 Vorgaben. Im Jahr 2016 wurde im Technical Corrigendum 1 die Regel 5.21 genauer formuliert; dies ist in der Ausgabe von 2018 (Edition 2) des Standards enthalten.

SEI CERT C Coding Standard

Dieser Standard [CERT] schreibt über sich selbst: „Dieser Standard stellt Vorgaben für das sichere („secure“) Programmieren in der Sprache C zur Verfügung.“ Allerdings ist der vom Computer Emergency Response Team (CERT) des Software Engineering Institutes (SEI) der Carnegie Mellon Universität in Pittsburgh, PA, USA herausgegebene „Standard“ kein eigentlicher Standard, denn er ist nicht von Standardisierungsgremien wie ISO oder IEC herausgegeben (nach z.T. langjährigen internationalen Absprachen und Abstimmungen). Zudem entwickelt sich dieser Standard im Internet weiter und ist sofern ein bewegliches Ziel („moving target“), beispielsweise für Hersteller von statischen Analysetools, die ihn unterstützen wollen. Im Jahr 2016 wurde der aktuelle Stand in einem Dokument fixiert; dieses Dokument enthält 99 Vorgaben.

Vergleich mit Richtlinie für Safety

Die Vorgaben der Motor Industry Software Reliability Association (MISRA), ursprünglich für den Automobilbereich gedacht, werden wohl am häufigsten mit Safety assoziiert. Die MISRA-Richtlinien werden auch von Standards zur Entwicklung sicherheitskritischer Software, beispielsweise in Teil 6, Abschnitt 5.4.3 von ISO 26262:2018 [26262], erwähnt. Die aktuelle Richtlinie MISRA C:2012 [MISRA] enthält ursprünglich 159 Vorgaben (16 Direktiven + 143 Regeln). Im Jahr 2016 kamen durch Ergänzung (Amendment) 1 [MISRAAM1] weitere 14 Vorgaben hinzu (1 Direktive + 13 Regeln). Diese 14 zusätzlichen Vorgaben adressieren speziell Security. Im Februar 2020 kamen durch Ergänzung (Amendment) 2 [MISRAAM2] zwei weitere Regeln hinzu. Insgesamt gibt es somit 175 Vorgaben.

Im Januar 2018 wurden zwei Zusätze (Addenda) veröffentlicht (Addendum 2 und 3). In Addendum 2 [MISRAAD2] wird die Abdeckung von MISRA C:2012 (mit Amendment 1) zu ISO/IEC TS 17961:2013 („C secure“) diskutiert, mit dem Ergebnis, dass alle 46 Vorgaben aus „C secure“ durch MISRA-Vorgaben abgedeckt sind.

In Addendum 3 [MISRAAD3] wird die Abdeckung von MISRA C:2012 (mit Amendment 1) zum SEI CERT C Coding Standard (Edition von 2016) diskutiert, mit dem Ergebnis, dass von den 99 Vorgaben 80 mehr oder weniger gut abgedeckt sind (d.h. manche nur partiell); 15 Vorgaben werden als „out-of-scope“ betrachtet, weil sie C11 betreffen und MISRA C:2012 zu der Zeit nur für C90 und C99 vorhanden war; 4 Vorgaben aus CERT werden durch MISRA nicht abgedeckt. Bei den nicht unterstützten Vorgaben handelt es sich um zwei, die sich mit Zufallszahlen befassen (MSC30-C und MSC32-C); eine, die zum Tragen kommt, wenn die Größe von char gleich der Größe von int ist (FIO34-C); und eine, die darauf hinweist, dass man die Größe eines int nicht mit seiner Genauigkeit verwechseln sollte (INT35-C). Insgesamt ergibt sich eine starke Überdeckung zwischen den „Safety“-Vorgaben aus MISRA und den „Security“-Vorgaben aus „C secure“ und CERT. Die Frage, ob gewisse Vorgaben eher Safety oder eher Security zuzuordnen sind, wird anhand der untenstehenden Beispiele beantwortet.

Jetzt Newsletter abonnieren

Verpassen Sie nicht unsere besten Inhalte

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung.

Aufklappen für Details zu Ihrer Einwilligung

Statische Analyse-Werkzeuge

Ganz offensichtlich sparen statische Analysewerkzeuge großen manuellen Aufwand ein, wenn es darum geht, Quellcode auf die Einhaltung von Codier-Richtlinien zu überprüfen. Das gilt für Vorgaben sowohl zur Safety als auch für Vorgaben zur Security. Allerdings muss man bedenken, dass Vorgaben aus Codier-Richtlinien entweder entscheidbar und unentscheidbar sind. In MISRA und „C Secure“ ist dies für jede Regel angegeben. Entscheidbare Vorgaben sind solche, bei denen man aufgrund des Quellcodes und Zusatzinformationen (beispielsweise der Anzahl der Bits in einem Integer) sicher sagen kann, ob sie eingehalten werden oder nicht. Beispielsweise wird durch die Regel 7.1 aus MISRA C:2012 der Gebrauch von oktalen Konstanten untersagt; es kann eindeutig entschieden werden, ob eine solche Konstante vorkommt oder nicht. Ob eine Vorgabe entscheidbar oder unentscheidbar ist, gilt ganz allgemein und hängt nicht etwa vom verwendeten Analysewerkzeug ab. Dass es unentscheidbare Probleme gibt, kann man sich am Halteproblem (halting theorem) klarmachen. Bei unentscheidbaren Vorgaben kann es false positives und false negatives geben. Bei einem false positive wird die Verletzung einer Vorgabe gemeldet, obwohl in Wahrheit gar keine Verletzung vorliegt.

Bei einem false negative wird keine Verletzung einer Vorgabe gemeldet, obwohl eine solche vorliegt. Bei einem statischen Analysetool ist die Möglichkeit von false negatives problematisch, denn selbst wenn das Werkzeug keinen Fehler meldet, kann man nicht sicher sein, ob nicht doch einer vorliegt. Eigentlich muss man dies durch geeignete andere Maßnahmen, beispielsweise einem Review, überprüfen. Dies reduziert den Nutzen eines statischen Analysetools gewaltig. Allerdings kann ein Werkzeug in Bezug auf eine bestimmte (unentscheidbare) Vorgabe „sound“ sein.

Das bedeutet, dass für diese Vorgabe false negatives ausgeschlossen sind (falls das Werkzeug fehlerfrei arbeitet), allerdings werden – wegen der Unentscheidbarkeit – false positives vorkommen. Auch diese müssen überprüft werden (beispielsweise durch einen Review), aber der Aufwand ist normalerweise wesentlich geringer, weil gezielter vorgegangen werden kann als bei der Suche nach möglichen false negatives. Die Anzahl der false positives bildet ein Kriterium für die Qualität eines Analysewerkzeugs; man könnte zwei unterschiedliche Werkzeuge danach bewerten, welches (bei Soundness für eine unentscheidbare Vorgabe) für diese Vorgabe weniger false positives beim gleichen Quellcode hat. Leider fallen die am meisten interessierenden Safety- bzw. Security-Probleme, beispielsweise Division durch null, in die Kategorie unentscheidbar.

Nummerierungen von Schwachstellen

Mit der Common Vulnerabilities and Exposures (CVE) und der Common Weakness Enumeration (CWE) gibt es zwei Nummerierungssysteme für Sicherheitslücken und Schwachstellen in Software (und auch in Hardware) [CWE, CVE]. Das Ziel ist eine „gemeinsame Sprache“ zur Bezeichnung der Schwachstellen, was durch die Nummerierung erreicht wird. Beide Nummerierungen sind im Internet einsehbar, werden durch die Community gepflegt und durch die Mitre-Organisation unterstützt.

Beispiele für Security-Vorgaben

Im Folgenden betrachten wir einige Verletzungen von Vorgaben und bewerten, inwieweit sie durch Werkzeuge gefunden werden können und ob es sich mehr um ein Safety- oder um ein Security-Problem handelt.

NULL-Pointer-Verwendung

Bild 2: Mögliche NULL-Pointer-Verwendung
Bild 2: Mögliche NULL-Pointer-Verwendung
(Bild: Hitex GmbH)

Im Bild links (Bild 2) wird eine mögliche NULL-Pointer-Verwendung gemeldet. In Zeile 13 wird Speicher allokiert und der Zeiger darauf der Variablen „str“ zugewiesen. Dieser Zeiger wird (ungeprüft) in der Zeile 14 in der Funktion strcpy() verwendet. Das verletzt die Direktive 4.7 von MISRA C:2012. Darauf wird durch eine Meldung des Werkzeugs ECLAIR [ECLAIR] hingewiesen. Denn kann nicht genügend Speicher allokiert werden, wird der Zeiger in der Variablen „str“ NULL sein. Dies führt zu undefiniertem Verhalten in Zeile 14, höchstwahrscheinlich wird das Programm abstürzen. Handelt es sich nun eher um Safety- oder um ein Security-Problem? Sowohl CERT als auch „C secure“ nennen dieses Problem, CERT unter der Bezeichnung EXP34-C (Do not dereference null pointers); “C secure” in Abschnitt 5.14 [nullref]. Die Common Weakness Enumeration führt es unter CWE-476: NULL Pointer Dereference. Aber meiner Meinung nach handelt es sich in erster Linie um einen Programmierfehler und hat mit Safety oder Security zunächst nichts zu tun. Falls ein potentieller NULL-Pointer vor Verwendung nicht geprüft wird, ist dies einfach fragwürdige Programmierung.

Pufferüberlauf

Bild 3: Ist Pufferüberlauf ein Security-Problem?
Bild 3: Ist Pufferüberlauf ein Security-Problem?
(Bild: Hitex GmbH)

Im Bild links (Bild 3) wird durch das Werkzeug ECLAIR ein Pufferüberlauf gemeldet. Dies liegt daran, dass das Array s[] für den Namen nur ein Zeichen (das X) vorsieht. Wird nun ein Name mit mehr als einem Zeichen an der Stelle des X in das Array hineinkopiert, wird Speicher überschrieben. Pufferüberlauf ist ein Klassiker unter den Security-Problemen, denn durch das Ausnutzen eines Pufferüberlaufs könnte beispielsweise die Rücksprungadresse einer Funktion manipuliert werden, wodurch der Rücksprung zu beliebigem Schadcode führen könnte. Sowohl CERT als auch „C secure“ nennen dieses Problem, CERT unter der Bezeichnung ARR30-C (Do not form out-of-bounds … array subscripts) und STR31-C (Guarantee… sufficient space …); “C secure” in Abschnitt 5.22 (Forming or using out-of-bounds … array subscripts [invptr]). Die Common Weakness Enumeration führt es unter CWE-120: Classic buffer overflow. Aber meiner Meinung nach handelt es sich in erster Linie um einen Programmierfehler und hat mit Safety oder Security direkt nichts zu tun. Falls Speicher durch einen Pufferüberlauf überschrieben wird, ist dies ein Fehler, der undefiniertes Verhalten (undefinied behavior) bewirkt, und das muss vermieden werden.

Verschmutzte Daten

Bild 4: Verschmutze Daten und wie sie gesäubert werden können
Bild 4: Verschmutze Daten und wie sie gesäubert werden können
(Bild: Hitex GmbH)

Im oberen Teil des Bildes links (Bild 4) wird durch die Funktion fgets() der Inhalt einer Datei in den Puffer input_buf[] eingelesen. Dieser Inhalt kommt für das Programm „von außen“ und das Programm hat keine Information, was in input_buf[] enthalten ist; somit sind in input_buf[] sogenannte „tainted“ (verschmutzte) Daten. Im obigen Beispiel werden diese verschmutzen Daten als Parameter an die Funktion system() übergeben, die diese Daten als Kommando ausführt. Dadurch kann ein beliebiges Kommando ausgeführt werden, was ein hohes Security-Risiko darstellt.

Dies führt zur Meldung der Verletzung der Direktive D14.4 aus Amendment 1 von MISRA C:2012. (Amendment 1 enthält die zusätzlichen Security-Vorgaben, s.o.). Dies sollte man als wirkliches Security-Problem ansehen; allein von der Tatsache, dass ein beliebiges Kommando an die Funktion system() zur Ausführung übergeben wird, tritt noch kein undefiniertes Verhalten ein (natürlich kann die Ausführung des Kommandos dazu führen). Um zu verhindern, dass in der obigen Situation eine Verletzung der Direktive 4.14 durch das Werkzeug ECLAIR gemeldet wird, kann man dem Werkzeug mitteilen, dass es eine Funktion gibt (in unserem Fall die Funktion check_string_for_system()), der die verschmutzen Daten als Parameter übergeben werden und die durch ihren Return-Wert (true oder false) mitteilt, ob die Daten verschmutzt sind oder nicht.

Diese Prüfung auf Verschmutzung kann beispielsweise durch eine Liste mit erlaubten Kommandos erfolgen („whitelisting“), d.h. die Funktion check_string_for_system() kennt alle erlaubten Kommandos und falls das zur Ausführung Anstehende erlaubt ist, wird true zurückgegeben, ansonsten false. Im unteren Teil von Bild 4 ist dargestellt, dass abhängig vom Return-Wert von check_string_for_system() die Funktion system() aufgerufen wird oder nicht. Man kann sich auch vorstellen, dass die Prüffunktion die verschmutzten Daten auch säubert („sanitize“), beispielsweise unzulässige Parameter des Kommandos entfernt.

Festes Passwort

Es gibt auch Security-Probleme, die statische Analysewerkzeuge schwerlich finden können. Dazu gehört beispielsweise ein fest programmiertes Passwort („hard-coded password“). Dieses Security-Problem wird weder in CERT noch in „C secure“ erwähnt; es läuft unter CWE-259: Use of Hard-coded password.

Bild 5: Beispiel für ein Security-Problem, das Werkzeuge kaum aufdecken können
Bild 5: Beispiel für ein Security-Problem, das Werkzeuge kaum aufdecken können
(Bild: Hitex GmbH)

Das Bild links (Bild 5) zeigt ein Security-Problem, das statische Analysewerkzeuge kaum aufdecken können, nämlich die Benutzung eines fest codierten Passworts. Im obigen Beispiel wird das initiale Passwort fest auf den Wert „123456“ gesetzt. Es war wohl beabsichtigt, ein Passwort-Management zu implementieren, aber aus irgendwelchen Gründen ist dies unterblieben. Das führt dazu, dass alle Systeme (bei Webcams ist das beispielsweise schon vorgekommen) mit demselben einfachen, bekannten Passwort ausgeliefert werden. Das ist nun definitiv ein Security-Problem (und kein Programmierfehler). Ein statisches Analysewerkzeug kann nicht wissen, dass eigentlich ein Passwort-Management implementiert werden sollte und kann deswegen nichts melden (vielleicht moniert es die leere Funktion set_pw(), aber diese und ihr Aufruf muss es ja nicht geben, sie könnten komplett fehlen). Die Abhilfe ist eine Anforderung (requirement) nach dem Passwort-Management und Tests, die sie prüfen.

Fazit

Statische Analysewerkzeuge sind nützlich, wenn es darum geht, Software auf mögliche Security-Probleme zu prüfen. Dabei ist es nicht ausschlaggebend, ob Codier-Richtlinien für Safety (z.B. MISRA) oder Codier-Richtlinien speziell für Security verwendet werden, denn es gibt große Übereinstimmungen. Aber nicht alle Vorgaben aus Codier-Richtlinien zur Vermeidung von Safety- oder Security-Problemen sind entscheidbar, und man muss mit false positives und false negatives rechnen, die manuellen Aufwand erfordern. Darüber hinaus gibt es Security-Probleme (z.B. fest-codierte Passwörter), denen mit statischen Analysewerkzeugen nicht beizukommen ist.

Frank Büchner hat ein Diplom in Informatik und widmet sich seit vielen Jahren dem Thema Testen und Software-Qualität. Momentan arbeitet er als „Principal Engineer Software Quality“ bei der Fa. Hitex GmbH in Karlsruhe.
Frank Büchner hat ein Diplom in Informatik und widmet sich seit vielen Jahren dem Thema Testen und Software-Qualität. Momentan arbeitet er als „Principal Engineer Software Quality“ bei der Fa. Hitex GmbH in Karlsruhe.
(Bild: Hitex GmbH)

Literaturverzeichnis

  • [17961] ISO/IEC TS 17961:2013, Information technology — Programming languages, their environments & system software interfaces — C Secure Coding Rules. Geneva, Switzerland: ISO/IEC, Nov. 2013.
  • [CERT] SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems, 2016th ed. Software Engineering, Carnegie Mellon University, 2016.
  • [26262] ISO 26262, International Standard, Road vehicles – Functional Safety, Second edition, 2018
  • [MISRA] MISRA C:2012, Guidelines for the use of the C language in critical systems, Horiba Mira Limited, UK, Edition 3, März 2013.
  • [MISRAAM1] MISRA C:2012 Amendment 1 — Additional security guidelines for MISRA C:2012. Horiba Mira Limited, UK, Apr. 2016.
  • [MISRAAM2] MISRA C:2012 Amendment 2 — Updates for ISO/IEC 9899:2011 Core functionality. Horiba Mira Limited, UK, Feb. 2020.
  • [MISRAAD2] MISRA C:2012 Addendum 2 — Coverage of MISRA C:2012 (including Amendment 1) against ISO/IEC TS 17961:2013 “C Secure”, 2nd ed. Horiba Mira Limited, UK, Jan. 2018.
  • [MISRAAD3] MISRA C:2012 Addendum 3 — Coverage of MISRA C:2012 (including Amendment 1) against CERT C 2016 Edition. Horiba Mira Limited, UK, Jan. 2018.
  • [ECLAIR] Statisches Analysewerkzeug von BUGSENG, mehr Info: http://www.hitex.de/eclair

* Frank Büchner hat ein Diplom in Informatik von der Technischen Hochschule Karlsruhe, heute KIT. Seit vielen Jahren widmet er sich dem Thema Testen und Software-Qualität. Momentan arbeitet er als „Principal Engineer Software Quality“ bei der Fa. Hitex GmbH in Karlsruhe.

(ID:48556547)