CodeGym /Blog Java /rawak /Keselamatan dalam Java: amalan terbaik
John Squirrels
Tahap
San Francisco

Keselamatan dalam Java: amalan terbaik

Diterbitkan dalam kumpulan
Salah satu metrik terpenting dalam aplikasi pelayan ialah keselamatan. Ini adalah jenis keperluan tidak berfungsi . Keselamatan di Java: amalan terbaik - 1Keselamatan merangkumi banyak komponen. Sudah tentu, ia akan mengambil lebih daripada satu artikel untuk merangkumi sepenuhnya semua prinsip keselamatan dan langkah keselamatan yang diketahui, jadi kami akan memikirkan perkara yang paling penting. Seseorang yang mahir dalam topik ini boleh menyediakan semua proses yang berkaitan, mengelak daripada mencipta lubang keselamatan baharu dan akan diperlukan pada mana-mana pasukan. Sudah tentu, anda tidak sepatutnya berfikir bahawa permohonan anda akan selamat 100% jika anda mengikuti amalan ini. Tidak! Tetapi ia pasti akan lebih selamat dengan mereka. Mari pergi.

1. Menyediakan keselamatan pada tahap bahasa Jawa

Pertama sekali, keselamatan di Jawa bermula tepat pada tahap keupayaan bahasa. Apa yang akan kami lakukan jika tiada pengubah akses? Tidak akan ada apa-apa selain anarki. Bahasa pengaturcaraan membantu kami menulis kod selamat dan juga menggunakan banyak ciri keselamatan tersirat:
  1. Kuat menaip. Java ialah bahasa yang ditaip secara statik. Ini memungkinkan untuk menangkap ralat berkaitan jenis pada masa jalan.
  2. Pengubah suai akses. Ini membolehkan kami menyesuaikan akses kepada kelas, kaedah dan medan seperti yang diperlukan.
  3. Pengurusan memori automatik. Untuk ini, pembangun Java mempunyai pengumpul sampah yang membebaskan kita daripada perlu mengkonfigurasi semuanya secara manual. Ya, masalah kadang-kadang timbul.
  4. Pengesahan Bytecode : Java disusun menjadi bytecode, yang disemak oleh masa jalan sebelum ia dilaksanakan.
Selain itu, terdapat cadangan keselamatan Oracle . Sudah tentu, ia tidak ditulis dalam bahasa yang tinggi dan anda mungkin tertidur beberapa kali semasa membacanya, tetapi ia berbaloi. Khususnya, dokumen bertajuk Garis Panduan Pengekodan Selamat untuk Java SE adalah penting. Ia memberikan nasihat tentang cara menulis kod selamat. Dokumen ini menyampaikan sejumlah besar maklumat yang sangat berguna. Sekiranya anda mempunyai peluang, anda pasti perlu membacanya. Untuk menyemarakkan minat anda terhadap bahan ini, berikut adalah beberapa petua menarik:
  1. Elakkan membuat siri kelas sensitif keselamatan. Pensirian mendedahkan antara muka kelas dalam fail bersiri, apatah lagi data yang bersiri.
  2. Cuba elakkan kelas boleh ubah untuk data. Ini menyediakan semua faedah kelas tidak berubah (cth keselamatan benang). Jika anda mempunyai objek boleh ubah, ia boleh membawa kepada tingkah laku yang tidak dijangka.
  3. Buat salinan objek boleh ubah yang dikembalikan. Jika kaedah mengembalikan rujukan kepada objek boleh ubah dalaman, maka kod klien boleh mengubah keadaan dalaman objek.
  4. Dan sebagainya…
Pada asasnya, Garis Panduan Pengekodan Selamat untuk Java SE ialah koleksi petua dan helah tentang cara menulis kod Java dengan betul dan selamat.

2. Menghapuskan kelemahan suntikan SQL

Ini adalah jenis kerentanan yang istimewa. Ia istimewa kerana ia adalah salah satu yang paling terkenal dan salah satu kelemahan yang paling biasa. Jika anda tidak pernah berminat dengan keselamatan komputer, maka anda tidak akan tahu mengenainya. Apakah suntikan SQL? Ini adalah serangan pangkalan data yang melibatkan suntikan kod SQL tambahan di mana ia tidak dijangka. Katakan kita mempunyai kaedah yang menerima beberapa jenis parameter untuk menanyakan pangkalan data. Sebagai contoh, nama pengguna. Kod terdedah akan kelihatan seperti ini:

// This method retrieves from the database all users with a certain name
public List findByFirstName(String firstName) throws SQLException {
   // Connect to the database
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);
  
   // Compose a SQL database query with our firstName
   String query = "SELECT * FROM USERS WHERE firstName = " + firstName;
  
   // Execute the query
   Statement statement = connection.createStatement();
   ResultSet result = statement.executeQuery(query);

   // Use mapToUsers to convert the ResultSet into a collection of users.
   return mapToUsers(result);
}

private List mapToUsers(ResultSet resultSet) {
   // Converts to a collection of users
}
Dalam contoh ini, pertanyaan SQL disediakan terlebih dahulu pada baris yang berasingan. Jadi apa masalahnya, kan? Mungkin masalahnya ialah lebih baik menggunakan String.format ? Tidak? Nah, kemudian apa? Mari letakkan diri kita dalam kedudukan penguji dan fikirkan tentang perkara yang boleh diluluskan sebagai nilai firstName . Sebagai contoh:
  1. Kita boleh lulus apa yang diharapkan — nama pengguna. Kemudian pangkalan data akan mengembalikan semua pengguna dengan nama itu.
  2. Kita boleh lulus rentetan kosong. Kemudian semua pengguna akan dikembalikan.
  3. Tetapi kita juga boleh lulus yang berikut: "'; DROP TABLE USERS;". Dan di sini kita sekarang mempunyai masalah huuuuuuge. Pertanyaan ini akan memadamkan jadual daripada pangkalan data. Bersama dengan semua data. SEMUANYA.
Bolehkah anda bayangkan masalah ini akan menyebabkan? Selain itu, anda boleh menulis apa sahaja yang anda mahukan. Anda boleh menukar nama semua pengguna. Anda boleh memadamkan alamat mereka. Skop untuk sabotaj adalah besar. Untuk mengelakkan ini, anda perlu menghalang suntikan pertanyaan siap dan sebaliknya membentuk pertanyaan menggunakan parameter. Ini sepatutnya menjadi satu-satunya cara untuk membuat pertanyaan pangkalan data. Inilah cara anda boleh menghapuskan kelemahan ini. Sebagai contoh:

// This method retrieves from the database all users with a certain name
public List findByFirstName(String firstName) throws SQLException {
   // Connect to the database
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Create a parameterized query.
   String query = "SELECT * FROM USERS WHERE firstName = ?";

   // Create a prepared statement with the parameterized query
   PreparedStatement statement = connection.prepareStatement(query);
  
   // Pass the parameter's value
   statement.setString(1, firstName);

   // Execute the query
   ResultSet result = statement.executeQuery(query);

   // Use mapToUsers to convert the ResultSet into a collection of users.
   return mapToUsers(result);
}

private List mapToUsers(ResultSet resultSet) {
   // Converts to a collection of users
}
Dengan cara ini, kelemahan dapat dielakkan. Bagi mereka yang ingin mendalami artikel ini, berikut ialah contoh yang bagus . Bagaimanakah anda tahu apabila anda memahami kelemahan ini? Jika anda mendapat jenaka dalam komik di bawah, maka anda mungkin mempunyai pemahaman yang jelas tentang kelemahan ini :DKeselamatan di Java: amalan terbaik - 2

3. Imbas kebergantungan dan pastikan ia dikemas kini

Apakah maksudnya? Jika anda tidak tahu apa itu kebergantungan, saya akan menerangkan. Kebergantungan ialah arkib JAR dengan kod yang disambungkan kepada projek menggunakan sistem binaan automatik (Maven, Gradle, Ant) untuk menggunakan semula penyelesaian orang lain. Contohnya, Project Lombok , yang menjana getter, setter, dsb. untuk kami dalam masa jalan. Aplikasi besar boleh mempunyai banyak dan banyak kebergantungan. Ada yang transitif (iaitu, setiap kebergantungan mungkin mempunyai kebergantungan sendiri, dan seterusnya). Akibatnya, penyerang semakin memberi perhatian kepada kebergantungan sumber terbuka, kerana ia kerap digunakan dan ramai pelanggan boleh menghadapi masalah kerana mereka. Adalah penting untuk memastikan bahawa tiada kelemahan yang diketahui dalam keseluruhan pepohon pergantungan (ya, ia kelihatan seperti pokok). Terdapat beberapa cara untuk melakukan ini.

Gunakan Snyk untuk pemantauan pergantungan

Snyk menyemak semua kebergantungan projek dan menandai kelemahan yang diketahui. Anda boleh mendaftar di Snyk dan mengimport projek anda melalui GitHub. Keselamatan di Java: amalan terbaik - 3Selain itu, seperti yang anda boleh lihat daripada gambar di atas, jika kelemahan diperbaiki dalam versi yang lebih baharu, maka Snyk akan menawarkan pembetulan dan membuat permintaan tarik. Anda boleh menggunakannya secara percuma untuk projek sumber terbuka. Projek diimbas secara berkala, contohnya seminggu sekali, sebulan sekali. Saya mendaftar dan menambah semua repositori awam saya ke imbasan Snyk (tiada apa-apa yang berbahaya tentang ini, kerana ia sudah terbuka kepada semua orang). Snyk kemudian menunjukkan hasil imbasan: Keselamatan di Java: amalan terbaik - 4Dan selepas beberapa ketika, Snyk-bot menyediakan beberapa permintaan tarik dalam projek di mana kebergantungan perlu dikemas kini: Keselamatan di Java: amalan terbaik - 5Dan juga:Keselamatan di Java: amalan terbaik - 6Ini ialah alat yang hebat untuk mencari kelemahan dan memantau kemas kini untuk versi baharu.

Gunakan GitHub Security Lab

Sesiapa sahaja yang bekerja di GitHub boleh memanfaatkan alatan terbina dalamnya. Anda boleh membaca lebih lanjut mengenai pendekatan ini dalam catatan blog mereka yang bertajuk Mengumumkan Makmal Keselamatan GitHub . Alat ini, sudah tentu, lebih mudah daripada Snyk, tetapi anda pasti tidak boleh mengabaikannya. Lebih-lebih lagi, bilangan kelemahan yang diketahui hanya akan berkembang, jadi kedua-dua Makmal Keselamatan Snyk dan GitHub akan terus berkembang dan bertambah baik.

Dayakan Sonatype DepShield

Jika anda menggunakan GitHub untuk menyimpan repositori anda, anda boleh menambah Sonatype DepShield, salah satu aplikasi dalam MarketPlace, pada projek anda. Ia juga boleh digunakan untuk mengimbas projek untuk kebergantungan. Selain itu, jika ia menemui sesuatu, Isu GitHub akan dihasilkan dengan penerangan yang sesuai seperti yang ditunjukkan di bawah:Keselamatan di Java: amalan terbaik - 7

4. Mengendalikan data sulit dengan berhati-hati

Sebagai alternatif, kami mungkin menggunakan frasa "data sensitif". Membocorkan maklumat peribadi pelanggan, nombor kad kredit dan maklumat sensitif lain boleh menyebabkan kemudaratan yang tidak boleh diperbaiki. Pertama sekali, lihat dengan teliti reka bentuk aplikasi anda dan tentukan sama ada anda benar-benar memerlukan data ini atau itu. Mungkin anda sebenarnya tidak memerlukan beberapa data yang anda miliki — data yang telah ditambahkan untuk masa hadapan yang belum datang dan tidak mungkin akan datang. Selain itu, anda ramai secara tidak sengaja membocorkan data sedemikian melalui pengelogan. Cara mudah untuk menghalang data sensitif daripada memasuki log anda ialah dengan menyental kaedah toString() entiti domain (seperti Pengguna, Pelajar, Guru, dll.). Ini akan menghalang anda daripada mengeluarkan medan sulit secara tidak sengaja. Jika anda menggunakan Lombok untuk menjana toString()kaedah, anda boleh menggunakan anotasi @ToString.Exclude untuk menghalang medan daripada digunakan dalam output kaedah toString() . Juga, berhati-hati semasa menghantar data ke dunia luar. Katakan kita mempunyai titik akhir HTTP yang menunjukkan nama semua pengguna. Tidak perlu menunjukkan ID dalaman unik pengguna. kenapa? Kerana penyerang boleh menggunakannya untuk mendapatkan maklumat lain yang lebih sensitif tentang pengguna. Sebagai contoh, jika anda menggunakan Jackson untuk mensiri/menyahserialisasikan POJO kepada/daripada JSON , maka anda boleh menggunakan @JsonIgnore dan @JsonIgnorePropertiesanotasi untuk mengelakkan pensirilan/deserialisasi medan tertentu. Secara umum, anda perlu menggunakan kelas POJO yang berbeza di tempat yang berbeza. Apakah maksudnya?
  1. Apabila bekerja dengan pangkalan data, gunakan satu jenis POJO (entiti).
  2. Apabila bekerja dengan logik perniagaan, tukar entiti kepada model.
  3. Apabila bekerja dengan dunia luar dan menghantar permintaan HTTP, gunakan entiti yang berbeza (DTO).
Dengan cara ini anda boleh menentukan dengan jelas medan mana yang akan kelihatan dari luar dan yang tidak.

Gunakan penyulitan kuat dan algoritma pencincangan

Data sulit pelanggan mesti disimpan dengan selamat. Untuk melakukan ini, kita perlu menggunakan penyulitan. Bergantung pada tugas, anda perlu memutuskan jenis penyulitan yang hendak digunakan. Selain itu, penyulitan yang lebih kuat memerlukan lebih banyak masa, jadi sekali lagi anda perlu mempertimbangkan berapa banyak keperluan untuk itu membenarkan masa yang dihabiskan untuknya. Sudah tentu, anda boleh menulis sendiri algoritma penyulitan. Tetapi ini tidak perlu. Anda boleh menggunakan penyelesaian sedia ada di kawasan ini. Contohnya, Google Tink :

<!-- https://mvnrepository.com/artifact/com.google.crypto.tink/tink -->
<dependency>
   <groupid>com.google.crypto.tink</groupid>
   <artifactid>tink</artifactid>
   <version>1.3.0</version>
</dependency>
Mari lihat apa yang perlu dilakukan, menggunakan contoh ini yang melibatkan penyulitan dan penyahsulitan:

private static void encryptDecryptExample() {
   AeadConfig.register();
   KeysetHandle handle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_CTR_HMAC_SHA256);

   String plaintext = "Elvis lives!";
   String aad = "Buddy Holly";

   Aead aead = handle.getPrimitive(Aead.class);
   byte[] encrypted = aead.encrypt(plaintext.getBytes(), aad.getBytes());
   String encryptedString = Base64.getEncoder().encodeToString(encrypted);
   System.out.println(encryptedString);

   byte[] decrypted = aead.decrypt(Base64.getDecoder().decode(encrypted), aad.getBytes());
   System.out.println(new String(decrypted));
}

Menyulitkan kata laluan

Untuk tugas ini, adalah paling selamat untuk menggunakan penyulitan asimetri. kenapa? Kerana aplikasi itu tidak perlu menyahsulit kata laluan. Ini adalah pendekatan standard. Pada hakikatnya, apabila pengguna memasukkan kata laluan, sistem menyulitkannya dan membandingkannya dengan apa yang ada dalam stor kata laluan. Proses penyulitan yang sama dilakukan, jadi kita boleh menjangkakan bahawa ia akan sepadan, jika kata laluan yang betul dimasukkan, sudah tentu :) BCrypt dan SCrypt sesuai di sini. Kedua-duanya adalah fungsi sehala (cincang kriptografi) dengan algoritma kompleks pengiraan yang mengambil masa yang lama. Inilah yang kita perlukan, kerana pengiraan langsung akan mengambil masa selama-lamanya (baik, lama, lama). Spring Security menyokong pelbagai jenis algoritma. Kita boleh menggunakan SCryptPasswordEncoder dan BCryptPasswordEncoder. Apa yang pada masa ini dianggap sebagai algoritma penyulitan yang kuat mungkin dianggap lemah tahun depan. Akibatnya, kami membuat kesimpulan bahawa kami harus menyemak algoritma yang kami gunakan secara kerap dan, seperti yang diperlukan, mengemas kini perpustakaan yang mengandungi algoritma penyulitan.

Daripada kesimpulan

Hari ini kita bercakap tentang keselamatan dan, secara semulajadi, banyak perkara tertinggal di belakang tabir. Saya baru sahaja membuka pintu ke dunia baru untuk anda, dunia yang mempunyai kehidupannya sendiri. Keselamatan adalah seperti politik: jika anda tidak menyibukkan diri dengan politik, politik akan menyibukkan diri dengan anda. Saya secara tradisinya mencadangkan anda mengikuti saya di akaun GitHub . Di sana saya menyiarkan hasil ciptaan saya yang melibatkan pelbagai teknologi yang sedang saya pelajari dan aplikasikan di tempat kerja.

Pautan berguna

  1. Guru99: Tutorial Suntikan SQL
  2. Oracle: Pusat Sumber Keselamatan Java
  3. Oracle: Garis Panduan Pengekodan Selamat untuk Java SE
  4. Baeldung: Asas Keselamatan Java
  5. Sederhana: 10 petua untuk memperkasakan keselamatan Java anda
  6. Snyk: 10 amalan terbaik keselamatan Java
  7. GitHub: Mengumumkan GitHub Security Lab: melindungi kod dunia, bersama-sama
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION