Dalam artikel sebelumnya dalam seri ini, kami membahas keamanan memori dan keamanan utas di Rust. Dalam artikel terakhir ini, kita akan melihat implikasi aplikasi Rust nyata menggunakan proyek Quantum CSS .Mesin CSS menerapkan aturan CSS ke halaman. Ini adalah proses menurun yang menurunkan pohon DOM, setelah menghitung CSS induk, gaya anak dapat dihitung secara independen: ideal untuk komputasi paralel. Pada 2017, Mozilla melakukan dua upaya untuk memparalelkan sistem gaya menggunakan C ++. Keduanya gagal.
Pengembangan CSS kuantum telah mulai meningkatkan produktivitas. Meningkatkan keamanan hanyalah efek samping yang baik.
Ada koneksi tertentu antara perlindungan memori dan bug keamanan informasi. Oleh karena itu, kami berharap menggunakan Rust akan mengurangi permukaan serangan di Firefox. Artikel ini akan melihat potensi kerentanan yang telah diidentifikasi dalam mesin CSS sejak rilis awal Firefox pada tahun 2002. Kemudian lihat apa yang bisa dan tidak bisa dicegah dengan Rust.
Sepanjang waktu, 69 kesalahan keamanan telah terdeteksi di komponen CSS Firefox. Jika kami memiliki mesin waktu dan kami dapat menulisnya Rust sejak awal, maka 51 (73,9%) kesalahan akan menjadi tidak mungkin. Meskipun Rust membuat penulisan kode yang baik lebih mudah, ia juga tidak menawarkan perlindungan absolut.
Karat
Rust adalah bahasa pemrograman sistem modern yang aman untuk jenis dan memori. Sebagai efek samping dari jaminan keamanan ini, program Rust juga aman saat dikompilasi. Karenanya, Rust sangat cocok untuk:
- pemrosesan yang aman dari data yang masuk tidak dapat diandalkan;
- konkurensi untuk meningkatkan kinerja;
- integrasi masing-masing komponen ke dalam basis kode yang ada.
Namun, Rust tidak secara eksplisit memperbaiki beberapa kelas kesalahan, terutama kesalahan kebenaran. Bahkan, ketika teknisi kami menulis ulang Quantum CSS, mereka secara tidak sengaja mengulangi bug keamanan kritis, yang sebelumnya diperbaiki dalam kode C ++, mereka secara tidak sengaja menghapus perbaikan
bug 641731 , yang memungkinkan kebocoran sejarah global melalui SVG. Kesalahan telah didaftarkan ulang sebagai
bug 1420001 . Kebocoran riwayat dinilai sebagai kerentanan keamanan kritis. Perbaikan awal adalah pemeriksaan tambahan untuk melihat apakah dokumen SVG adalah gambar. Sayangnya, pemeriksaan ini tidak terjawab saat menulis ulang kode.
Meskipun pengujian otomatis harus menemukan pelanggaran aturan
:visited
seperti ini, dalam praktiknya mereka tidak menemukan kesalahan ini. Untuk mempercepat pengujian otomatis, kami menonaktifkan sementara mekanisme yang menguji fitur ini - tes tidak terlalu berguna jika tidak dilakukan. Risiko menerapkan kembali kesalahan logis dapat dikurangi dengan cakupan tes yang baik. Tetapi masih ada bahaya kesalahan logis baru.
Ketika seorang pengembang menjadi akrab dengan Rust, kodenya menjadi lebih aman. Meskipun Rust tidak mencegah semua kemungkinan kerentanan, ia memperbaiki seluruh kelas bug paling serius.
Kesalahan Keamanan CSS Quantum
Secara umum, secara default, Rust mencegah kesalahan yang terkait dengan memori, batas, variabel null / tidak diinisialisasi, dan kelebihan bilangan bulat. Bug non-standar yang disebutkan di atas tetap memungkinkan: kerusakan terjadi karena alokasi memori yang gagal.
Kesalahan Keamanan berdasarkan Kategori
- Memori: 32
- Batas: 12
- Implementasi: 12
- Null: 7
- Stack Overflow: 3
- Overflow bilangan bulat: 2
- Lainnya: 1
Dalam analisis kami, semua bug terkait dengan keamanan, tetapi hanya 43 yang menerima peringkat resmi (ditugaskan oleh insinyur keamanan Mozilla berdasarkan asumsi yang memenuhi syarat tentang "eksploitasi"). Bug biasa dapat menunjukkan fungsi yang hilang atau semacam kerusakan, yang tidak selalu menyebabkan kebocoran data atau perubahan perilaku. Kesalahan keamanan resmi berkisar dari kepentingan rendah (jika ada batasan kuat pada permukaan serangan) hingga kerentanan kritis (memungkinkan penyerang menjalankan kode arbitrer pada platform pengguna).
Kerentanan memori sering diklasifikasikan sebagai masalah keamanan serius. Dari 34 masalah kritis / serius, 32 terkait dengan memori.
Distribusi keparahan bug keamanan
- Total: 70
- Kesalahan Keamanan: 43
- Kritis / Serius: 34
- Fixed Rust: 32
Membandingkan Rust dan C ++
Bug 955913 - tumpukan buffer overflow dalam fungsi
GetCustomPropertyNameAt
. Kode menggunakan variabel yang salah untuk pengindeksan, yang menyebabkan interpretasi memori setelah akhir array. Ini dapat menyebabkan kerusakan saat mengakses pointer buruk atau menyalin memori ke string yang diteruskan ke komponen lain.
Urutan semua
properti CSS (termasuk kustom, mis. Kustom) disimpan dalam larik
mOrder
. Setiap elemen diwakili oleh nilai properti CSS, atau, dalam kasus properti khusus, nilai yang dimulai dengan
eCSSProperty_COUNT
(jumlah total properti CSS non-kustom). Untuk mendapatkan nama properti khusus, Anda harus terlebih dahulu mendapatkan nilai dari
mOrder
, dan kemudian mengakses nama dalam indeks yang sesuai dari array
mVariableOrder
, yang menyimpan nama-nama properti kustom secara berurutan.
Kode C ++ yang Rentan:
void GetCustomPropertyNameAt(uint32_t aIndex, nsAString& aResult) const { MOZ_ASSERT(mOrder[aIndex] >= eCSSProperty_COUNT); aResult.Truncate(); aResult.AppendLiteral("var-"); aResult.Append(mVariableOrder[aIndex]);
Masalahnya terjadi pada baris 6 saat menggunakan
aIndex
untuk mengakses elemen dari array
mVariableOrder
. Faktanya adalah
aIndex
harus digunakan dengan array
mOrder
, bukan
mVariableOrder
. Elemen yang sesuai untuk properti kustom yang diwakili oleh
aIndex
di
mOrder
sebenarnya adalah
mOrder[aIndex] - eCSSProperty_COUNT
.
Kode C ++ yang diperbaiki:
void Get CustomPropertyNameAt(uint32_t aIndex, nsAString& aResult) const { MOZ_ASSERT(mOrder[aIndex] >= eCSSProperty_COUNT); uint32_t variableIndex = mOrder[aIndex] - eCSSProperty_COUNT; aResult.Truncate(); aResult.AppendLiteral("var-"); aResult.Append(mVariableOrder[variableIndex]); }
Kode Rust yang sesuai
Meskipun Rust agak mirip dengan C ++, ia menggunakan abstraksi dan struktur data lainnya. Kode karat akan sangat berbeda dari C ++ (lihat di bawah untuk lebih jelasnya). Pertama, mari kita lihat apa yang terjadi jika kode rentan diterjemahkan sejelas mungkin:
fn GetCustomPropertyNameAt(&self, aIndex: usize) -> String { assert!(self.mOrder[aIndex] >= self.eCSSProperty_COUNT); let mut result = "var-".to_string(); result += &self.mVariableOrder[aIndex]; result }
Compiler Rust akan menerima kode ini karena panjang vektor tidak dapat ditentukan sebelum eksekusi. Tidak seperti array, panjang yang harus diketahui,
tipe Vec di Rust memiliki ukuran dinamis. Namun, pemeriksaan batas dibangun ke dalam implementasi vektor perpustakaan standar. Jika indeks yang tidak valid muncul, program segera berakhir dengan cara yang terkontrol, mencegah akses yang tidak sah.
Kode nyata Quantum CSS menggunakan struktur data yang sangat berbeda, sehingga tidak ada padanan yang pasti. Sebagai contoh, kami menggunakan struktur data bawaan Rust yang kuat untuk menyatukan pengaturan dan nama properti. Ini menghilangkan kebutuhan untuk mempertahankan dua array independen. Struktur data karat juga meningkatkan enkapsulasi data dan mengurangi kemungkinan kesalahan logis semacam itu. Karena kode harus berinteraksi dengan kode C ++ di bagian lain browser, fungsi
GetCustomPropertyNameAt
baru tidak terlihat seperti kode idiomatik Rust. Tapi itu masih memberikan semua jaminan keamanan, sambil memberikan abstraksi yang lebih dimengerti dari data yang mendasarinya.
tl; dr
Karena kerentanan sering dikaitkan dengan pelanggaran keamanan memori, kode Rust harus secara signifikan mengurangi jumlah
CVE kritis. Tetapi bahkan Rust tidak sempurna. Pengembang masih perlu melacak kesalahan kebenaran dan serangan kebocoran data. Dukungan untuk perpustakaan aman masih memerlukan tinjauan kode, tes, dan fuzzing.
Compiler tidak dapat menangkap semua kesalahan programmer. Namun demikian, Rust melepaskan beban keamanan memori kami, memungkinkan kami untuk fokus pada kebenaran logis dari kode.