Satu baris kod boleh membatalkan keseluruhan model kawalan akses anda. Masukkan request body terus ke dalam kemas kini pangkalan data dan anda telah menyerahkan pen kepada klien untuk menulis semula skema anda. Itulah mass assignment. Ia bukan pepijat eksotik atau kes terpencil. Ia adalah kegagalan reka bentuk yang muncul setiap kali API melayan sesuatu payload sebagai polisi kemas kini miliknya sendiri.
await db.users.update(req.params.id, { ...req.body });
Ia kelihatan kemas. Ia menjimatkan masa menaip. Tetapi klien mengawal kunci-kunci tersebut. Seorang penyerang boleh menambah "role": "admin", "accountId": "someone_else", atau "credit": 99999" ke dalam kemas kini profil yang sepatutnya biasa. Lapisan pengesahan (validation layer) anda mungkin menyemak sama ada nilai-nilai tersebut adalah string atau nombor dan menyatakan bahawa ia kelihatan baik. Walau bagaimanapun, kesahihan (validity) bukanlah autorisasi. Seorang pengguna mungkin secara sah memiliki rekod sasaran tersebut. Itu tidak bermakna mereka mempunyai hak untuk menyunting setiap medan di dalamnya.
Bagaimana Rupa Sebenar Mass Assignment
Bahaya tersembunyi di sebalik kemudahan. Framework dan ORM memudahkan pemetaan kunci JSON secara terus ke atas kolum pangkalan data. Apabila anda melakukan ini, anda memberitahu pangkalan data untuk mempercayai klien tentang apa yang patut diubah, bukan sekadar bagaimana ia patut diubah.
Seorang pengguna yang mengemas kini profil mereka mungkin menghantar data yang sah untuk displayName dan bio, tetapi menyelitkan role atau balance bersama-sama dengannya. Jika pengawal (controller) anda hanya memanjangkan objek tersebut, pangkalan data akan menulis semuanya. Pengesahan (validation) menangkap nilai yang salah format. Ia jarang menangkap kunci yang berniat jahat. Peraturan perniagaan yang menyatakan "pengguna ini dibenarkan untuk mengemas kini profil mereka" menjadi kebenaran menyeluruh ke atas setiap kolum dalam baris tersebut.
Penyelesaiannya bukan dengan menambah lebih banyak pengesahan. Ia adalah seni bina yang lebih ketat.
Tiga Pintu Gerbang
Satu mutasi yang selamat mesti melalui tiga semakan berasingan sebelum ia menyentuh storan.
Medan Diterima (Allowlist)
Mulakan dengan memutuskan kunci mana yang akan anda periksa. Jika sesuatu medan tidak ada dalam allowlist, tolak permintaan tersebut atau buang kunci itu. Ini mengubah postur lalai: kolum pangkalan data baharu adalah tidak boleh ditulis sehingga pembangun mendedahkannya secara eksplisit. Skema berkembang dari semasa ke semasa. Seorang rakan sepasukan menambah stripeCustomerId, departmentBudget, atau bendera isVerified. Dengan allowlist, kolum baharu tersebut secara automatik dilindungi daripada penulisan klien. Tanpa allowlist, setiap kolum baharu menjadi permukaan API yang tidak disengajakan.
Nilai Sah
Setelah anda tahu medan mana yang dibenarkan, periksa sama ada nilai tersebut masuk akal. Adakah string zon masa itu benar-benar zon masa yang diiktiraf? Adakah e-mel diformat seperti e-mel? Adakah nombor tersebut dalam julat yang munasabah? Ini adalah aspek kebersihan data. Ia menghalang sampah daripada memasuki sistem anda, tetapi ia tidak menghalang penyalahgunaan. String "admin" yang sangat sah tetap berbahaya dalam medan role jika orang yang salah menghantarnya.
Transisi Berautoriti
Ini adalah pintu gerbang yang paling kerap diabaikan oleh kebanyakan pasukan, dan di sinilah perlindungan sebenar terletak. Tanya soalan yang terperinci: adakah aktor khusus ini mempunyai kebenaran untuk mengubah medan khusus ini pada rekod khusus ini? Bukan "adakah pengguna ini admin?" Bukan "adakah pengguna mempunyai skop write:users?" Sebaliknya, "adakah pengguna ini dibenarkan untuk mengubah displayName mereka sendiri, tetapi tidak sekali-kali accountId mereka?" Autorasi setiap medan menghalang kebenaran luas seperti "Editor" atau "User" daripada menjadi kunci induk kepada setiap harta dalam baris tersebut.
Membina Fungsi Patch
Satukan ketiga-tiga pintu gerbang tersebut ke dalam satu saluran (pipeline) tunggal. Apabila permintaan patch tiba, jalankannya melalui peringkat-peringkat tersebut mengikut urutan.
Pertama, tapis input terhadap allowlist anda. Jika role bukan medan yang dibenarkan untuk endpoint ini, berhenti di situ juga. Tidak ada sebab untuk mengesahkan atau memberi autorisasi kepada nilai yang sepatutnya tidak pernah anda terima.
Kedua, sahkan nilai yang dibenarkan. Semak jenis, format, dan peraturan perniagaan. Medan lokasi mestilah string yang merujuk kepada zon masa sebenar. URL avatar mestilah URI yang sah di bawah panjang tertentu.
Ketiga, berikan autorisasi kepada tindakan tersebut. Sahkan bahawa aktor tersebut memiliki rekod sasaran, atau memegang kebenaran tepat yang diperlukan untuk medan ini. Pemilikan adalah tetapan lalai yang baik untuk data peribadi, tetapi sesetengah medan masih memerlukan pintu gerbang tambahan. Seorang pengguna mungkin memiliki profil mereka, namun hanya admin pengebilan yang sepatutnya menyentuh taxRegion.
Keempat, normalisasikan data. Buang ruang kosong (whitespace), rapatkan ruang yang berulang, tukar e-mel kepada huruf kecil, atau buang aksara kawalan. Lakukan ini selepas pengesahan tetapi sebelum storan supaya anda tidak membandingkan string yang kotor semasa semakan autorisasi.
Jika input gagal melepasi mana-mana tapisan, tolak keseluruhan mutasi tersebut. Jangan laksanakan medan yang selamat secara separa dan abaikan medan yang tidak sah secara senyap. Respons yang bercampur-campur melatih klien untuk mencuba setiap kunci yang mereka fikirkan dan melihat apa yang berjaya. Gagal secara eksplisit.
Kes-kes Pinggiran yang Benar-benar Penting
Pertahanan penetapan massa (mass assignment) bergantung sepenuhnya pada perincian yang sering terlepas pandang oleh ujian unit.
Kunci JSON pendua. Penyerang boleh menghantar muatan seperti {"role": "user", "role": "admin"}. Bergantung pada parser HTTP dan rangka kerja anda, nilai kedua mungkin menindih nilai pertama sebelum kod aplikasi anda melihat objek tersebut. Uji tingkah laku ini pada tahap parser. Jika rangka kerja anda menerima kunci terakhir secara senyap, senarai benar (allowlist) anda mungkin melihat "user" manakala pangkalan data menerima "admin".
Objek bersarang, null, dan tatasusunan. Jangan andaikan muatan adalah rata. Klien mungkin membungkus medan yang disekat di dalam objek bersarang seperti { "profile": { "role": "admin" } }. Senarai benar anda mesti melakukan rekursi jika skema anda melakukannya. Begitu juga, tentukan cara anda mengendalikan null. Adakah ia bermaksud "abaikan medan ini" atau "padam medan ini"? Dan jika tatasusunan dijangkakan, adakah pengesah anda menolak struktur yang tidak dijangka, atau adakah ia menukar objek tunggal menjadi tatasusunan dan membenarkannya masuk?
Normalisasi Unicode. Dua rentetan boleh kelihatan serupa kepada manusia walaupun merupakan jujukan bait yang berbeza. Seorang pengguna mungkin menghantar é yang telah digabungkan atau e yang dipecahkan berserta aksen gabungan. Jika semakan kebenaran anda melakukan normalisasi sekali tetapi lapisan storan anda melakukan normalisasi secara berbeza, anda boleh berakhir dengan data yang tidak konsisten atau, lebih buruk lagi, pintasan di mana pertembungan nama pengguna terlepas daripada logik anda. Lakukan normalisasi lebih awal dan lakukan secara konsisten.
Keadaan perlumbaan (Race conditions). Keputusan kebenaran bukanlah bingkai pegun. Ia berlaku pada satu titik masa. Dua permintaan boleh membaca rekod yang sama, kedua-duanya melihat bahawa pelakon dibenarkan untuk menulis, dan kedua-duanya mengeluarkan kemas kini. Dalam tempoh tersebut, keadaan atau kebenaran pelakon mungkin telah berubah. Sentiasa laksanakan kemas kini pangkalan data dengan syarat pada nombor versi atau nilai mesin keadaan. Gunakan sesuatu seperti UPDATE users SET ... WHERE id = ? AND version = 5. Jika baris tersebut telah berubah sejak anda membacanya, penulisan akan gagal. Kendalikan kegagalan tersebut dengan mencuba semula atau menolak. Ini menghalang semakan kebenaran yang lapuk daripada merosakkan data anda.
Pantau Apa yang Penting
Anda tidak boleh mengamankan apa yang anda tidak boleh lihat. Bina log audit anda berdasarkan keputusan, bukan sekadar tindakan.
Log ID pelakon dan ID sasaran. Log nama medan yang tepat yang telah diterima dan yang telah ditolak. Log versi polisi yang membuat keputusan dan keputusan akhir. Jika seorang pengguna tiba-tiba mula mendapat penolakan role dalam kemas kini profil mereka, anda mahu mengetahuinya dengan segera.
Jangan sekali-kali log token pembawa (bearer tokens). Jangan sekali-kali membuang keseluruhan badan permintaan ke dalam log anda. Jejak audit sepatutnya membantu anda menyiasat penyalahgunaan, bukannya menjadi repositori kredential dan data peribadi.
Satu-satunya Peraturan yang Anda Perlukan
Badan permintaan mencadangkan data. Ia tidak pernah menentukan autoritinya sendiri. Klien boleh meminta apa sahaja. Pelayan anda memutuskan, medan demi medan dan baris demi baris, apa yang dibenarkan untuk disimpan dalam storan kekal. Bina tampalan anda dengan memikirkan pengasingan tersebut, dan penetapan massa akan menjadi masalah yang telah anda hentikan lama sebelum ia sampai ke lapisan kebenaran anda.
