Evaluasi Kode Dinamis di Java - Pintar atau Ceroboh?

30

Saya mencoba membuat kerangka kerja ACL yang fleksibel di Java untuk aplikasi saya.

Banyak kerangka kerja ACL dibangun di atas daftar putih aturan, di mana aturan ada dalam bentuk pemilik: action: resource . Sebagai contoh,

  • "JOHN bisa MELIHAT sumber daya FOOBAR-1"
  • "MARY can VIEW resource FOOBAR-1"
  • "MARY can EDIT resource FOOBAR-1"

Ini menarik karena aturan dapat dengan mudah diserialisasi / dipertahankan ke database. Tetapi aplikasi saya memiliki logika bisnis yang kompleks. Sebagai contoh,

  • "Semua pengguna di departemen 1 dengan senioritas lebih dari 5 tahun dapat MELIHAT sumber daya FOOBAR-1, jika tidak, tidak boleh"
  • "Semua pengguna di departemen 2, jika tanggalnya setelah 15/3/2016, dapat MELIHAT sumber daya FOOBAR-2, kalau tidak, tidak akan diizinkan"

Setelah dipikirkan pertama kali, akan menjadi mimpi buruk untuk menyusun skema basis data yang dapat menangani aturan yang sangat rumit seperti ini. Oleh karena itu, sepertinya saya perlu "memanggang" mereka ke dalam aplikasi yang dikompilasi, mengevaluasi mereka untuk setiap pengguna, dan kemudian menghasilkan pemilik: tindakan: aturan sumber daya sebagai hasil dari evaluasi. Saya ingin menghindari memanggang logika ke dalam aplikasi yang dikompilasi.

Jadi, saya berpikir untuk mewakili aturan dalam bentuk predikat : action: resource , di mana predikatnya adalah ekspresi boolean yang menentukan apakah pengguna diizinkan. Predikatnya akan berupa string ekspresi JavaScript yang dapat dievaluasi oleh mesin Rhino Java. Sebagai contoh,

  • return user.getDept() == 1 && user.seniority > 5;

Dengan melakukan itu, predikat dapat dengan mudah disimpan ke dalam basis data.

Apakah ini pintar ? Apakah ini ceroboh ? Apakah ini menarik perhatian ? Apakah ini rekayasa berlebihan ? Apakah ini aman (rupanya, Jawa bisa mem-sandbox mesin Rhino).

Twittopher
sumber
8
Apa manfaatnya mencoba mendorong aturan bisnis ini ke dalam basis data alih-alih memasukkan logika ke dalam aplikasi yang dikompilasi?
Winston Ewert
6
@ WinstonEWert Eksternalisasi aturan menghilangkan kebutuhan untuk mengkompilasi ulang dan mendistribusikan kembali aplikasi jika aturan diubah, ditambahkan, atau dihapus.
Twittopher
2
Pertanyaan menarik! Saya ingin melihat jawaban yang tidak terlalu fokus pada keamanan tetapi lebih pada pemeliharaan, keandalan dan aspek kemudahan penggunaan dari solusi semacam itu.
oliver
6
Ini terdengar mirip dengan aturan email Outlook yang pada dasarnya adalah mesin aturan yang dapat dikonfigurasi oleh pengguna.

Jawaban:

37

Memipis data dinamis ke juru bahasa implementasi Anda biasanya merupakan ide yang buruk, karena hal itu meningkatkan potensi korupsi data menjadi potensi pengambilalihan aplikasi jahat. Dengan kata lain, Anda akan keluar dari cara Anda untuk membuat sebuah kode injeksi kerentanan.

Masalah Anda dapat diselesaikan dengan lebih baik oleh mesin aturan atau mungkin bahasa khusus domain (DSL) . Lihatlah konsep-konsep itu, tidak perlu menemukan kembali roda.

Kilian Foth
sumber
16
Tapi bukankah JavaScript akan digunakan sebagai bahasa penulisan DSL di sini? Kami menyiapkan data yang diperlukan (hanya baca), membungkus snippet dalam suatu fungsi, dan dengan aman mengevaluasinya. Karena kode tidak dapat melakukan apa pun kecuali mengembalikan boolean, tidak akan ada peluang berbahaya di sini.
amon
6
@Twittopher Menyeret seluruh mesin JavaScript untuk mengevaluasi beberapa predikat masih tampak seperti 1) benar-benar berlebihan, 2) berisiko dan 3) rawan kesalahan bagi saya. Contoh kasus, Anda menggunakan ==bukan ===dalam contoh Anda. Apakah Anda benar-benar ingin memberikan kelengkapan turing ketika semua aturan harus selalu berakhir? Alih-alih melompat-lompat untuk memastikan semua interaksi antara Jawa dan JavaScript adalah halal, mengapa Anda tidak menulis parser dan juru bahasa sederhana seperti yang disarankan Kilian? Akan jauh lebih mudah untuk menyesuaikan dengan kebutuhan dan keamanan Anda. Gunakan ANTLR atau sesuatu.
Doval
6
@Doval Menulis DSL kecil bukan ilmu roket, dan saya bisa menyiapkan bahasa sederhana dalam 3 jam hingga 5 hari. Tapi ini sepertinya 1) benar-benar berlebihan, 2) berisiko, dan 3) rawan kesalahan bagi saya. Apakah Anda benar-benar ingin menulis seluruh bahasa mini? Bagaimana jika beberapa aturan bisnis lebih rumit dari yang diharapkan dan memerlukan bahasa berfitur lengkap? Apakah Anda menderita sindrom Not Invented Here? Alih-alih menciptakan kembali roda, mengapa Anda tidak menggunakan bahasa yang ada? Jauh lebih mudah menggunakan bahasa yang sudah teruji pertempuran.
amon
14
@amon Ketika itu terjadi, Anda menemukan mesin aturan nyata (yang bukan JavaScript) seperti kata Killian. Menyamakan risiko kedua pendekatan itu menyesatkan. Hanya diperlukan satu kelalaian untuk menghancurkan semua upaya Anda mendapatkan juru bahasa untuk bahasa yang lengkap; itu adalah proses yang subtraktif. Jauh lebih sulit untuk secara tidak sengaja membuat DSL kecil berbahaya; ini merupakan proses tambahan. Jenis kesalahan yang mungkin Anda buat adalah menafsirkan pohon sintaks secara salah, dan itu bisa diuji unit. Anda mungkin tidak akan sengaja memberi penerjemah kemampuan memformat hard drive.
Doval
4
@amon: Sekalipun cuplikan js mungkin tidak memiliki efek samping, ia dapat memilih untuk tidak mengembalikan nilai boolean:while (true) ;
Bergi
44

Saya melakukan ini, dan saya sarankan Anda tidak melakukannya.

Apa yang saya lakukan adalah menulis semua logika bisnis dalam Lua, dan menyimpan skrip Lua itu dalam database. Ketika aplikasi saya mulai itu akan memuat dan menjalankan skrip. Dengan begitu saya dapat memperbarui logika bisnis aplikasi saya tanpa mendistribusikan biner baru.

Saya selalu menemukan bahwa saya selalu perlu memperbarui biner ketika membuat perubahan. Beberapa perubahan ada di skrip Lua, tapi saya selalu punya daftar perubahan yang perlu dibuat, dan akhirnya saya hampir selalu harus membuat beberapa perubahan dalam biner dan beberapa perubahan dalam skrip Lua. Imajinasi saya bahwa saya bisa menghindari mendistribusikan binari sepanjang waktu tidak berhasil.

Apa yang saya temukan jauh lebih bermanfaat adalah memudahkan distribusi binari. Aplikasi saya secara otomatis memeriksa pembaruan pada startup, mengunduh, dan menginstal pembaruan apa pun. Pengguna saya selalu berada di binari terbaru yang saya dorong. Hampir tidak ada perbedaan antara perubahan dalam biner dan perubahan dalam skrip. Jika saya melakukannya lagi, saya akan berusaha lebih keras untuk membuat pembaruan tanpa cacat.

Winston Ewert
sumber
3

Database saya tidak akan berisi kode. Tetapi Anda dapat melakukan sesuatu yang serupa dengan memiliki database yang berisi nama fungsi dan kemudian menggunakan refleksi untuk memanggil mereka. Ketika Anda menambahkan kondisi baru, Anda harus menambahkannya ke kode dan basis data Anda, tetapi Anda dapat menggabungkan kondisi dan parameter yang diteruskan ke mereka untuk membuat evaluasi yang cukup kompleks.

Dengan kata lain, jika Anda memiliki departemen bernomor, akan mudah untuk memiliki cek UserDepartmentIs dan cek TodayIsAfter dan kemudian menggabungkannya untuk memiliki Department = 2 dan Today> 15/03/2016. Jika Anda ingin memiliki cek TodayIsBefore sehingga Anda dapat mengakhiri tanggal izin, Anda harus menulis fungsi TodayIsBefore.

Saya belum melakukan ini untuk izin pengguna, tetapi telah melakukannya untuk validasi data, tetapi harus berfungsi.

jmoreno
sumber
2

XACML adalah solusi yang benar-benar Anda cari. Ini adalah jenis mesin aturan yang berfokus pada kontrol akses saja. XACML, standar yang ditentukan oleh OASIS, mendefinisikan tiga bagian:

  • sebuah arsitektur
  • bahasa kebijakan (yang benar-benar seperti yang Anda inginkan)
  • skema permintaan / respons (bagaimana Anda meminta keputusan otorisasi).

Arsitekturnya adalah sebagai berikut:

  • Titik Keputusan Kebijakan (PDP) adalah bagian inti dari arsitektur. Ini adalah komponen yang mengevaluasi permintaan otorisasi yang masuk terhadap serangkaian kebijakan yang diketahui
  • Titik Penegakan Kebijakan (PEP) adalah bagian dari kode yang melindungi aplikasi / API / layanan Anda. PEP mencegat permintaan bisnis, membuat permintaan otorisasi XACML, mengirimkannya ke PDP, menerima respons balik, dan menegakkan keputusan di dalam respons tersebut.
  • Titik Informasi Kebijakan (PIP) adalah komponen yang dapat menghubungkan PDP ke sumber data eksternal misalnya LDAP, database atau layanan web. PIP sangat berguna ketika PEP mengirim permintaan misalnya "Bisakah Alice melihat dokumen # 12?" dan PDP memiliki kebijakan yang mengharuskan usia pengguna. PDP akan meminta PIP "beri saya usia Alice" dan kemudian akan dapat memproses kebijakan.
  • Titik Administrasi Kebijakan (PAP) adalah tempat di mana Anda mengelola seluruh solusi XACML (mendefinisikan atribut, menulis kebijakan, dan mengkonfigurasi PDP).

Bahasa Markup Kontrol Akses eXtensible - Arsitektur XACML

Seperti apa kasus penggunaan pertama Anda:

/*
 * All users in department 1 with over 5 years of seniority can VIEW resource FOOBAR-1, else not authorized
 * 
 */
 policy departmentOne{
    target clause department == 1
    apply firstApplicable
    /**
     * All users in department 1 with over 5 years of seniority can VIEW resource FOOBAR-1, else not authorized
     */
    rule allowFooBar1{
        target clause resourceId=="FOOBAR-1" and seniority>=5 and actionId=="VIEW"
        permit
    }
    rule denyOtherAccess{
        deny
    }

 }

Kasus penggunaan kedua Anda adalah:

 /*
  * "All users in department 2, if the date is after 03/15/2016, can VIEW resource FOOBAR-2, else not authorized"
  *  
  */
  policy departmentTwo{
    target clause department == 1
    apply firstApplicable
    rule allowFooBar2{
        target clause resourceId=="FOOBAR-1" and seniority>=5 and currentDate>"2016/03/15":date and actionId=="VIEW"
        permit
    }
    rule denyOtherAccess{
        deny
    }
  }

Anda dapat menggabungkan kedua kasus penggunaan menjadi satu kebijakan dengan menggunakan referensi:

  policyset global{
    apply firstApplicable
    departmentOne
    departmentTwo
  }

Dan kamu selesai!

Anda dapat membaca lebih lanjut tentang XACML dan ALFA dari:

David Brossard
sumber
0

Yang Anda inginkan di sini adalah XACML . Cukup banyak memberi Anda apa yang Anda inginkan. Anda tidak perlu harus mengimplementasikan arsitektur lengkap dengan semua peran yang benar-benar terpisah ... jika Anda hanya memiliki satu aplikasi, Anda mungkin bisa lolos dengan mengintegrasikan PDP dan PEP ke dalam aplikasi Anda dengan balana dan PIP adalah apa pun basis data pengguna Anda saat ini.

Sekarang, di mana saja di aplikasi Anda Anda perlu mengesahkan sesuatu, Anda membuat permintaan XACML yang memiliki pengguna, tindakan, dan konteksnya, dan mesin XACML akan membuat keputusan berdasarkan pada file kebijakan XACML yang Anda tulis. File-file kebijakan ini dapat disimpan dalam database atau pada filesystem, atau di mana pun Anda ingin menyimpan konfigurasi. Axiomatics memiliki alternatif yang bagus untuk representasi XACML XML yang disebut ALFA yang sedikit lebih mudah dibaca daripada XML mentah, dan plugin Eclipse untuk menghasilkan XACML XML dari kebijakan ALFA.

gregsymons
sumber
1
bagaimana ini menjawab pertanyaan yang diajukan?
nyamuk
Dia secara khusus mencoba menerapkan sistem otorisasi yang dikonfigurasi secara eksternal. XACML adalah sistem otorisasi yang siap untuk dikonfigurasikan secara eksternal yang mencakup kasus penggunaan spesifiknya dengan sangat baik. Saya akui itu mungkin bukan jawaban yang bagus untuk pertanyaan yang lebih umum tentang eksekusi kode dinamis, tetapi ini adalah solusi yang bagus untuk pertanyaan spesifiknya.
gregsymons
0

Kami melakukan ini di perusahaan saya saat ini, dan kami sangat senang dengan hasilnya.

Ekspresi kami ditulis dalam js, dan kami bahkan menggunakannya untuk membatasi hasil yang dapat diperoleh pengguna dari permintaan ElasticSearch.

Triknya adalah memastikan bahwa tersedia cukup info untuk membuat keputusan, sehingga Anda dapat benar-benar menulis perm apa pun yang Anda inginkan tanpa perubahan kode, tetapi pada saat yang sama menjaga agar tetap cepat.

Kami tidak benar-benar khawatir dengan serangan injeksi kode, karena izin ditulis oleh mereka yang tidak perlu menyerang sistem. Dan hal yang sama berlaku untuk serangan DOS seperti while(true)contohnya. Admin sistem tidak perlu melakukan itu, mereka hanya bisa menghapus izin semua orang ...

Memperbarui:

Sesuatu seperti XACML tampaknya lebih baik sebagai titik manajemen auth pusat untuk suatu organisasi. Kasus penggunaan kami sedikit berbeda karena klien kami tidak memiliki departemen TI untuk menjalankan semua itu. Kami membutuhkan sesuatu yang mandiri tetapi berusaha mempertahankan fleksibilitas sebanyak mungkin.

Adagios
sumber