Tentang analisis statis dalam semua kejujuran

Baru-baru ini, mereka semakin berbicara tentang analisis statis sebagai salah satu cara penting untuk memastikan kualitas produk perangkat lunak yang dikembangkan, terutama dari sudut pandang keamanan. Analisis statis memungkinkan Anda untuk menemukan kerentanan dan kesalahan lainnya, dapat digunakan dalam proses pengembangan, diintegrasikan ke dalam proses yang disesuaikan. Namun, sehubungan dengan penerapannya, banyak pertanyaan muncul. Apa perbedaan antara alat berbayar dan gratis? Mengapa tidak cukup menggunakan linter? Pada akhirnya, apa hubungan statistik dengan itu? Mari kita coba mencari tahu.



Mari kita jawab pertanyaan terakhir segera - statistik tidak ada hubungannya dengan itu, meskipun analisis statis sering keliru disebut statistik. Analisisnya statis, karena aplikasi tidak memulai selama pemindaian.

Pertama, mari kita cari tahu apa yang ingin kita cari dalam kode program. Analisis statis paling sering digunakan untuk mencari kerentanan - bagian kode, yang keberadaannya dapat mengarah pada pelanggaran kerahasiaan, integritas, atau ketersediaan sistem informasi. Namun, teknologi yang sama dapat digunakan untuk mencari kesalahan atau fitur kode lainnya.

Kami membuat reservasi yang secara umum masalah analisis statis secara algoritmik tidak dapat diselesaikan (misalnya, oleh teorema Rice). Oleh karena itu, Anda harus membatasi kondisi tugas, atau membiarkan ketidakakuratan dalam hasil (lewati kerentanan, berikan positif palsu). Ternyata pada program nyata, opsi kedua ternyata berfungsi.

Ada banyak alat berbayar dan gratis yang mengklaim pencarian kerentanan dalam aplikasi yang ditulis dalam berbagai bahasa pemrograman. Mari kita perhatikan bagaimana penganalisa statis biasanya diatur. Selanjutnya kita akan fokus pada inti penganalisa dan algoritma. Tentu saja, alat tersebut dapat berbeda dalam hal keramahan antarmuka, dalam set fungsi, dalam set plug-in untuk sistem yang berbeda dan dalam kemudahan penggunaan API. Ini mungkin topik untuk artikel terpisah.

Presentasi menengah


Tiga langkah dasar dapat dibedakan dalam skema operasi penganalisa statis.

  1. Membangun representasi perantara (representasi perantara juga disebut representasi internal atau model kode).
  2. Penggunaan algoritma analisis statis, sebagai akibatnya model kode dilengkapi dengan informasi baru.
  3. Menerapkan aturan pencarian kerentanan ke model kode augmented.

Analisis statis yang berbeda dapat menggunakan model kode yang berbeda, misalnya, kode sumber program, aliran token, parse tree, kode tiga-alamat, grafik aliran kontrol, bytecode - standar atau asli - dan seterusnya.

gambar

Seperti kompiler, analisis leksikal dan sintaksis digunakan untuk membangun representasi internal, paling sering sebuah pohon analisis (AST, Pohon Sintaksis Abstrak). Analisis leksikal memecah teks program menjadi elemen semantik minimal, pada output yang menerima aliran token. Parsing memverifikasi bahwa aliran token cocok dengan tata bahasa bahasa pemrograman, yaitu, aliran token yang dihasilkan benar dari sudut pandang bahasa. Sebagai hasil parsing, pohon parsing dibangun - struktur yang memodelkan kode sumber program. Selanjutnya, analisis semantik diterapkan, itu memeriksa pemenuhan kondisi yang lebih kompleks, misalnya, korespondensi tipe data dalam instruksi penugasan.

Pohon parse dapat digunakan sebagai representasi internal. Anda juga bisa mendapatkan model lain dari pohon parse. Misalnya, Anda bisa menerjemahkannya menjadi kode tiga-alamat, yang, pada gilirannya, membangun grafik aliran kontrol (CFG). Biasanya, CFG adalah model utama untuk algoritma analisis statis.

gambar

Dalam analisis biner (analisis statis kode biner atau yang dapat dieksekusi), model juga dibuat, tetapi praktik rekayasa terbalik sudah digunakan di sini: dekompilasi, deobfusiasi, terjemahan terbalik. Hasilnya, Anda bisa mendapatkan model yang sama seperti dari kode sumber, termasuk kode sumber (menggunakan dekompilasi penuh). Terkadang kode biner itu sendiri dapat berfungsi sebagai representasi perantara.

Secara teoritis, semakin dekat model ke kode sumber, semakin buruk kualitas analisisnya. Pada kode sumber itu sendiri, Anda hanya dapat mencari ekspresi reguler, yang tidak akan memungkinkan Anda untuk menemukan setidaknya kerentanan rumit.

Analisis aliran data


Salah satu algoritma analisis statis utama adalah analisis aliran data. Tugas analisis ini adalah menentukan di setiap titik dalam program beberapa informasi tentang data yang dioperasikan oleh kode. Informasi mungkin berbeda, misalnya tipe data atau nilai. Bergantung pada informasi apa yang perlu ditentukan, tugas menganalisis aliran data dapat dirumuskan.



Misalnya, jika perlu untuk menentukan apakah ekspresi adalah konstanta, serta nilai konstanta ini, maka masalah konstanta propagasi (propagasi konstan) diselesaikan. Jika perlu untuk menentukan jenis variabel, maka kita dapat berbicara tentang masalah perambatan jenis. Jika Anda perlu memahami variabel mana yang dapat menunjuk ke area memori tertentu (menyimpan data yang sama), maka kita berbicara tentang tugas analisis sinonim (alias analisis). Ada banyak tugas analisis aliran data lainnya yang dapat digunakan dalam analisis statis. Seperti langkah-langkah membangun model kode, tugas-tugas ini juga digunakan dalam kompiler.

Dalam teori pembuatan kompiler, solusi untuk masalah analisis intra-prosedural dari aliran data dijelaskan (perlu untuk melacak data dalam satu prosedur / fungsi / metode tunggal). Keputusan didasarkan pada teori kisi aljabar dan elemen lain dari teori matematika. Masalah menganalisis aliran data dapat diselesaikan dalam waktu polinomial, yaitu, untuk waktu yang dapat diterima untuk komputer, jika kondisi masalah memenuhi kondisi teorema solvabilitas, yang dalam praktiknya tidak selalu terjadi.

Kami akan memberi tahu Anda lebih banyak tentang menyelesaikan masalah analisis intra-prosedural dari aliran data. Untuk menetapkan tugas tertentu, selain menentukan informasi yang Anda butuhkan, Anda perlu menentukan aturan untuk mengubah informasi ini saat meneruskan data sesuai dengan instruksi dalam CFG. Ingat bahwa node dalam CFG adalah blok dasar - set instruksi, eksekusi yang selalu berurutan, dan busur menunjukkan kemungkinan transfer kontrol antara blok dasar.

Untuk setiap instruksi Sset didefinisikan:

  • gen(S)(informasi yang dihasilkan oleh instruksi S),
  • kill(S)(informasi dihancurkan oleh instruksi S),
  • in(S)(informasi pada titik sebelum instruksi S),
  • out(S)(informasi pada titik setelah instruksi S)

Tujuan dari analisis aliran data adalah untuk mendefinisikan set in(S)dan out(S)untuk setiap instruksi Sprogram. Sistem dasar persamaan yang dengannya tugas menganalisis aliran data dipecahkan ditentukan oleh hubungan berikut (persamaan aliran data):

out(S)=(dalam(S)βˆ’kill(S))βˆͺgen(S),


in(S)=βˆͺiout(Si).



Relasi kedua merumuskan aturan di mana informasi "digabungkan" pada titik pertemuan busur CFG ( Si- pendahulu Sdalam CFG). Operasi penyatuan, persimpangan dan beberapa lainnya dapat digunakan.

Informasi yang diinginkan (himpunan nilai fungsi yang diperkenalkan di atas) diformalkan sebagai kisi aljabar. Fungsi gendan killdianggap sebagai pemetaan monoton pada kisi (fungsi aliran). Untuk persamaan aliran data, solusinya adalah titik tetap dari pemetaan ini.

Algoritma untuk menyelesaikan masalah analisis aliran data mencari titik tetap maksimum. Ada beberapa pendekatan untuk solusi: algoritma iteratif, analisis komponen yang sangat terhubung, analisis T1-T2, analisis interval, analisis struktural, dan sebagainya. Ada teorema tentang kebenaran dari algoritma ini, mereka menentukan ruang lingkup penerapannya untuk masalah nyata. Saya ulangi, kondisi teorema mungkin tidak terpenuhi, yang mengarah pada algoritma yang lebih rumit dan hasil yang tidak akurat.

Analisis antar prosedural


Dalam praktiknya, adalah perlu untuk menyelesaikan masalah analisis antar-proses dari aliran data, karena jarang kerentanan akan sepenuhnya terlokalisasi dalam satu fungsi. Ada beberapa algoritma umum.

Fungsi sebaris . Pada titik panggilan fungsi, kami menanamkan fungsi yang dipanggil, dengan demikian mengurangi tugas analisis antar-prosedur menjadi tugas analisis intraprosedural. Metode ini mudah diimplementasikan, tetapi dalam praktiknya, ketika diterapkan, ledakan kombinatorial cepat tercapai.

Membuat grafik umum dari aliran kontrol program di mana pemanggilan fungsi digantikan oleh transisi ke alamat awal dari fungsi yang dipanggil, dan instruksi pengembalian digantikan oleh transisi ke semua instruksi mengikuti semua instruksi untuk memanggil fungsi ini. Pendekatan ini menambahkan sejumlah besar jalur eksekusi yang tidak dapat direalisasi, yang sangat mengurangi keakuratan analisis.

Algoritma mirip dengan yang sebelumnya, tetapi ketika beralih ke fungsi, konteksnya disimpan - misalnya, bingkai tumpukan. Dengan demikian, masalah membuat jalur yang tidak dapat direalisasi diselesaikan. Namun, algoritma ini berlaku dengan kedalaman panggilan terbatas.

Informasi fungsi bangunan (ringkasan fungsi). Algoritma analisis antar prosedur yang paling berlaku. Ini didasarkan pada konstruksi ringkasan untuk masing-masing fungsi: aturan-aturan di mana informasi tentang data dikonversi saat menerapkan fungsi ini, tergantung pada berbagai nilai argumen input. Ringkasan siap pakai digunakan dalam analisis fungsi prosedural internal. Kesulitan terpisah di sini adalah penentuan urutan traversal fungsi, karena dalam analisis kasus per kasus, ringkasan harus sudah dibangun untuk semua fungsi yang disebut. Biasanya, algoritma iteratif khusus untuk melintasi grafik panggilan dibuat.

Analisis aliran data antarprocedural adalah tugas yang sulit secara eksponensial, oleh karena itu penganalisa perlu melakukan sejumlah optimasi dan asumsi (tidak mungkin untuk menemukan solusi yang tepat dalam waktu yang cukup untuk daya komputasi). Biasanya, ketika mengembangkan alat analisis, perlu untuk menemukan kompromi antara jumlah sumber daya yang dikonsumsi, waktu analisis, jumlah positif palsu dan kerentanan yang ditemukan. Oleh karena itu, penganalisa statis dapat bekerja untuk waktu yang lama, mengkonsumsi banyak sumber daya dan memberikan hasil positif palsu. Namun, tanpa ini tidak mungkin untuk menemukan kerentanan yang paling penting.

Pada titik inilah analis statis serius berbeda dari banyak alat terbuka, yang, khususnya, dapat memposisikan diri dalam pencarian kerentanan. Pemeriksaan cepat dalam waktu linier baik ketika Anda perlu mendapatkan hasilnya dengan cepat, misalnya, selama kompilasi. Namun, pendekatan ini tidak dapat menemukan kerentanan paling kritis - misalnya, terkait dengan implementasi data.

Analisis noda


Kita juga harus memikirkan salah satu tugas analisis aliran data - analisis noda. Analisis noda memungkinkan Anda untuk mendistribusikan bendera di seluruh program. Tugas ini adalah kunci untuk keamanan informasi, karena dengan bantuannya ditemukan kerentanan terkait dengan implementasi data (injeksi SQL, skrip lintas situs, pengalihan terbuka, pemalsuan jalur file, dan sebagainya), serta kebocoran data rahasia (entri kata sandi di log peristiwa, transfer data tidak aman).

Mari kita coba mensimulasikan tugas. Misalkan kita ingin melacak n flag - f1,f2,...,fn. Banyak informasi di sini akan banyak himpunan bagian \ {f_1, ..., f_n \} , karena untuk setiap variabel di setiap titik dalam program, kami ingin menentukan benderanya.

Selanjutnya, kita perlu mendefinisikan fungsi aliran. Dalam hal ini, fungsi aliran dapat ditentukan dengan pertimbangan berikut.

  • Banyak aturan diberikan di mana konstruksi didefinisikan yang mengarah ke penampilan atau perubahan dari set bendera.
  • Operasi penugasan membalik bendera dari kanan ke kiri.
  • Setiap operasi yang tidak dikenal untuk set aturan menggabungkan flag dari semua operan dan set final flag ditambahkan ke hasil operasi.


Akhirnya, Anda perlu mendefinisikan aturan untuk menggabungkan informasi pada titik persimpangan busur CFG. Penggabungan ditentukan oleh aturan serikat, yaitu, jika set bendera yang berbeda untuk satu variabel berasal dari blok dasar yang berbeda, maka mereka digabungkan ketika menggabungkan. Termasuk positif palsu berasal dari sini: algoritma tidak menjamin bahwa jalur ke CFG tempat bendera muncul dapat dieksekusi.

Misalnya, Anda perlu mendeteksi kerentanan seperti SQL Injection. Kerentanan ini terjadi ketika data yang tidak diverifikasi dari pengguna masuk ke dalam metode bekerja dengan database. Sangat penting untuk menentukan bahwa data berasal dari pengguna dan menambahkan tanda noda ke data ini. Biasanya, basis aturan analisa menetapkan aturan untuk mengatur bendera yang kotor. Misalnya, atur bendera ke nilai pengembalian metode getParameter () dari kelas Permintaan.



Selanjutnya, Anda perlu mendistribusikan flag di seluruh program yang dianalisis menggunakan analisis taint, mengingat bahwa data dapat divalidasi dan flag tersebut mungkin hilang pada salah satu jalur eksekusi. Alat analisis menetapkan banyak fungsi yang menghapus bendera. Misalnya, fungsi memvalidasi data dari tag html dapat menghapus tanda untuk kerentanan skrip lintas situs (XSS). Atau, fungsi untuk mengikat variabel ke ekspresi SQL menghilangkan tanda untuk disematkan dalam SQL.

Aturan Pencarian Kerentanan


Sebagai hasil dari penerapan algoritma di atas, representasi perantara dilengkapi dengan informasi yang diperlukan untuk mencari kerentanan. Misalnya, dalam model kode, informasi muncul tentang variabel mana yang termasuk dalam bendera tertentu, yang datanya konstan. Aturan pencarian kerentanan dirumuskan dalam bentuk model kode. Aturan menjelaskan fitur apa dalam tampilan interim final yang mengindikasikan kerentanan.

Misalnya, Anda bisa menerapkan aturan pencarian kerentanan yang mendefinisikan pemanggilan metode dengan parameter yang memiliki flag taint. Kembali ke contoh injeksi SQL, kami memverifikasi bahwa variabel-variabel dengan flag taint tidak termasuk dalam fungsi query database.

Ternyata bagian penting dari penganalisa statis, selain kualitas algoritma, adalah konfigurasi dan basis aturan: deskripsi yang membangun dalam kode menghasilkan bendera atau informasi lainnya, yang membangun memvalidasi data tersebut, dan yang membangun penggunaan data tersebut sangat penting.

Pendekatan lain


Selain analisis aliran data, ada beberapa pendekatan lain. Salah satu yang terkenal adalah eksekusi simbolik atau interpretasi abstrak. Dalam pendekatan ini, program berjalan pada domain abstrak, perhitungan dan distribusi pembatasan data dalam program. Dengan menggunakan pendekatan ini, seseorang tidak hanya dapat menemukan kerentanan, tetapi juga menghitung kondisi pada data input di mana kerentanan dieksploitasi. Namun, pendekatan ini memiliki kelemahan serius - dengan solusi standar pada program nyata, algoritma meledak secara eksponensial, dan optimasi menyebabkan kerugian serius dalam kualitas analisis.

Kesimpulan


Pada akhirnya, saya pikir ini layak untuk disimpulkan, berbicara tentang pro dan kontra dari analisis statis. Adalah logis bahwa kita akan membandingkan dengan analisis dinamis, di mana pencarian kerentanan terjadi selama eksekusi program.



Keuntungan tidak diragukan dari analisis statis adalah cakupan lengkap dari kode yang dianalisis. Juga, keuntungan dari analisis statis termasuk fakta bahwa untuk menjalankannya tidak perlu menjalankan aplikasi di lingkungan pertempuran. Analisis statis dapat diimplementasikan pada tahap awal pengembangan, meminimalkan biaya kerentanan yang ditemukan.

Kerugian dari analisis statis adalah kehadiran positif palsu yang tidak terhindarkan, konsumsi sumber daya dan waktu pemindaian yang lama pada sejumlah besar kode. Namun, kerugian ini tidak dapat dihindari, berdasarkan pada spesifikasi algoritma. Seperti yang kita lihat, penganalisa cepat tidak akan pernah menemukan kerentanan nyata seperti injeksi SQL dan sejenisnya.

Kami menulis di artikel lain tentang kesulitan yang tersisa menggunakan alat analisis statis, yang, ternyata, dapat diatasi dengan cukup baik.

Source: https://habr.com/ru/post/id439286/


All Articles