Selama beberapa dekade, penggunaan kembali perangkat lunak telah dibahas lebih sering daripada sebelumnya. Hari ini situasinya sebaliknya: pengembang menggunakan kembali program orang lain setiap hari dalam bentuk ketergantungan perangkat lunak, dan masalahnya sendiri hampir tidak dijelajahi.
Pengalaman saya sendiri termasuk satu dekade bekerja dengan
repositori internal Google , di mana dependensi ditetapkan sebagai konsep prioritas, serta mengembangkan
sistem dependensi untuk bahasa pemrograman Go .
Ketergantungan membawa risiko serius yang terlalu sering diabaikan. Transisi ke penggunaan kembali sederhana dari perangkat lunak terkecil telah terjadi begitu cepat sehingga kami belum mengembangkan praktik terbaik untuk pemilihan dan penggunaan dependensi yang efektif. Bahkan untuk membuat keputusan saat itu tepat dan kapan tidak. Tujuan artikel ini adalah untuk menilai risiko dan merangsang pencarian solusi di bidang ini.
Apa itu kecanduan?
Dalam perkembangan modern,
ketergantungan adalah kode tambahan yang dipanggil dari suatu program. Menambahkan ketergantungan menghindari pengulangan pekerjaan yang sudah dilakukan: merancang, menulis, menguji, men-debug, dan mendukung unit kode tertentu. Kami menyebut unit kode ini
paket , meskipun pada beberapa sistem istilah lain, seperti perpustakaan atau modul, digunakan sebagai pengganti paket.
Menerima ketergantungan eksternal adalah praktik lama: sebagian besar programmer mengunduh dan menginstal pustaka yang diperlukan, baik itu PCRE atau zlib dari C, Boost atau Qt dari C ++, JodaTime atau Junit dari Jawa. Paket-paket ini memiliki kode debug berkualitas tinggi yang membutuhkan banyak pengalaman untuk membuat. Jika suatu program membutuhkan fungsionalitas dari paket seperti itu, jauh lebih mudah untuk mengunduh, menginstal dan memperbarui paket secara manual daripada mengembangkan fungsionalitas ini dari awal. Tetapi biaya dimuka yang besar berarti penggunaan kembali secara manual itu mahal: paket kecil lebih mudah untuk ditulis sendiri.
Manajer dependensi (kadang-kadang disebut manajer paket) mengotomatiskan pengunduhan dan pemasangan paket dependensi. Karena manajer dependensi memudahkan untuk mengunduh dan menginstal paket individual, menurunkan biaya tetap membuat paket kecil lebih ekonomis untuk diterbitkan dan digunakan kembali.
Misalnya, manajer dependensi Node.js yang disebut NPM menyediakan akses ke lebih dari 750.000 paket. Salah satunya,
escape-string-regexp
, berisi fungsi tunggal yang lolos dari operator ekspresi reguler dari data input. Semua implementasi:
var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; module.exports = function (str) { if (typeof str !== 'string') { throw new TypeError('Expected a string'); } return str.replace(matchOperatorsRe, '\\$&'); };
Sebelum manajer ketergantungan muncul, tidak mungkin membayangkan menerbitkan perpustakaan delapan baris: terlalu banyak biaya overhead dan terlalu sedikit manfaat. Tetapi NPM mengurangi overhead menjadi hampir nol, dengan hasil bahwa fungsi yang hampir sepele dapat dikemas dan digunakan kembali. Pada akhir Januari 2019, dependensi
escape-string-regexp
dibangun ke hampir seribu paket NPM lainnya, belum lagi semua paket yang ditulis pengembang untuk penggunaannya sendiri dan tidak dipublikasikan dalam domain publik.
Sekarang manajer dependensi telah muncul untuk hampir setiap bahasa pemrograman. Maven Central (Java), Nuget (.NET), Packagist (PHP), PyPI (Python) dan RubyGems (Ruby) - masing-masing memiliki lebih dari 100.000 paket. Munculnya penggunaan kembali yang begitu luas dari paket kecil adalah salah satu perubahan terbesar dalam pengembangan perangkat lunak selama dua dekade terakhir. Dan jika kita tidak lebih berhati-hati, ini akan menimbulkan masalah serius.
Apa yang bisa salah?
Dalam konteks diskusi ini, paket adalah kode yang diunduh dari Internet. Menambahkan ketergantungan mempercayakan pekerjaan mengembangkan kode ini - desain, penulisan, pengujian, debugging, dan dukungan - kepada orang lain di Internet yang biasanya Anda tidak tahu. Dengan menggunakan kode ini, Anda mengekspos program Anda sendiri untuk efek dari semua crash dan kekurangan ketergantungan. Pengerjaan perangkat lunak Anda sekarang secara harfiah
tergantung pada kode orang asing dari Internet. Sederhananya, semuanya terdengar sangat tidak aman. Mengapa ada orang yang setuju dengan ini?
Kami setuju, karena mudah, karena semuanya tampak bekerja, karena semua orang melakukannya juga, dan yang paling penting, karena tampaknya merupakan kelanjutan alami dari praktik yang sudah ada selama berabad-abad. Tetapi ada perbedaan penting yang kita abaikan.
Beberapa dekade yang lalu, sebagian besar pengembang juga memercayai orang lain untuk menulis program yang menjadi sandaran mereka, seperti sistem operasi dan kompiler. Perangkat lunak ini dibeli dari sumber terkenal, seringkali dengan semacam perjanjian dukungan. Masih ada ruang untuk
kesalahan atau perusakan langsung . Tapi setidaknya kami tahu dengan siapa kami berurusan dan, sebagai aturan, bisa menggunakan ukuran pengaruh komersial atau hukum.
Fenomena perangkat lunak open source, yang didistribusikan secara gratis melalui Internet, sebagian besar menggantikan praktik lama pembelian perangkat lunak. Ketika penggunaan kembali masih sulit, beberapa proyek memperkenalkan ketergantungan seperti itu. Meskipun lisensi mereka biasanya melepaskan "jaminan nilai komersial dan kesesuaian untuk tujuan tertentu," proyek membangun reputasi yang baik. Sebagian besar pengguna telah memperhitungkan reputasi ini saat mengambil keputusan. Alih-alih intervensi komersial dan hukum, dukungan reputasi datang. Banyak paket umum dari era itu masih menikmati reputasi yang baik: misalnya, BLAS (diterbitkan pada tahun 1979), Netlib (1987), libjpeg (1991), LAPACK (1992), HP STL (1994), dan zlib (1995).
Manajer batch telah mengurangi model penggunaan kembali kode menjadi sangat mudah: sekarang pengembang dapat membagikan kode dengan presisi ke fungsi individu dalam lusinan baris. Ini adalah pencapaian teknis yang luar biasa. Ada banyak paket yang tersedia, dan sebuah proyek dapat memasukkan sejumlah besar dari mereka, tetapi mekanisme kepercayaan kode komersial, hukum, atau reputasi adalah sesuatu dari masa lalu. Kami mempercayai lebih banyak kode, meskipun ada sedikit alasan untuk percaya.
Biaya untuk membuat kecanduan yang buruk dapat dilihat sebagai jumlah dari semua hasil buruk yang mungkin dalam serangkaian harga setiap hasil yang buruk dikalikan dengan probabilitasnya (risiko).

Harga hasil yang buruk tergantung pada konteks di mana ketergantungan digunakan. Pada satu ujung spektrum adalah proyek hobi pribadi di mana harga sebagian besar hasil buruk mendekati nol: Anda hanya bersenang-senang, kesalahan tidak memiliki dampak nyata kecuali untuk sedikit lebih banyak waktu yang dihabiskan, dan men-debug mereka bahkan bisa menyenangkan. Dengan demikian, probabilitas risiko hampir tidak relevan: dikalikan dengan nol. Di ujung lain dari spektrum adalah perangkat lunak produksi, yang harus didukung selama bertahun-tahun. Di sini, biaya ketergantungan bisa sangat tinggi: server dapat jatuh, data rahasia dapat diungkapkan, pelanggan dapat menderita, perusahaan bahkan mungkin bangkrut. Dalam produksi, jauh lebih penting untuk mengevaluasi dan meminimalkan risiko kegagalan serius.
Terlepas dari harga yang diharapkan, ada beberapa pendekatan untuk menilai dan mengurangi risiko menambah ketergantungan. Sangat mungkin bahwa manajer paket harus dioptimalkan untuk mengurangi risiko ini, sementara sejauh ini mereka telah berfokus pada pengurangan biaya pengunduhan dan pemasangan.
Pemeriksaan ketergantungan
Anda tidak akan menyewa pengembang yang belum pernah Anda dengar dan tidak tahu apa-apa tentangnya. Pertama, Anda akan belajar sesuatu tentang dia: periksa tautannya, lakukan wawancara, dan sebagainya. Sebelum bergantung pada paket yang Anda temukan di Internet, sebaiknya pelajari sedikit tentang paket ini.
Pemeriksaan dasar dapat memberikan gambaran tentang kemungkinan masalah saat mencoba menggunakan kode ini. Jika masalah kecil ditemukan selama inspeksi, Anda dapat mengambil tindakan untuk menghilangkannya. Jika cek menunjukkan masalah serius, mungkin lebih baik tidak menggunakan paket: Anda mungkin menemukan yang lebih cocok, atau mungkin Anda perlu mengembangkannya sendiri. Ingat bahwa paket sumber terbuka diterbitkan oleh penulis dengan harapan bahwa mereka akan berguna, tetapi tanpa jaminan kegunaan atau dukungan. Jika terjadi kegagalan produksi, terserah Anda untuk men-debug-nya. Seperti yang diperingatkan oleh
Lisensi Publik Umum GNU yang pertama, “semua risiko yang terkait dengan kualitas dan kinerja program berada di tangan Anda. Jika program ternyata rusak, Anda menanggung biaya semua perawatan, perbaikan, atau koreksi yang diperlukan. "
Selanjutnya, kami menguraikan beberapa pertimbangan untuk memeriksa paket dan memutuskan apakah akan bergantung padanya.
Desain
Apakah dokumentasi paket jelas? Apakah API memiliki desain yang jelas? Jika penulis dapat menjelaskan API dan desain dengan baik kepada seseorang, maka ini meningkatkan kemungkinan bahwa mereka juga menjelaskan dengan baik implementasi komputer dalam kode sumber. Menulis kode untuk API yang jelas dan dirancang dengan baik lebih sederhana, lebih cepat, dan mungkin lebih sedikit kesalahan. Sudahkah penulis mendokumentasikan apa yang mereka harapkan dari kode klien agar kompatibel dengan pembaruan di masa mendatang? (Contohnya termasuk dokumen kompatibilitas
C ++ dan
Go ).
Kualitas kode
Apakah kodenya ditulis dengan baik? Baca beberapa cuplikan. Apakah penulis tampaknya berhati-hati, teliti, dan konsisten? Apakah terlihat seperti kode yang ingin Anda debug? Anda mungkin harus melakukan ini.
Kembangkan cara sistematis Anda sendiri untuk memverifikasi kualitas kode. Sesuatu yang sederhana, seperti kompilasi dalam C atau C ++ dengan peringatan kompiler yang dinyalakan (misalnya,
-Wall
), dapat memberikan gambaran tentang seberapa serius pengembang bekerja untuk menghindari berbagai perilaku yang tidak terdefinisi. Bahasa terbaru, seperti Go, Rust, dan Swift, menggunakan kata kunci yang
unsafe
untuk menunjukkan kode yang melanggar sistem tipe; lihat berapa banyak kode tidak aman yang ada. Alat semantik yang lebih canggih seperti
Infer atau
SpotBugs juga berguna. Linter kurang bermanfaat: Anda harus mengabaikan tips standar tentang topik seperti gaya kurung dan fokus pada masalah semantik.
Jangan lupa tentang metode pengembangan yang mungkin tidak Anda kenal. Misalnya, pustaka SQLite datang sebagai file tunggal dengan 200.000 kode dan header 11.000 baris - sebagai hasil penggabungan banyak file. Ukuran sangat besar dari file-file ini segera menaikkan bendera merah, tetapi penelitian yang lebih menyeluruh akan mengarah pada kode sumber aktual untuk pengembangan: pohon file tradisional dengan lebih dari seratus file sumber C, tes dan skrip dukungan. Ternyata distribusi file tunggal dibangun secara otomatis dari sumber asli: ini lebih mudah bagi pengguna akhir, terutama mereka yang tidak memiliki manajer dependensi. (Kode yang dikompilasi juga bekerja lebih cepat karena kompiler melihat lebih banyak opsi optimisasi).
Pengujian
Apakah ada tes dalam kode? Bisakah kamu mengendalikan mereka? Apakah mereka lulus? Pengujian menetapkan bahwa fungsi utama kode sudah benar, dan menandakan bahwa pengembang serius berusaha untuk mempertahankannya. Sebagai contoh, pohon pengembangan SQLite berisi suite uji yang sangat rinci dengan lebih dari 30.000 kasus uji individual. Ada
dokumentasi untuk pengembang yang menjelaskan strategi pengujian. Di sisi lain, jika ada sedikit atau tidak ada tes sama sekali, atau jika tes gagal, ini adalah tanda bahaya yang serius: perubahan di masa depan dalam paket cenderung mengarah pada regresi yang dapat dengan mudah dideteksi. Jika Anda bersikeras melakukan tes dalam kode Anda (kanan?), Anda harus memberikan tes untuk kode yang Anda sampaikan kepada orang lain.
Dengan asumsi tes ada, jalankan, dan lulus, Anda dapat mengumpulkan informasi tambahan dengan menjalankan alat untuk menganalisis cakupan kode,
mendeteksi kondisi balapan , memeriksa alokasi memori, dan mendeteksi kebocoran memori.
Debugging
Temukan pelacak bug untuk paket ini. Apakah ada banyak pesan kesalahan terbuka? Sudah berapa lama mereka buka? Berapa banyak bug yang diperbaiki? Apakah ada bug yang diperbaiki baru-baru ini? Jika ada banyak pertanyaan terbuka tentang bug nyata, terutama yang tidak ditutup untuk waktu yang lama, ini pertanda buruk. Di sisi lain, jika kesalahan jarang terjadi dan cepat diperbaiki, itu bagus.
Dukungan
Lihatlah sejarah komitmen. Berapa lama kode dipelihara secara aktif? Apakah sekarang didukung secara aktif? Paket-paket yang telah didukung secara aktif untuk jangka waktu yang lama kemungkinan akan terus didukung. Berapa banyak orang yang mengerjakan paket ini? Banyak paket adalah proyek pribadi yang dibuat pengembang untuk hiburan di waktu luang mereka. Lainnya adalah hasil dari ribuan jam kerja untuk sekelompok pengembang berbayar. Secara umum, paket dari tipe kedua biasanya memperbaiki kesalahan lebih cepat, terus memperkenalkan fungsi baru, dan secara umum mereka didukung lebih baik.
Di sisi lain, beberapa kode benar-benar "sempurna". Misalnya,
escape-string-regexp
dari NPM mungkin tidak perlu diubah lagi.
Gunakan
Berapa banyak paket bergantung pada kode ini? Manajer paket sering memberikan statistik seperti itu, atau Anda dapat melihat di internet seberapa sering pengembang lain menyebutkan paket ini. Semakin banyak pengguna berarti setidaknya fakta bahwa banyak kode berfungsi dengan baik, dan kesalahan di dalamnya akan diketahui lebih cepat. Penggunaan yang meluas juga merupakan jaminan parsial atas layanan yang berkelanjutan: jika paket yang banyak digunakan kehilangan pengelolanya, maka sangat mungkin bahwa pengguna yang tertarik akan mengambil perannya.
Misalnya, perpustakaan seperti PCRE, Boost, atau JUnit sangat banyak digunakan. Ini membuatnya lebih mungkin - walaupun tentu saja tidak menjamin - bahwa kesalahan yang mungkin Anda temui sudah diperbaiki karena orang lain menemukannya sebelum Anda.
Keamanan
Apakah paket ini bekerja dengan input yang tidak aman? Jika ya, seberapa resistensinya terhadap data berbahaya? Apakah dia memiliki bug yang disebutkan dalam
National Vulnerability Database (NVD) ?
Sebagai contoh, ketika pada tahun 2006 Jeff Dean dan saya mulai bekerja pada
Pencarian Kode Google (
grep
untuk basis kode publik), pustaka ekspresi reguler populer PCRE tampaknya menjadi pilihan yang jelas. Namun, dalam percakapan dengan tim keamanan Google, kami mengetahui bahwa PCRE memiliki sejarah panjang masalah, seperti buffer overflows, terutama di parser. Kami sendiri yakin akan hal ini dengan mencari PCRE di NVD. Penemuan ini tidak segera membuat kami meninggalkan PCRE, tetapi membuat kami berpikir lebih hati-hati tentang pengujian dan isolasi.
Perizinan
Apakah kode dilisensikan dengan benar? Apakah dia bahkan memiliki lisensi? Apakah lisensi dapat diterima untuk proyek atau perusahaan Anda? Bagian luar biasa dari proyek GitHub tidak memiliki lisensi yang jelas. Proyek atau perusahaan Anda dapat menempatkan batasan tambahan pada lisensi ketergantungan. Misalnya, Google
melarang penggunaan kode di bawah lisensi seperti AGPL (terlalu ketat) dan ketik WTFPL (terlalu kabur).
Ketergantungan
Apakah paket ini memiliki dependensinya sendiri? Kekurangan dalam ketergantungan tidak langsung sama berbahayanya dengan kerugian dalam ketergantungan langsung. Manajer paket dapat mendaftar semua dependensi transitif dari paket yang diberikan, dan masing-masing dari mereka idealnya harus diperiksa seperti yang dijelaskan dalam bagian ini. Paket dengan banyak dependensi akan membutuhkan banyak pekerjaan.
Banyak pengembang tidak pernah melihat daftar lengkap dependensi transitif kode mereka dan tidak tahu apa yang mereka andalkan. Misalnya, pada bulan Maret 2016, komunitas pengguna NPM menemukan bahwa banyak proyek populer - termasuk Babel, Ember, dan React - secara tidak langsung bergantung pada paket kecil yang disebut
left-pad
fungsi 8-line. Mereka menemukan ini ketika penulis
left-pad
menghapus paket dari NPM,
secara tidak sengaja memecah sebagian besar rakitan pengguna Node.js. Dan
left-pad
hampir tidak istimewa dalam hal ini. Misalnya, 30% dari 750.000 paket dalam NPM bergantung - setidaknya secara tidak langsung - pada
escape-string-regexp
. Mengadaptasi pengamatan Leslie Lamport dari sistem terdistribusi, manajer paket dengan mudah menciptakan situasi di mana kegagalan paket, keberadaan yang Anda bahkan tidak tahu, dapat membuat kode Anda sendiri tidak dapat digunakan.
Tes kecanduan
Proses verifikasi harus mencakup menjalankan tes paket Anda sendiri. Jika paket telah lulus tes dan Anda memutuskan untuk membuat proyek Anda bergantung padanya, langkah selanjutnya adalah menulis tes baru yang berfokus pada fungsionalitas aplikasi Anda secara khusus. Tes ini sering dimulai sebagai program yang berdiri sendiri untuk memastikan bahwa Anda dapat memahami paket API dan melakukan apa yang Anda pikirkan (jika Anda tidak dapat memahami atau tidak melakukan apa yang Anda butuhkan, segera hentikan!). Maka sepadan dengan usaha ekstra untuk mengubah program-program ini menjadi pengujian otomatis yang akan berjalan dengan versi paket yang baru. Jika Anda menemukan kesalahan dan Anda memiliki potensi perbaikan, Anda dapat dengan mudah memulai kembali tes ini untuk proyek tertentu dan memastikan bahwa perbaikan itu tidak merusak apa pun.
Perhatian khusus harus diberikan pada area masalah yang diidentifikasi selama tinjauan baseline. Untuk Pencarian Kode, dari pengalaman sebelumnya, kami tahu bahwa PCRE terkadang membutuhkan waktu lama untuk mengeksekusi ekspresi reguler tertentu. Rencana awal kami adalah membuat kumpulan utas terpisah untuk ekspresi reguler "sederhana" dan "kompleks". Salah satu tes pertama adalah benchmark yang membandingkan
pcregrep
dengan beberapa implementasi
grep
lainnya. Ketika kami menemukan bahwa
pcregrep
adalah 70 kali lebih lambat daripada
grep
tercepat untuk satu kasus uji dasar, kami mulai memikirkan kembali rencana kami untuk menggunakan PCRE. Terlepas dari kenyataan bahwa kami akhirnya benar-benar meninggalkan PCRE, tes ini tetap menjadi basis kode kami hari ini.
Ketergantungan Abstraksi
Ketergantungan paket adalah solusi yang dapat Anda pilih di masa mendatang. Mungkin pembaruan akan membawa paket ke arah yang baru. Masalah keamanan serius dapat ditemukan. Mungkin opsi terbaik akan muncul. Karena semua alasan ini, ada baiknya upaya menyederhanakan migrasi proyek ke ketergantungan baru.
Jika suatu paket dipanggil dari banyak tempat dalam kode sumber proyek, Anda perlu melakukan perubahan pada semua tempat yang berbeda ini untuk beralih ke ketergantungan baru. Lebih buruk lagi, jika paket disajikan dalam API proyek Anda sendiri, maka bermigrasi ke dependensi baru akan memerlukan membuat perubahan pada semua kode yang memanggil API Anda, dan ini mungkin sudah di luar kendali Anda. Untuk menghindari biaya seperti itu, masuk akal untuk mendefinisikan antarmuka Anda sendiri bersama dengan pembungkus tipis yang mengimplementasikan antarmuka ini menggunakan dependensi. Harap dicatat bahwa pembungkus harus mencakup hanya apa yang dibutuhkan proyek dari ketergantungan, dan bukan semua yang ditawarkan ketergantungan. Idealnya, ini memungkinkan Anda untuk mengganti dependensi lain yang sama-sama cocok, hanya mengubah pembungkusnya.Migrasi tes untuk setiap proyek untuk menggunakan antarmuka baru memeriksa implementasi antarmuka dan pembungkus, dan juga menyederhanakan pengujian setiap penggantian potensial untuk ketergantungan.Untuk Pencarian Kode, kami telah mengembangkan kelas abstrak Regexp
yang mendefinisikan antarmuka Pencarian Kode yang diperlukan dari mesin ekspresi reguler apa pun. Kemudian mereka menulis bungkus tipis di sekitar PCRE yang mengimplementasikan antarmuka ini. Metode ini membuatnya lebih mudah untuk menguji perpustakaan alternatif dan mencegah pengenalan secara tidak sengaja pengetahuan tentang komponen PCRE internal ke seluruh pohon sumber. Ini, pada gilirannya, memastikan bahwa jika perlu akan mudah untuk beralih ke ketergantungan lainnya.Ketergantungan isolasi
Mungkin juga tepat untuk mengisolasi ketergantungan pada waktu berjalan untuk membatasi kemungkinan kerusakan yang disebabkan oleh kesalahan di dalamnya. Misalnya, Google Chrome memungkinkan pengguna untuk menambahkan dependensi ke kode ekstensi browser. Ketika Chrome pertama kali diluncurkan pada 2008, ia memperkenalkan fungsi kritis (sekarang menjadi standar pada semua browser) untuk mengisolasi setiap ekstensi dalam kotak pasir yang berjalan dalam proses terpisah dari sistem operasi. Eksploitasi potensial dalam ekstensi yang ditulis dengan buruk tidak memiliki akses otomatis ke semua memori browser itu sendiridan tidak dapat melakukan panggilan sistem yang tidak pantas. Untuk Pencarian Kode, sampai kami benar-benar menjatuhkan PCRE, rencananya adalah untuk setidaknya mengisolasi parser PCRE di kotak pasir yang sama. Saat ini, opsi lain adalah kotak pasir berbasis-hypervisor, seperti gVisor . Ketergantungan isolasi mengurangi risiko terkait dari mengeksekusi kode ini.Bahkan dengan contoh-contoh ini dan opsi siap pakai lainnya, mengisolasi kode yang mencurigakan saat runtime masih terlalu rumit dan jarang dilakukan. Isolasi sejati akan membutuhkan bahasa yang sepenuhnya aman-memori, tanpa menabrak kode yang tidak diketik. Ini rumit tidak hanya dalam bahasa yang sepenuhnya tidak aman, seperti C dan C ++, tetapi juga dalam bahasa yang menyediakan pembatasan operasi yang tidak aman, seperti Java saat JNI dihidupkan, atau seperti Go, Rust, dan Swift ketika Anda mengaktifkan fungsi tidak aman Anda. Bahkan dalam bahasa yang aman-memori seperti JavaScript, kode sering kali memiliki akses lebih dari yang dibutuhkannya. Pada November 2018, ternyata versi terbaru dari paket npm event-stream
(API streaming fungsional untuk acara JavaScript) berisi kode berbahaya yang membingungkanditambahkan dua setengah bulan lalu. Kode tersebut mengumpulkan dompet bitcoin dari pengguna aplikasi seluler Copay, memperoleh akses ke sumber daya sistem yang sama sekali tidak terkait dengan pemrosesan arus acara. Salah satu dari banyak cara yang mungkin untuk melindungi dari masalah-masalah seperti ini adalah isolasi ketergantungan yang lebih baik.Pengabaian kecanduan
Jika kecanduan tampaknya terlalu berisiko dan Anda tidak dapat mengisolasinya, opsi terbaik adalah meninggalkannya sepenuhnya, atau setidaknya untuk mengecualikan bagian yang paling bermasalah.Misalnya, ketika kami lebih memahami risiko PCRE, rencana kami untuk Pencarian Kode Google berubah dari "menggunakan perpustakaan PCRE secara langsung" menjadi "menggunakan PCRE, tetapi menempatkan parser di kotak pasir", lalu menjadi "menulis parser ekspresi reguler baru, tetapi simpan mesin PCRE", kemudian di "tulis parser baru dan hubungkan ke mesin open source lain yang lebih efisien." Kemudian, Jeff Dean dan saya menulis ulang mesin juga, jadi tidak ada ketergantungan yang tersisa, dan kami menemukan hasilnya: RE2 .Jika Anda hanya memerlukan sebagian kecil dari ketergantungan, cara termudah adalah membuat salinan dari apa yang Anda butuhkan (tentu saja, menjaga hak cipta dan pemberitahuan hukum lainnya yang relevan). Anda bertanggung jawab atas koreksi kesalahan, pemeliharaan, dll., Tetapi Anda juga sepenuhnya terisolasi dari risiko yang lebih besar. Ada pepatah di komunitas pengembang Go : "Sedikit penyalinan lebih baik daripada sedikit ketergantungan."Pembaruan Ketergantungan
Untuk waktu yang lama, kebijakan perangkat lunak yang diterima secara umum adalah: "Jika berhasil, jangan sentuh apa pun." Memperbarui membawa risiko kesalahan baru; tanpa hadiah - jika Anda tidak memerlukan fitur baru, mengapa mengambil risiko? Pendekatan ini mengabaikan dua aspek. Pertama, biaya upgrade bertahap. Dalam perangkat lunak, kompleksitas membuat perubahan pada kode tidak berskala linear: sepuluh perubahan kecil kurang bekerja dan lebih mudah daripada satu perubahan besar yang sesuai. Kedua, kesulitan mendeteksi kesalahan yang sudah diperbaiki. Terutama dalam konteks keamanan, di mana kesalahan yang diketahui dieksploitasi secara aktif, setiap hari tanpa memperbaruinya meningkatkan risiko penyerang dapat memanfaatkan bug dalam kode lama.Misalnya, pertimbangkan kisah Equifax 2017, yang diceritakan para eksekutif secara terperinci dalam kesaksian di depan Kongres. Pada 7 Maret, kerentanan baru ditemukan di Apache Struts dan versi yang ditambal dirilis. Pada 8 Maret, Equifax menerima pemberitahuan US-CERT tentang perlunya memperbarui setiap penggunaan Apache Struts. Equifax meluncurkan pemindaian kode sumber dan jaringan pada 9 dan 15 Maret, masing-masing; tidak satu pun pemindaian yang ditemukan server web rentan terbuka di Internet. Pada 13 Mei, penyerang menemukan server semacam itu yang tidak ditemukan oleh ahli Equifax. Mereka menggunakan kerentanan Apache Struts untuk meretas jaringan Equifax dan mencuri informasi pribadi dan finansial terperinci tentang 148 juta orang selama dua bulan ke depan. Akhirnya, pada 29 Juli, Equifax memperhatikan peretasan dan mengumumkannya secara terbuka pada 4 September. Pada akhir September, CEO Equifax, serta CIO dan CSO, telah mengundurkan diri, dan penyelidikan telah dimulai di Kongres.Pengalaman Equifax mengarah pada fakta bahwa meskipun manajer paket mengetahui versi yang mereka gunakan selama pembuatan, Anda memerlukan mekanisme lain untuk melacak informasi ini selama penyebaran dalam produksi. Untuk bahasa Go, kami bereksperimen dengan secara otomatis memasukkan manifes manifes di setiap biner sehingga proses penyebaran dapat memindai binari untuk dependensi yang memerlukan pembaruan. Go juga membuat informasi ini tersedia pada saat dijalankan, sehingga server dapat mengakses database dari kesalahan yang diketahui dan secara independen melapor ke sistem pemantauan ketika mereka perlu diperbarui.Pembaruan cepat penting, tetapi memperbarui berarti menambahkan kode baru ke proyek, yang berarti memperbarui penilaian risiko penggunaan dependensi berdasarkan versi baru. Minimal, Anda ingin melihat perbedaan yang menunjukkan perubahan yang dilakukan dari versi saat ini ke versi yang diperbarui, atau setidaknya membaca catatan rilis untuk mengidentifikasi area masalah yang paling mungkin dalam kode yang diperbarui. Jika banyak perubahan kode, sehingga perbedaannya sulit dipahami, ini juga informasi yang dapat Anda sertakan dalam memperbarui penilaian risiko Anda.Selain itu, Anda harus menjalankan kembali tes yang ditulis khusus untuk proyek untuk memastikan bahwa paket yang diperbarui setidaknya cocok untuk proyek seperti versi sebelumnya. Masuk akal untuk menjalankan kembali tes paket Anda sendiri. Jika paket memiliki dependensi sendiri, ada kemungkinan bahwa konfigurasi proyek menggunakan versi lain dari dependensi ini (lebih tua atau lebih baru) daripada yang digunakan oleh penulis paket. Menjalankan tes paket Anda sendiri memungkinkan Anda untuk dengan cepat mengidentifikasi masalah khusus untuk konfigurasi.Sekali lagi, pembaruan tidak harus sepenuhnya otomatis. Sebelum menggunakan versi yang diperbarui , pastikan itu sesuai untuk lingkungan Anda .Jika proses pembaruan melibatkan pengeksekusian ulang pengujian integrasi dan kualifikasi yang telah ditulis, maka dalam kebanyakan kasus keterlambatan memperbarui lebih berisiko daripada pembaruan cepat.Jendela untuk pembaruan keamanan penting sangat kecil. Setelah Equifax diretas, tim keamanan forensik menemukan bukti bahwa penyerang (mungkin berbeda) berhasil mengeksploitasi kerentanan Apache Struts pada server yang terpengaruh pada 10 Maret, hanya tiga hari setelah diungkapkan kepada publik. Tetapi mereka hanya meluncurkan satu tim di sana whoami
.Awasi kecanduan Anda
Bahkan setelah semua ini, pekerjaannya belum selesai. Penting untuk terus memantau dependensi dan, dalam beberapa kasus, bahkan mengabaikannya.Pertama, pastikan Anda terus menggunakan versi paket tertentu. Sebagian besar manajer paket sekarang memungkinkan Anda untuk dengan mudah atau bahkan secara otomatis merekam hash kriptografi dari kode sumber yang diharapkan untuk versi paket yang diberikan, dan kemudian memeriksa hash ini ketika Anda mengunduh ulang paket ke komputer lain atau dalam lingkungan pengujian. Ini memastikan bahwa build akan menggunakan kode sumber ketergantungan yang sama yang Anda uji dan uji. Pemeriksaan semacam itu mencegah penyerangevent-stream
, secara otomatis menyuntikkan kode berbahaya ke versi 3.3.5 yang sudah dirilis. Sebaliknya, penyerang harus membuat versi baru 3.3.6 dan menunggu orang untuk memperbarui (tanpa melihat perubahannya dengan hati-hati).Penting juga untuk memantau munculnya dependensi tidak langsung baru: pembaruan dapat dengan mudah memperkenalkan paket-paket baru, di mana kesuksesan proyek Anda sekarang tergantung. Mereka juga layak mendapatkan perhatian Anda. Dalam kasus ini, event-stream
kode jahat disembunyikan di paket lain flatMap-stream
, yang event-stream
ditambahkan sebagai ketergantungan baru di versi baru .Ketergantungan merayap juga dapat mempengaruhi ukuran proyek. Selama pengembangan Google Sawzall- Bahasa pemrosesan log JIT - pada waktu yang berbeda, penulis menemukan bahwa biner interpreter utama tidak hanya berisi JIT Sawzall, tetapi juga (tidak digunakan) PostScript, Python, dan penerjemah JavaScript. Setiap kali, pelakunya ternyata dependensi yang tidak digunakan dinyatakan oleh beberapa perpustakaan Sawzall, dikombinasikan dengan fakta bahwa sistem build Google sepenuhnya secara otomatis menggunakan dependensi baru. Inilah sebabnya mengapa kompiler Go melontarkan kesalahan saat mengimpor paket yang tidak digunakan.Memperbarui adalah waktu alami untuk merevisi keputusan Anda untuk menggunakan ketergantungan yang berubah. Penting juga untuk meninjau secara berkala setiap kecanduan yang tidaksedang berubah. Apakah masuk akal bahwa tidak ada masalah keamanan atau kesalahan lain untuk diperbaiki? Apakah proyek itu ditinggalkan? Mungkin sudah waktunya merencanakan penggantian untuk ketergantungan ini.Juga penting untuk memeriksa log keamanan setiap ketergantungan. Misalnya, Apache Struts mengungkapkan kerentanan serius dalam eksekusi kode jarak jauh pada 2016, 2017, dan 2018. Bahkan jika Anda memiliki banyak server yang memulainya dan memperbaruinya dengan cepat, rekam jejak seperti itu menunjukkan apakah layak untuk digunakan sama sekali.Kesimpulan
Era penggunaan kembali perangkat lunak akhirnya telah tiba, dan saya tidak ingin mengecilkan manfaatnya: ia telah membawa transformasi yang sangat positif bagi para pengembang. Namun, kami menerima transformasi ini tanpa sepenuhnya mempertimbangkan konsekuensi potensial. Alasan sebelumnya untuk mempercayai dependensi kehilangan relevansi pada saat yang sama ketika kita memiliki lebih banyak dependensi daripada sebelumnya.Analisis kritis dependensi spesifik yang saya jelaskan dalam artikel ini mewakili sejumlah besar pekerjaan dan tetap menjadi pengecualian daripada aturan. Tetapi saya ragu bahwa ada pengembang yang benar-benar bekerja keras untuk melakukan ini untuk setiap kemungkinan kecanduan baru. Saya hanya mengerjakan sebagian dari pekerjaan ini untuk beberapa ketergantungan saya sendiri. Pada dasarnya, seluruh solusi muncul sebagai berikut: "mari kita lihat apa yang terjadi." Terlalu sering, sesuatu yang tampaknya terlalu banyak usaha.Tetapi serangan Copay dan Equifax adalah peringatan yang jelas tentang masalah nyata dalam bagaimana kita menggunakan dependensi perangkat lunak saat ini. Kita tidak boleh mengabaikan peringatan. Saya menawarkan tiga rekomendasi umum.- . , , , . , .
- . , , . , , . , , , .
- . . . , . , , . , , API. .
Ada banyak perangkat lunak yang bagus. Mari kita bekerja bersama dan mencari tahu cara menggunakannya dengan aman.