JavaRush /Blog Java /Random-MS /Keselamatan dalam Java: amalan terbaik

Keselamatan dalam Java: amalan terbaik

Diterbitkan dalam kumpulan
Dalam aplikasi pelayan, salah satu petunjuk terpenting ialah keselamatan. Ini adalah salah satu jenis Keperluan Bukan Fungsian . Keselamatan di Java: amalan terbaik - 1Keselamatan mempunyai banyak komponen. Sudah tentu, untuk merangkumi sepenuhnya semua prinsip dan tindakan perlindungan yang diketahui, anda perlu menulis lebih daripada satu artikel, jadi mari fokus pada yang paling penting. Seseorang yang mahir dalam topik ini, akan dapat menyediakan semua proses dan memastikan bahawa mereka tidak mencipta lubang keselamatan baru, akan diperlukan dalam mana-mana pasukan. Sudah tentu, anda tidak sepatutnya berfikir bahawa jika anda mengikuti amalan ini, permohonan itu akan selamat sepenuhnya. Tidak! Tetapi ia pasti akan lebih selamat dengan mereka. Pergi.

1. Menyediakan keselamatan di peringkat bahasa Java

Pertama sekali, keselamatan di Java bermula tepat pada tahap ciri bahasa. Inilah yang akan kami lakukan jika tiada pengubah akses?... Anarki, tidak kurang. Bahasa pengaturcaraan membantu kami menulis kod selamat dan juga memanfaatkan banyak ciri keselamatan tersirat:
  1. Kuat menaip. Java ialah bahasa bertaip statik yang menyediakan keupayaan untuk mengesan ralat taip pada masa jalankan.
  2. Pengubah suai akses. Terima kasih kepada mereka, kami boleh mengkonfigurasi akses kepada kelas, kaedah dan medan kelas mengikut cara yang kami perlukan.
  3. Pengurusan memori automatik. Untuk tujuan ini, kami (Javaists ;)) mempunyai Garbage Collector, yang membebaskan anda daripada konfigurasi manual. Ya, kadang-kadang masalah timbul.
  4. Pemeriksaan Bytecode: Java menyusun kepada bytecode, yang disemak mengikut masa jalan sebelum menjalankannya.
Antara lain, terdapat cadangan daripada Oracle mengenai keselamatan. Sudah tentu, ia tidak ditulis dalam "gaya tinggi" dan anda mungkin tertidur beberapa kali semasa membacanya, tetapi ia berbaloi. Dokumen yang sangat penting ialah Garis Panduan Pengekodan Selamat untuk Java SE , yang menyediakan petua tentang cara menulis kod selamat. Dokumen ini mengandungi banyak maklumat berguna. Kalau boleh, pasti berbaloi untuk dibaca. Untuk mencetuskan minat terhadap bahan ini, berikut adalah beberapa petua menarik:
  1. Elakkan daripada membuat siri kelas sensitif selamat. Dalam kes ini, anda boleh mendapatkan antara muka kelas daripada fail bersiri, apatah lagi data yang sedang bersiri.
  2. Cuba elakkan kelas data boleh ubah. Ini memberikan semua faedah kelas tidak berubah (mis. keselamatan benang). Jika terdapat objek boleh ubah, ini mungkin 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 menukar keadaan dalaman objek.
  4. Dan sebagainya…
Secara umum, Garis Panduan Pengekodan Selamat untuk Java SE mengandungi set petua dan helah tentang cara menulis kod dalam Java dengan betul dan selamat.

2. Menghapuskan kelemahan suntikan SQL

Kerentanan yang unik. Keunikannya terletak pada fakta bahawa ia adalah salah satu yang paling terkenal dan salah satu kelemahan yang paling biasa. Jika anda tidak berminat dengan isu keselamatan, maka anda tidak akan tahu mengenainya. Apakah suntikan SQL? Ini adalah serangan ke atas pangkalan data dengan menyuntik kod SQL tambahan di mana ia tidak dijangka. Katakan kita mempunyai kaedah yang mengambil beberapa parameter untuk menanyakan pangkalan data. Sebagai contoh, nama pengguna. Kod dengan kelemahan akan kelihatan seperti ini:
// Метод достает из базы данных всех пользователей с определенным именем
public List<User> findByFirstName(String firstName) throws SQLException {
   // Создается связь с базой данных
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Пишем sql request в базу данных с нашим firstName
   String query = "SELECT * FROM USERS WHERE firstName = " + firstName;

   // выполняем request
   Statement statement = connection.createStatement();
   ResultSet result = statement.executeQuery(query);

   // при помощи mapToUsers переводит ResultSet в коллекцию юзеров.
   return mapToUsers(result);
}

private List<User> mapToUsers(ResultSet resultSet) {
   //переводит в коллекцию юзеров
}
Dalam contoh ini, pertanyaan sql disediakan terlebih dahulu dalam baris yang berasingan. Nampaknya apa masalahnya, bukan? Mungkin masalahnya ialah lebih baik digunakan String.format? Tidak? Selepas itu, apa? Mari letakkan diri kita di tempat penguji dan fikirkan apa yang boleh disampaikan dalam nilai firstName. Sebagai contoh:
  1. Anda boleh lulus apa yang diharapkan - nama pengguna. Kemudian pangkalan data akan mengembalikan semua pengguna dengan nama itu.
  2. Anda boleh menghantar rentetan kosong: kemudian semua pengguna akan dikembalikan.
  3. Atau anda boleh lulus yang berikut: “''; DROP MEJA PENGGUNA;”. Dan di sini akan ada masalah yang lebih besar. Pertanyaan ini akan mengalih keluar jadual daripada pangkalan data. Dengan semua data. SEMUA ORANG.
Bolehkah anda bayangkan apakah masalah ini boleh menyebabkan? Kemudian anda boleh menulis apa sahaja yang anda mahu. Anda boleh menukar nama semua pengguna, anda boleh memadamkan alamat mereka. Skop untuk sabotaj adalah luas. Untuk mengelakkan ini, anda perlu berhenti menyuntik pertanyaan siap dan sebaliknya membinanya menggunakan parameter. Ini sepatutnya menjadi satu-satunya cara untuk menanyakan pangkalan data. Dengan cara ini anda boleh menghapuskan kelemahan ini. Contoh:
// Метод достает из базы данных всех пользователей с определенным именем
public List<User> findByFirstName(String firstName) throws SQLException {
   // Создается связь с базой данных
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Создаем параметризированный request.
   String query = "SELECT * FROM USERS WHERE firstName = ?";

   // Создаем подготовленный стейтмент с параметризованным requestом
   PreparedStatement statement = connection.prepareStatement(query);

   // Передаем meaning параметра
   statement.setString(1, firstName);

   // выполняем request
   ResultSet result = statement.executeQuery(query);

   // при помощи mapToUsers переводим ResultSet в коллекцию юзеров.
   return mapToUsers(result);
}

private List<User> mapToUsers(ResultSet resultSet) {
   //переводим в коллекцию юзеров
}
Dengan cara ini, kelemahan ini dapat dielakkan. Bagi mereka yang ingin mendalami artikel ini, berikut ialah contoh yang bagus . Bagaimana anda tahu jika anda memahami bahagian ini? Jika jenaka di bawah menjadi jelas, maka ini adalah tanda pasti bahawa intipati kelemahan itu jelas :D Keselamatan di Java: amalan terbaik - 2

3. Imbas dan pastikan kebergantungan dikemas kini

Apakah maksudnya? Bagi mereka yang tidak tahu apa itu pergantungan, saya akan menerangkan: ia adalah arkib balang 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 semasa runtime. Dan jika kita bercakap tentang aplikasi besar, mereka menggunakan banyak kebergantungan yang berbeza. Ada yang transitif (iaitu, setiap kebergantungan boleh mempunyai kebergantungan sendiri, dan sebagainya). Oleh itu, penyerang semakin memberi perhatian kepada kebergantungan sumber terbuka, kerana ia kerap digunakan dan boleh menyebabkan masalah kepada banyak pelanggan. Adalah penting untuk memastikan bahawa tiada kelemahan yang diketahui dalam keseluruhan pepohon kebergantungan (yang betul-betul rupanya). Dan terdapat beberapa cara untuk melakukan ini.

Gunakan Snyk untuk pemantauan

Alat Snyk menyemak semua kebergantungan projek dan menandai kelemahan yang diketahui. Di sana anda boleh mendaftar dan mengimport projek anda melalui GitHub, sebagai contoh. Keselamatan di Java: amalan terbaik - 3Selain itu, seperti yang anda boleh lihat daripada imej di atas, jika versi yang lebih baharu mempunyai penyelesaian kepada kelemahan ini, Snyk akan menawarkan untuk berbuat demikian dan mencipta Permintaan Tarik. Ia boleh digunakan secara percuma untuk projek sumber terbuka. Projek akan diimbas pada beberapa kekerapan: seminggu sekali, sebulan sekali. Saya mendaftar dan menambah semua repositori awam saya ke imbasan Snyk (tiada apa-apa yang berbahaya tentang ini: mereka sudah terbuka kepada semua orang). Seterusnya, Snyk 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 ini satu lagi: Keselamatan di Java: amalan terbaik - 6Jadi ini adalah alat yang sangat baik untuk mencari kelemahan dan pemantauan untuk mengemas kini versi baharu.

Gunakan GitHub Security Lab

Mereka yang bekerja di GitHub juga boleh memanfaatkan alatan terbina dalam mereka. Anda boleh membaca lebih lanjut mengenai pendekatan ini dalam terjemahan saya dari blog mereka Pengumuman Lab Keselamatan GitHub . Alat ini, sudah tentu, lebih mudah daripada Snyk, tetapi anda pasti tidak boleh mengabaikannya. Selain itu, bilangan kelemahan yang diketahui hanya akan berkembang, jadi kedua-dua Makmal Keselamatan Snyk dan GitHub akan berkembang dan bertambah baik.

Aktifkan Sonatype DepShield

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

4. Mengendalikan data sensitif dengan berhati-hati

Keselamatan di Java: amalan terbaik - 8Dalam pertuturan bahasa Inggeris, frasa "data sensitif" adalah lebih biasa. Pendedahan maklumat peribadi, nombor kad kredit dan maklumat peribadi pelanggan lain boleh menyebabkan kemudaratan yang tidak boleh diperbaiki. Pertama sekali, anda perlu melihat dengan teliti reka bentuk aplikasi dan menentukan sama ada sebarang data sebenarnya diperlukan. Mungkin tidak ada keperluan untuk sebahagian daripada mereka, tetapi mereka telah ditambah untuk masa depan yang belum datang dan tidak mungkin akan datang. Di samping itu, semasa pengelogan projek, data tersebut mungkin bocor. Cara mudah untuk menghalang data sensitif daripada masuk ke dalam log anda ialah membersihkan kaedah toString()entiti domain (seperti Pengguna, Pelajar, Guru dan sebagainya). Ini akan menghalang medan sensitif daripada dicetak secara tidak sengaja. Jika anda menggunakan Lombok untuk menjana kaedah toString(), anda boleh menggunakan anotasi @ToString.Excludeuntuk menghalang medan daripada digunakan dalam output melalui kaedah tersebut toString(). Juga, berhati-hati apabila berkongsi data dengan dunia luar. Sebagai contoh, terdapat titik akhir http yang menunjukkan nama semua pengguna. Tidak perlu menunjukkan ID unik dalaman pengguna. kenapa? Kerana menggunakannya, penyerang boleh mendapatkan maklumat lain yang lebih sulit tentang setiap pengguna. Contohnya, jika anda menggunakan Jackson untuk mensiri dan menyahsiri POJO ke dalam JSON , anda boleh menggunakan @JsonIgnoredan anotasi @JsonIgnorePropertiesuntuk menghalang medan tertentu daripada bersiri dan dinyahsiri. Secara umum, anda perlu menggunakan kelas POJO yang berbeza untuk tempat yang berbeza. Apakah maksudnya?
  1. Untuk bekerja dengan pangkalan data, gunakan hanya POJO - Entiti.
  2. Untuk bekerja dengan logik perniagaan, pindahkan Entiti ke Model.
  3. Untuk bekerja dengan dunia luar dan menghantar permintaan http, gunakan entiti ketiga - 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 pelanggan sulit mesti disimpan dengan selamat. Untuk melakukan ini, anda perlu menggunakan penyulitan. Bergantung pada tugas, anda perlu memutuskan jenis penyulitan yang hendak digunakan. Selanjutnya, 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 algoritma sendiri. Tetapi ini tidak perlu. Anda boleh memanfaatkan 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 cara menggunakannya, menggunakan contoh cara menyulitkan satu cara dan yang lain:
private static void encryptDecryptExample() {
   AeadConfig.register();
   KeysetHandle handle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_CTR_HMAC_SHA256);

   String plaintext = "Цой жив!";
   String aad = "Юрий Клинских";

   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));
}

Penyulitan kata laluan

Untuk tugas ini, adalah paling selamat untuk menggunakan penyulitan asimetri. kenapa? Kerana aplikasi itu benar-benar tidak perlu menyahsulit kata laluan kembali. Ini adalah pendekatan umum. Pada hakikatnya, apabila pengguna memasukkan kata laluan, sistem menyulitkannya dan membandingkannya dengan apa yang ada dalam peti besi kata laluan. Penyulitan dijalankan menggunakan cara yang sama, jadi anda boleh mengharapkan mereka sepadan (jika anda memasukkan kata laluan yang betul ;), sudah tentu). BCrypt dan SCrypt sesuai untuk tujuan ini. Kedua-duanya adalah fungsi sehala (cincang kriptografi) dengan algoritma kompleks pengiraan yang mengambil banyak masa. Inilah yang anda perlukan, kerana mentafsirkannya secara langsung akan mengambil masa selama-lamanya. Sebagai contoh, Spring Security menyokong pelbagai algoritma. SCryptPasswordEncoderAnda juga boleh menggunakan BCryptPasswordEncoder. Apakah algoritma penyulitan yang kuat sekarang mungkin lemah tahun depan. Akibatnya, kami membuat kesimpulan bahawa adalah perlu untuk menyemak algoritma yang digunakan dan mengemas kini perpustakaan dengan algoritma.

Daripada output

Hari ini kita bercakap tentang keselamatan dan, sudah tentu, banyak perkara yang tertinggal di belakang tabir. Saya baru sahaja membuka pintu kepada dunia baharu untuk anda: dunia yang menjalani kehidupannya sendiri. Dengan keselamatan ia adalah sama seperti dengan politik: jika anda tidak terlibat dalam politik, politik akan melibatkan diri anda. Secara tradisinya, saya cadangkan melanggan akaun Github saya . Di sana saya menyiarkan kerja saya mengenai pelbagai teknologi yang saya pelajari dan gunakan di tempat kerja.

pautan yang berguna

Ya, hampir semua artikel di laman web ini ditulis dalam bahasa Inggeris. Sama ada kita suka atau tidak, bahasa Inggeris adalah bahasa untuk pengaturcara berkomunikasi. Semua artikel, buku dan majalah terbaru tentang pengaturcaraan ditulis dalam bahasa Inggeris. Itulah sebabnya pautan saya kepada pengesyoran kebanyakannya dalam bahasa Inggeris:
  1. Habr: Suntikan SQL untuk Pemula
  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. JR: Pengumuman Lab Keselamatan GitHub: melindungi semua kod anda bersama-sama
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION