Statyczna analiza kodu
Analiza statyczna kodu - analiza struktury kodu źródłowego lub kodu skompilowanego bez jego uruchomienia.
Static Analysis Software Testing (SAST) - narzędzia do automatycznej analizy statycznej
Analiza statyczna a inspekcja kodu („code review”)
SDL for Agile
Agile Development Using Microsoft Security Development Lifecycle - analiza statyczna w każdym sprincie
Źródło: http://www.microsoft.com/security/sdl/discover/sdlagile.aspx
Metody analizy
- analiza leksykograficzna - wyszukiwanie niebezpiecznych konstrukcji w kodzie źródłowym, zazwyczaj na podstawie zestawu reguł, różnych heurystyk i dopasowania do wzorców błedów
- metody formalne - oparte na matematycznej definicji zachowania programu
- modele matematyczne
- reguły logiczne Hoare_logic
- analiza przepływu Data-flow_analysis
- metryki kodu źródłowego - ocena jakości kodu źródłowego na podstawie danych statystycznych.
Zalety
- Szybkość działania - szybkie wykrywanie błędów, których naprawa jest prosta i mało kosztowna
- nie wymagają uruchamiania programu
- łatwe do zrównoleglenia
- Łatwość użycia - proste we wdrożeniu w cykl wytwarzania oprogramowania
- Automatyzacja - integracja z narzędziami continius integrations
- Możliwości rozszerzania: własne reguły, wtyczki, …
- Integracja z innymi narzędziami: serwery automatyzacji, IDE, kontrola wersji
- Dużo darmowych narzędzi
Wady
- Wymagany dostęp do źródeł
- Reguły wykrywają zazwyczaj proste błędy i nie są w stanie wyeliminować ręcznego sprawdzenia kodu
- Dużo szumu, zbyt czułe - duże prawdopodobieństwo zaklasyfikowanie poprawnego fragmentu jako błędu (false positive)
- Każde narzędzie zazwyczaj pokrywa pewien zakres testów (do 14% błędów?!). Dlatego warto korzystać z kilku różnych skanerów kodu. Istnieją narzędzia integrujące wiele narzędzi SAST, np: CodeEx, Yast (open source)
Czego dotyczy analiza statyczna
- analiza poprawności składni
- luki w bezpieczeństwie, także błędy które mogą pojawić się przy specyficznych danych wejściowych
- detekcja backdoors, niebezpieczne i nieaktualne funkcje, wycieki pamięci, przepełnienie bufora, używanie niezainicjowanych zmiennych, SQL Injections,
- jakość kodu, ocena stylu, powtórzenia kodu, nieużywane fragmenty kodu, …
- wydajność, wykrywanie wąskich gardeł, niewydajne konstrukcje, sugestie dotyczące poprawienia wydajności
- zgodność z dobrymi praktykami, zachowanie standardów, norm nazewniczych, problemy z przenośnością kodu
Dlaczego analizować kod?
Statyczna analiza programu pozwala na:
- zwiększenie wydajności i stabilności poprzez zasady oparte na dobrych praktykach,
- unikniecie typowych błędów podczas programowania,
- dostarczenie struktury do zarządzania standardami kodu.
- wymuszenie zasad i standardów pisania kodu
- zwiększanie bezpieczeństwa poprzez kolejny etap testowania
- analizując sygnalizowane błędy można się sporo nauczyć na temat dobrych praktyk bezpiecznego programowania
Dlaczego analizować kod?
Koszt naprawy błędu w zależności od momentu wykrycia
Źródło: http://www.microsoft.com/security/sdl/about/benefits.aspx
Narzędzia
Lint - UNIX V7 1979, historyczny program od którego nazwy często określa się narzędzia do analizy i szukania błedów
- C/C++: cppcheck, clint, splint, Clang Static Analyzer (składowa kompilatora clang), frama-c, CODAN wbudowany w CDT (wtyczka Eclipse)
- Python: pylint
- dotNet: FxCop, CAT.NET
- JavaScript: jslint
Wielojęzykowe
- rats (rough auditing tool for security) C, C++, Perl, PHP, Python
- PMD Java, JavaScript, XML, XSL (zawiera CPD analiza powtórzeń: C, C++, C#, PHP, Ruby, Fortran)
- Yasca Java, C/C++, HTML, JavaScript, ASP, ColdFusion, PHP, COBOL, .NET, and other languages + integracja z innymi SAT
- VisualCodeGrepper C/C++, C#, VB, PHP, Java and PL/SQL
Wiele innych:
Kompilatory
Kompilatory oprócz sprawdzania składni również potrafią sygnalizować możliwość występowania błędu w kodzie.
-pedantic
tylko czyste ISO C i ISO C++-Wall
wszystkie ostrzeżenia-Werror
ostrzeżenia generują błąd kompilacji-Wextra
dodatkowe ostrzeżenia wykraczające pozaWall
Visual C++: warning level 4
cppcheck
- analiza kodu c/C++
- CLI, GUI, biblioteka (API)
- Raporty: txt, XML, HTML
- Cel:
- wykrywanie błędów, które nie są wykrywane przez kompilatory
- brak fałszywych alarmów - wykrywanie tylko prawdziwych błędów
- wielowątkowy
- wsparcie: Code::Blocks (wbudowany), Hudson, wtyczka Visual Studio
- możliwość dodawania własnych reguł opartych na wyrażeniach regularnych
Wykrywane błedu
- wykroczenia poza zakres tablicy
- wycieki pamięci
- odwołania do adresu NULL
- niezainicjowane zmienne
- używanie przestarzałych lub niebezpiecznych funkcji
- wykrywanie niedostępnych fragmentów kodu oraz powtórzeń kodu
- wiele innych …
Ograniczenia
- Jest wiele typów błędów, które ciężko jest wykryć, np błędy związane są z konfiguracją lub logiką biznesową
- Problemem są zależności, dodatkowe moduły, biblioteki, konteksty dodawane przez frameworki
- Skomplikowane algorytmy i architektury bardzo ciężko jest analizować
// calculate the number of days int days = hours / 23;
Przykład
int main() { char *x; int a[20]; *x = 5; a[20] = 5; return 0; }
cppcheck --enable=all test1.c Checking test1.c... [test1.c:4]: (style) Variable 'x' is not assigned a value. [test1.c:8]: (style) Variable 'a' is assigned a value that is never used. [test1.c:8]: (error) Array 'a[20]' accessed at index 20, which is out of bounds. [test1.c:7]: (error) Uninitialized variable: x Checking usage of global functions..
Przykład
#include <stdio.h> int main() { char c; while (c != 'x'); { c = getchar(); if (c = 'x') return 0; switch (c) { case '\n': case '\r': printf("Newline\n"); default: printf("%c",c); } } return 0; }
Źródło: wikipedia.org
Przykład: buffer overflow
Śledzenie wywołań funkcji:
void f1(char *s) { s[20] = 0; } void f2() { char a[10]; if (x + y == 2) { f1(a); } }
Array 'a[10]' index 20 out of bounds
Prawie to samo ale cppcheck
już nie wykrywa błędu
void f3(char *s) { if (x + y == 2) { s[20] = 0; } } void f4() { char a[10]; f3(a); }
Wycieki pamięci
void f() { char *a = malloc(10); if (x + y == 2) { return; } free(a); }
Wynik cppcheck
[mem.c:5]: (error) Memory leak: a
void f2(int x) { char *a = 0; if (x == 10) a = malloc(10); if (x == 20) free(a); }
Brak błedu?!
Automatyzacja testów
- kiedy analizować: przed/po commicie, po każdym buildzie, po zapisaniu pliku
- IDE i ich edytory sprawdzają składnię też (rozszerzenia Resharper)
- testy po stronie developera lub po stronie repozytorium (serwera autoamtyzacji)
- skrypty uruchamiane po zatwierdzeniu zmiany + powiadomienia (email, inne alerty), w SVN
hooks/post-commit
- uniemożliwienie zatwierdzenia kodu z błędami, w SVN
hooks/pre-commit
- serwery automatyzacji, np. Hudson
Przykłady: FindBugs, Cppcheck+TortoiseSVN SVN pre-commit
Źródła
- Statyczna analiza bezpieczeństwa kodu aplikacji Adrian “Vizzdoom” Michalczy