Elektroniknet Logo

Safe SW durch Codierrichtlinien-Prüfung

Mit Werkzeugunterstützung zur Security

Um sichere Software zu erstellen, bietet sich das strikte Befolgen von Codierrichtlinien an, die dieses Ziel verfolgen. Doch was sind gängige Security-Richtlinien und inwieweit lassen sie sich mittels statischer Analysewerkzeuge prüfen?
© sdecoret | Shutterstock)

Um sichere Software zu erstellen, bietet sich das strikte Befolgen von Codierrichtlinien an, die dieses Ziel verfolgen. Doch was sind gängige Security-Richtlinien, wie ist deren Beziehung zu Richtlinien für Safety, und inwieweit lassen sie sich mittels statischer Analysewerkzeuge prüfen?

Die vielen Aspekte von Security beginnen bei Secure Boot und Hardware-Security-Modulen (HSM), gehen weiter mit Kryptographie (neueste Algorithmen nutzen) und enden nicht bei Passwörtern (lange Passwörter mit Sonderzeichen verwenden). Denn selbst wenn man ein ausreichend sicheres Passwort benutzt, darf man es sich nicht durch Social-Engineering-Attacken wie etwa einen überzeugenden Telefonanruf entlocken lassen.

Anbieter zum Thema

zu Matchmaker+
Der Unterschied zwischen Safety und Security
Bild 1: Der Unterschied zwischen Safety und Security.
© Hitex

Nachfolgend soll es um Security und Safety gegen und darum, inwieweit sie sich unterscheiden beziehungsweise ergänzen: Safety ist der Schutz des Menschen vor dem System – ein Roboter sollte einen Menschen nicht verletzen. Security ist die umgekehrte Blickrichtung, nämlich der Schutz des Systems vor dem Menschen (Bild 1). So soll ein Mensch mit bösen Absichten nicht in der Lage sein, ein (Computer-)System zu übernehmen und darauf beliebige Aktionen auszuführen.

Die drei Ziele von Security

Mit Security werden üblicherweise drei Schutzziele verbunden:
➔ Confidentiality (Vertraulichkeit)
➔ Integrity (Integrität, Intaktheit)
➔ Availability (Verfügbarkeit)

Sie werden nach den Anfangsbuchstaben der englischen Begriffe auch als CIA-Schutzziele bezeichnet. Vertraulichkeit bedeutet, dass Daten, etwa Bankkontodaten oder medizinische Befunde, nicht frei im Internet einsehbar sind. Integrität bedeutet, dass Daten, beispielsweise der Bankkontostand, nicht unbefugt verändert werden. Verfügbarkeit bedeutet, dass das System jederzeit – von geplanten Wartungen abgesehen – berechtigt benutzt werden kann, etwa dass in einem Banksystem jederzeit Kontostände abgefragt und Überweisungen vorgenommen werden können.

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 es auch in den Graben lenken.

Aber wenn ein System nicht safe ist, kann es dann unter Umständen 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. Die Argumentation greift aber zu kurz, denn eines der Ziele von Security kann verfehlt werden, nämlich das Ziel Verfügbarkeit. Wenn das System nicht safe ist, kann seine Software abstürzen (etwa 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 [3] 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 stellt Vorgaben für das sichere Programmieren – im Sinne von secure – in der Sprache C zur Verfügung. Allerdings ist der vom Computer Emergency Response Team (CERT) [4] des Software Engineering Institute (SEI) der Carnegie Mellon University in Pittsburgh, Pennsylvania, herausgegebene »Standard« kein eigentlicher Standard, denn er ist nicht von Standardisierungsgremien wie ISO oder IEC herausgegeben. Zudem entwickelt sich dieser Standard im Internet weiter und ist sofern ein bewegliches Ziel (Moving Target), etwa für Hersteller statischer 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 Richtlinien 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. So werden die MISRA-Richtlinien auch von Standards zur Entwicklung sicherheitskritischer Software erwähnt, etwa in Teil 6, Abschnitt 5.4.3 von ISO 26262:2018 [5]. Die aktuelle Richtlinie MISRA C:2012 [6] enthält ursprünglich 159 Vorgaben (16 Direktiven und 143 Regeln). Im Jahr 2016 kamen durch Ergänzung (Amendment) 1 [7] weitere 14 Vorgaben hinzu (1 Direktive + 13 Regeln). Diese 14 zusätzlichen Vorgaben adressieren speziell Security.

Im Januar 2018 wurden zwei Zusätze (Addenda) veröffentlicht (Addendum 2 und 3). In Addendum 2 [8] 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 [9] 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; vier Vorgaben aus CERT werden durch MISRA nicht abgedeckt.

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.

Statische Analysewerkzeuge

Ganz offensichtlich sparen statische Analysewerkzeuge großen manuellen Aufwand ein, wenn es darum geht, Quellcode auf die Einhaltung von Codierrichtlinien zu überprüfen. Das gilt für Vorgaben sowohl zur Safety als auch zur Security. Allerdings muss man bedenken, dass Vorgaben aus Codierrichtlinien entweder entscheidbar oder unentscheidbar sind. In MISRA und C Secure ist dies für jede Regel angegeben. Entscheidbare Vorgaben sind solche, bei denen man aufgrund des Quellcodes sicher sagen kann, ob sie eingehalten werden oder nicht. Ob eine Vorgabe entscheidbar oder unentscheidbar ist, gilt ganz allgemein und hängt nicht 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 überprüfen, etwa einen Review. 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, allerdings werden – wegen der Unentscheidbarkeit – False Positives vorkommen. Auch diese müssen überprüft werden, etwa 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) 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 Common Vulnerabilities and Exposures (CVE) und Common Weakness Enumeration (CWE) gibt es zwei Nummerierungssysteme für Sicherheitslücken oder Schwachstellen in Software (und auch in Hardware) [1, 2]. Das Ziel ist eine gemeinsame Sprache zur Bezeichnung der Schwachstellen, was durch die Nummerierung erreicht wird. Beide Nummerierungen sind im Internet einsehbar, werden von der Community gepflegt und von der Mitre-Organisation unterstützt.

Beispiele für Security- Vorgaben

Im Folgenden werden einige Verletzungen von Vorgaben betrachtet, und es wird bewertet, inwieweit sie mittels Werkzeugen gefunden werden können und ob es sich mehr um ein Safety- oder um ein Security-Problem handelt.

 Ist Pufferüberlauf ein Security-Problem?
Bild 2: Ist Pufferüberlauf ein Security-Problem?
© Hitex

Pufferüberlauf

Im Bild 2 meldet das Werkzeug ECLAIR einen Pufferüberlauf [10]. 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.

Man kann aber auch der Meinung sein, es handelt 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

Im oberen Teil von Bild 3 liest die Funktion fgets() den Inhalt einer Datei in den Puffer input_buf[] ein. 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.

Verschmutzte Daten und wie sie gesäubert werden können
Bild 3: Verschmutzte Daten und wie sie gesäubert werden können.
© Hitex

Im obigen Beispiel werden die verschmutzten 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 dazu, dass die Verletzung der Direktive D14.4 aus Amendment 1 von MISRA C:2012 gemeldet wird (Amendment 1 enthält die zusätzlichen Security-Vorgaben, s. o.). Diesen Fall sollte man als Security-Problem betrachten; allein von der Tatsache, dass ein beliebiges Kommando an die Funktion system() zur Ausführung übergeben wird, tritt zwar noch kein undefiniertes Verhalten ein – jedoch kann die Ausführung des Kommandos dazu führen.

Um zu verhindern, dass in der obigen Situation das Werkzeug ECLAIR eine Verletzung der Direktive 4.14 meldet, kann man dem Werkzeug mitteilen, dass es eine Funktion gibt (in unserem Fall die Funktion check_string_for_system()), der die verschmutzten 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 3 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 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; die Common Weakness Enumeration führt es als CWE-259: Use of Hard-coded Password.

Beispiel für ein Security-Problem, das Werkzeuge kaum aufdecken können
Bild 4: Beispiel für ein Security-Problem, das Werkzeuge kaum aufdecken können.
© Hitex

Bild 4 zeigt solch 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 (z.B. Webcam) 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. Abhilfe schaffen 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 Codierrichtlinien für Safety (z.B. MISRA) oder Codierrichtlinien speziell für Security verwendet werden, denn es gibt große Übereinstimmungen. Aber nicht alle Vorgaben aus Codierrichtlinien 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 wie etwa fest-codierte Passwörter, denen mit statischen Analysewerkzeugen nicht beizukommen ist.

 

Literatur
 
[1] https://cwe.mitre.org
[2] https://cve.mitre.org
[3] ISO/IEC TS 17961:2013, Information technology — Programming languages, their environments & system software interfaces — C Secure Coding Rules. Geneva, Switzerland: ISO/IEC, Nov. 2013
[4] SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems, 2016th ed. Software Engineering, Carnegie Mellon University, 2016
[5] ISO 26262, International Standard, Road vehicles – Functional Safety, Second edition, 2018
[6] MISRA C:2012, Guidelines for the use of the C language in critical systems, Horiba Mira Limited, UK, Edition 3, März 2013.
[7] MISRA C:2012 Amendment 1 — Additional security guidelines for MISRA C:2012. Horiba Mira Limited, UK, Apr. 2016.
[8] 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.
[9] MISRA C:2012 Addendum 3 — Cov- erage of MISRA C:2012 (including Amendment 1) against CERT C 2016 Edition. Horiba Mira Limited, UK, Jan. 2018.
[10] Statisches Analysewerkzeug von BUGSENG, mehr Info: www.hitex.de/eclair

 

Der Autor

 

Frank Büchner von Hitex
Frank Büchner von Hitex
© Hitex

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 Softwarequalität. Momentan arbeitet er als Principal Engineer Software Quality bei Hitex in Karlsruhe.


Verwandte Artikel

Hitex GmbH