Keselamatan dan pengesah kelas

Artikel bulan ini melanjutkan perbincangan model keselamatan Java yang dimulai pada bulan Ogos "Under the Hood." Dalam artikel itu, saya memberikan gambaran umum mengenai mekanisme keselamatan yang terdapat di dalam mesin maya Java (JVM). Saya juga melihat dengan teliti satu aspek mekanisme keselamatan tersebut: ciri keselamatan terbina dalam JVM. Pada bulan September "Under the Hood," saya memeriksa seni bina pemuat kelas, aspek lain dari mekanisme keselamatan terbina dalam JVM. Bulan ini saya akan menumpukan perhatian pada strategi ketiga strategi keselamatan JVM: pengesah kelas.

Pengesahan fail kelas

Setiap mesin maya Java mempunyai pengesahan fail kelas, yang memastikan bahawa fail kelas yang dimuat mempunyai struktur dalaman yang betul. Sekiranya pengesahan fail kelas menemui masalah dengan fail kelas, ia akan memberikan pengecualian. Oleh kerana fail kelas hanyalah urutan data binari, mesin maya tidak dapat mengetahui sama ada fail kelas tertentu dihasilkan oleh penyusun Java yang baik atau oleh keropok teduh yang berusaha menjejaskan integriti mesin maya. Akibatnya, semua implementasi JVM memiliki pengesahan fail kelas yang dapat digunakan pada kelas yang tidak dipercaya, untuk memastikan kelas tersebut selamat digunakan.

Salah satu tujuan keselamatan yang dapat dicapai oleh pengesahan fail kelas adalah ketahanan program. Sekiranya penyusun buggy atau cracker yang bijak menghasilkan fail kelas yang mengandungi kaedah yang bykecodenya menyertakan arahan untuk melompati akhir kaedah, kaedah itu dapat, jika dipanggil, menyebabkan mesin maya mogok. Oleh itu, demi kekuatan, adalah mustahak bahawa mesin maya mengesahkan integriti kod byk yang diimportnya.

Walaupun pereka mesin maya Java dibenarkan untuk memutuskan kapan mesin maya mereka akan melakukan pemeriksaan ini, banyak pelaksanaan akan melakukan pemeriksaan paling banyak setelah kelas dimuat. Mesin maya seperti itu menganalisis kod byk (dan mengesahkan integriti mereka) sekali, sebelum ia dijalankan. Sebagai sebahagian daripada pengesahan kod byteknya, mesin maya Java memastikan semua arahan lompatan - misalnya, goto(lompat selalu),ifeq(melompat jika bahagian atas timbunan sifar), dan lain-lain - menyebabkan lompatan ke arahan lain yang sah dalam aliran kaedah bytecode. Akibatnya, mesin maya tidak perlu memeriksa sasaran yang sah setiap kali menghadapi arahan lompat kerana melaksanakan kod bytek. Dalam kebanyakan kes, memeriksa semua kod bytes sekali sebelum dijalankan adalah cara yang lebih cekap untuk menjamin ketahanan daripada memeriksa setiap arahan kod bytek setiap kali dijalankan.

Pengesahan fail kelas yang melakukan pemeriksaan sedini mungkin kemungkinan besar beroperasi dalam dua fasa yang berbeza. Semasa fasa satu, yang berlaku sejurus selepas kelas dimuat, pengesahan fail kelas memeriksa struktur dalaman fail kelas, termasuk mengesahkan integriti kod bytit yang terdapat di dalamnya. Semasa fasa kedua, yang berlaku semasa kod bytek dijalankan, pengesahan fail kelas mengesahkan adanya kelas, bidang, dan kaedah yang dirujuk secara simbolik.

Fasa pertama: Pemeriksaan dalaman

Semasa fasa pertama, pengesah fail kelas memeriksa segala kemungkinan untuk memeriksa fail kelas dengan melihat hanya fail kelas itu sendiri (tanpa memeriksa kelas atau antara muka lain). Fasa pertama pengesahan fail kelas memastikan fail kelas yang diimport terbentuk dengan betul, konsisten secara dalaman, mematuhi batasan bahasa pengaturcaraan Java, dan mengandungi kod byte yang akan aman untuk dijalankan oleh mesin maya Java. Sekiranya pengesah fail kelas mendapati bahawa semua ini tidak benar, ini akan menyebabkan kesalahan, dan fail kelas tidak akan pernah digunakan oleh program.

Memeriksa format dan ketekalan dalaman

Selain mengesahkan integriti kod bytek, pengesah melakukan banyak pemeriksaan untuk format fail kelas yang betul dan ketekalan dalaman semasa fasa satu. Sebagai contoh, setiap fail kelas mesti bermula dengan yang sama empat bait, bilangan ajaib: 0xCAFEBABE. Tujuan nombor sihir adalah untuk memudahkan pengurai fail mengenali jenis fail tertentu. Oleh itu, perkara pertama yang mungkin diperiksa oleh pengesahan fail kelas adalah bahawa fail yang diimport memang bermula 0xCAFEBABE.

Pengesahan fail kelas juga memeriksa untuk memastikan fail kelas tidak terpotong atau ditingkatkan dengan bait tambahan. Walaupun fail kelas yang berbeza boleh panjangnya berbeza, setiap komponen individu yang terdapat di dalam fail kelas menunjukkan panjangnya dan juga jenisnya. Pengesah boleh menggunakan jenis dan panjang komponen untuk menentukan jumlah panjang yang betul untuk setiap fail kelas individu. Dengan cara ini, ia dapat mengesahkan bahawa fail yang diimport mempunyai panjang yang sesuai dengan kandungan dalamannya.

Pengesah juga melihat komponen individu untuk memastikannya adalah contoh jenis komponennya yang terbentuk dengan baik. Sebagai contoh, deskriptor kaedah (jenis pengembalian kaedah dan bilangan dan jenis parameternya) disimpan dalam fail kelas sebagai rentetan yang mesti mematuhi tatabahasa bebas konteks tertentu. Salah satu pemeriksaan yang dilakukan oleh verifier pada komponen individu adalah memastikan setiap deskriptor kaedah adalah rentetan tatabahasa yang sesuai.

Di samping itu, pengesahan fail kelas memeriksa bahawa kelas itu sendiri mematuhi kekangan tertentu yang diletakkan di atasnya oleh spesifikasi bahasa pengaturcaraan Java. Sebagai contoh, pengesah menguatkuasakan peraturan bahawa semua kelas, kecuali kelas Object, mesti mempunyai superclass. Oleh itu, pengesahan fail kelas memeriksa pada waktu runtime beberapa peraturan bahasa Java yang seharusnya diberlakukan pada waktu kompilasi. Oleh kerana pengesah tidak mempunyai cara untuk mengetahui sama ada fail kelas dihasilkan oleh penyusun bebas bug yang baik, ia memeriksa setiap fail kelas untuk memastikan peraturannya diikuti.