CodeGym/Java Blog/Acak/Keamanan di Jawa: praktik terbaik
John Squirrels
Level 41
San Francisco

Keamanan di Jawa: praktik terbaik

Dipublikasikan di grup Acak
anggota
Salah satu metrik terpenting dalam aplikasi server adalah keamanan. Ini adalah jenis persyaratan non-fungsional . Keamanan di Jawa: praktik terbaik - 1Keamanan mencakup banyak komponen. Tentu saja, dibutuhkan lebih dari satu artikel untuk sepenuhnya mencakup semua prinsip keamanan dan tindakan keamanan yang diketahui, jadi kita akan membahas yang paling penting. Seseorang yang berpengalaman dalam topik ini dapat mengatur semua proses yang relevan, menghindari pembuatan lubang keamanan baru, dan akan dibutuhkan di tim mana pun. Tentu saja, Anda tidak boleh berpikir bahwa aplikasi Anda akan 100% aman jika mengikuti praktik ini. TIDAK! Tapi itu pasti akan lebih aman dengan mereka. Ayo pergi.

1. Memberikan keamanan pada level bahasa Java

Pertama-tama, keamanan di Java dimulai tepat di tingkat kemampuan bahasa. Apa yang akan kita lakukan jika tidak ada pengubah akses? Tidak akan ada apa-apa selain anarki. Bahasa pemrograman membantu kami menulis kode aman dan juga memanfaatkan banyak fitur keamanan implisit:
  1. Pengetikan yang kuat. Java adalah bahasa yang diketik secara statis. Ini memungkinkan untuk menangkap kesalahan terkait jenis saat runtime.
  2. Pengubah akses. Ini memungkinkan kami menyesuaikan akses ke kelas, metode, dan bidang sesuai kebutuhan.
  3. Manajemen memori otomatis. Untuk ini, pengembang Java memiliki pengumpul sampah yang membebaskan kita dari keharusan mengonfigurasi semuanya secara manual. Ya, masalah terkadang muncul.
  4. Verifikasi bytecode : Java dikompilasi menjadi bytecode, yang diperiksa oleh runtime sebelum dijalankan.
Selain itu, ada rekomendasi keamanan Oracle . Tentu saja, ini tidak ditulis dalam bahasa yang luhur dan Anda mungkin tertidur beberapa kali saat membacanya, tetapi itu sepadan. Secara khusus, dokumen berjudul Secure Coding Guidelines for Java SE itu penting. Ini memberikan saran tentang cara menulis kode aman. Dokumen ini menyampaikan sejumlah besar informasi yang sangat berguna. Jika Anda memiliki kesempatan, Anda pasti harus membacanya. Untuk membangkitkan minat Anda pada materi ini, berikut adalah beberapa tips menarik:
  1. Hindari serialisasi kelas yang peka terhadap keamanan. Serialisasi memaparkan antarmuka kelas dalam file berseri, belum lagi data yang diserialisasi.
  2. Cobalah untuk menghindari kelas yang bisa berubah untuk data. Ini memberikan semua manfaat dari kelas yang tidak dapat diubah (mis. keamanan utas). Jika Anda memiliki objek yang dapat diubah, itu dapat menyebabkan perilaku yang tidak terduga.
  3. Buat salinan dari objek yang dapat diubah yang dikembalikan. Jika suatu metode mengembalikan referensi ke objek internal yang dapat diubah, maka kode klien dapat mengubah keadaan internal objek tersebut.
  4. Dan seterusnya…
Pada dasarnya, Panduan Pengkodean Aman untuk Java SE adalah kumpulan tips dan trik tentang cara menulis kode Java dengan benar dan aman.

2. Hilangkan kerentanan injeksi SQL

Ini adalah jenis kerentanan khusus. Ini istimewa karena merupakan salah satu kerentanan yang paling terkenal dan paling umum. Jika Anda tidak pernah tertarik dengan keamanan komputer, maka Anda tidak akan mengetahuinya. Apa itu injeksi SQL? Ini adalah serangan basis data yang melibatkan penyuntikan kode SQL tambahan di tempat yang tidak diharapkan. Misalkan kita memiliki metode yang menerima semacam parameter untuk menanyakan database. Misalnya, nama pengguna. Kode yang rentan akan terlihat 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, kueri SQL disiapkan terlebih dahulu pada baris terpisah. Jadi apa masalahnya, bukan? Mungkin masalahnya adalah lebih baik menggunakan String.format ? TIDAK? Nah, lalu bagaimana? Mari menempatkan diri kita pada posisi penguji dan memikirkan tentang apa yang dapat diteruskan sebagai nilai firstName . Misalnya:
  1. Kami dapat memberikan apa yang diharapkan — nama pengguna. Kemudian database akan mengembalikan semua pengguna dengan nama itu.
  2. Kita bisa melewatkan string kosong. Kemudian semua pengguna akan dikembalikan.
  3. Tapi kita juga bisa melewati yang berikut: "'; DROP TABLE USERS;". Dan di sini kita sekarang memiliki masalah yang sangat besar. Kueri ini akan menghapus tabel dari database. Bersama dengan semua data. SEMUA ITU.
Dapatkah Anda membayangkan masalah yang akan ditimbulkannya? Selain itu, Anda dapat menulis apa pun yang Anda inginkan. Anda dapat mengubah nama semua pengguna. Anda dapat menghapus alamat mereka. Cakupan sabotase sangat besar. Untuk menghindarinya, Anda perlu mencegah injeksi kueri yang sudah jadi dan sebagai gantinya membentuk kueri menggunakan parameter. Ini harus menjadi satu-satunya cara untuk membuat kueri basis data. Ini adalah bagaimana Anda dapat menghilangkan kerentanan ini. Misalnya:
// 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 kerentanan dihindari. Bagi mereka yang ingin mendalami artikel ini lebih dalam, inilah contoh yang bagus . Bagaimana Anda tahu kapan Anda memahami kerentanan ini? Jika Anda mendapatkan lelucon dalam komik di bawah ini, maka Anda mungkin memiliki pemahaman yang jelas tentang kerentanan ini :DKeamanan di Jawa: praktik terbaik - 2

3. Pindai dependensi dan terus perbarui

Maksudnya itu apa? Jika Anda tidak tahu apa itu ketergantungan, saya akan menjelaskan. Dependensi adalah arsip JAR dengan kode yang terhubung ke proyek menggunakan sistem build otomatis (Maven, Gradle, Ant) untuk menggunakan kembali solusi orang lain. Misalnya, Project Lombok , yang menghasilkan getter, setter, dll. untuk kita saat runtime. Aplikasi besar dapat memiliki banyak ketergantungan. Beberapa bersifat transitif (yaitu, setiap dependensi mungkin memiliki dependensinya sendiri, dan seterusnya). Akibatnya, penyerang semakin memperhatikan dependensi sumber terbuka, karena mereka sering digunakan dan banyak klien dapat mengalami masalah karenanya. Penting untuk memastikan bahwa tidak ada kerentanan yang diketahui di seluruh pohon ketergantungan (ya, ini terlihat seperti pohon). Ada beberapa cara untuk melakukan ini.

Gunakan Snyk untuk pemantauan ketergantungan

Snyk memeriksa semua dependensi proyek dan menandai kerentanan yang diketahui. Anda dapat mendaftar di Snyk dan mengimpor proyek Anda melalui GitHub. Keamanan di Jawa: praktik terbaik - 3Selain itu, seperti yang dapat Anda lihat dari gambar di atas, jika kerentanan diperbaiki di versi yang lebih baru, Snyk akan menawarkan perbaikan dan membuat permintaan penarikan. Anda dapat menggunakannya secara gratis untuk proyek sumber terbuka. Proyek dipindai secara berkala, misalnya seminggu sekali, sebulan sekali. Saya mendaftar dan menambahkan semua repositori publik saya ke pemindaian Snyk (tidak ada yang berbahaya tentang ini, karena sudah publik untuk semua orang). Snyk kemudian menunjukkan hasil pemindaian: Keamanan di Jawa: praktik terbaik - 4Dan setelah beberapa saat, Snyk-bot menyiapkan beberapa permintaan penarikan dalam proyek di mana dependensi perlu diperbarui: Keamanan di Jawa: praktik terbaik - 5Dan juga:Keamanan di Jawa: praktik terbaik - 6Ini adalah alat yang hebat untuk menemukan kerentanan dan memantau pembaruan untuk versi baru.

Gunakan Lab Keamanan GitHub

Siapa saja yang bekerja di GitHub dapat memanfaatkan alat bawaannya. Anda dapat membaca lebih lanjut tentang pendekatan ini di postingan blog mereka yang berjudul Announcing GitHub Security Lab . Alat ini, tentu saja, lebih sederhana daripada Snyk, tetapi Anda tidak boleh mengabaikannya. Terlebih lagi, jumlah kerentanan yang diketahui hanya akan bertambah, sehingga Lab Keamanan Snyk dan GitHub akan terus berkembang dan meningkat.

Aktifkan Sonatype DepShield

Jika Anda menggunakan GitHub untuk menyimpan repositori, Anda dapat menambahkan Sonatype DepShield, salah satu aplikasi di MarketPlace, ke proyek Anda. Itu juga dapat digunakan untuk memindai proyek untuk dependensi. Selain itu, jika menemukan sesuatu, Masalah GitHub akan dibuat dengan deskripsi yang sesuai seperti yang ditunjukkan di bawah ini:Keamanan di Jawa: praktik terbaik - 7

4. Tangani data rahasia dengan hati-hati

Alternatifnya, kami dapat menggunakan frasa "data sensitif". Membocorkan informasi pribadi pelanggan, nomor kartu kredit, dan informasi sensitif lainnya dapat menyebabkan kerugian yang tidak dapat diperbaiki. Pertama-tama, perhatikan baik-baik desain aplikasi Anda dan tentukan apakah Anda benar-benar membutuhkan data ini atau itu. Mungkin Anda sebenarnya tidak membutuhkan beberapa data yang Anda miliki — data yang ditambahkan untuk masa depan yang belum datang dan kemungkinan besar tidak akan datang. Selain itu, Anda banyak yang secara tidak sengaja membocorkan data tersebut melalui pencatatan. Cara mudah untuk mencegah data sensitif memasuki log Anda adalah dengan menggosok metode toString() dari entitas domain (seperti Pengguna, Siswa, Guru, dll.). Ini akan mencegah Anda mengeluarkan bidang rahasia secara tidak sengaja. Jika Anda menggunakan Lombok untuk menghasilkan toString(), Anda bisa menggunakan anotasi @ToString.Exclude untuk mencegah kolom digunakan dalam output metode toString() . Juga, berhati-hatilah saat mengirim data ke dunia luar. Misalkan kita memiliki titik akhir HTTP yang menampilkan nama semua pengguna. Tidak perlu menunjukkan ID internal unik pengguna. Mengapa? Karena penyerang dapat menggunakannya untuk mendapatkan informasi lain yang lebih sensitif tentang pengguna. Misalnya, jika Anda menggunakan Jackson untuk membuat serial/deserialisasi POJO ke/dari JSON , maka Anda dapat menggunakan @JsonIgnore dan @JsonIgnorePropertiesanotasi untuk mencegah serialisasi/deserialisasi bidang tertentu. Secara umum, Anda perlu menggunakan kelas POJO yang berbeda di tempat yang berbeda. Maksudnya itu apa?
  1. Saat bekerja dengan database, gunakan satu jenis POJO (entitas).
  2. Saat bekerja dengan logika bisnis, ubah entitas menjadi model.
  3. Saat bekerja dengan dunia luar dan mengirim permintaan HTTP, gunakan entitas yang berbeda (DTO).
Dengan cara ini Anda dapat dengan jelas menentukan bidang mana yang akan terlihat dari luar dan mana yang tidak.

Gunakan enkripsi yang kuat dan algoritma hashing

Data rahasia pelanggan harus disimpan dengan aman. Untuk melakukan ini, kita perlu menggunakan enkripsi. Bergantung pada tugasnya, Anda perlu memutuskan jenis enkripsi mana yang akan digunakan. Selain itu, enkripsi yang lebih kuat membutuhkan lebih banyak waktu, jadi sekali lagi Anda perlu mempertimbangkan berapa banyak kebutuhan untuk membenarkan waktu yang dihabiskan untuk itu. Tentu saja, Anda dapat menulis sendiri algoritme enkripsi. Tapi ini tidak perlu. Anda dapat menggunakan solusi yang ada di area ini. Misalnya, 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 kita lihat apa yang harus dilakukan, dengan menggunakan contoh berikut yang melibatkan enkripsi dan dekripsi:
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));
}

Mengenkripsi kata sandi

Untuk tugas ini, paling aman menggunakan enkripsi asimetris. Mengapa? Karena aplikasi tidak terlalu perlu mendekripsi kata sandi. Ini adalah pendekatan standar. Pada kenyataannya, ketika pengguna memasukkan kata sandi, sistem mengenkripsinya dan membandingkannya dengan apa yang ada di penyimpanan kata sandi. Proses enkripsi yang sama dilakukan, jadi kita bisa berharap mereka akan cocok, jika kata sandi yang dimasukkan benar, tentu saja :) BCrypt dan SCrypt cocok di sini. Keduanya adalah fungsi satu arah (hash kriptografi) dengan algoritme kompleks komputasi yang memakan waktu lama. Inilah yang kita butuhkan, karena perhitungan langsung akan memakan waktu lama (yah, lama, lama sekali). Spring Security mendukung berbagai macam algoritme. Kita bisa menggunakan SCryptPasswordEncoder dan BCryptPasswordEncoder. Apa yang saat ini dianggap sebagai algoritme enkripsi yang kuat dapat dianggap lemah tahun depan. Akibatnya, kami menyimpulkan bahwa kami harus secara teratur memeriksa algoritme yang kami gunakan dan, jika perlu, memperbarui pustaka yang berisi algoritme enkripsi.

Alih-alih sebuah kesimpulan

Hari ini kami berbicara tentang keamanan dan, tentu saja, banyak hal yang tertinggal. Saya baru saja membuka pintu ke dunia baru untuk Anda, dunia yang memiliki kehidupannya sendiri. Keamanan sama seperti politik: jika Anda tidak menyibukkan diri dengan politik, politik akan menyibukkan diri dengan Anda. Saya secara tradisional menyarankan agar Anda mengikuti saya di akun GitHub . Di sana saya memposting kreasi saya yang melibatkan berbagai teknologi yang saya pelajari dan terapkan di tempat kerja.

Tautan yang bermanfaat

  1. Guru99: Tutorial Injeksi SQL
  2. Oracle: Pusat Sumber Daya Keamanan Java
  3. Oracle: Pedoman Pengkodean Aman untuk Java SE
  4. Baeldung: Dasar-dasar Keamanan Java
  5. Sedang: 10 tips untuk memperkuat keamanan Java Anda
  6. Snyk: 10 praktik terbaik keamanan Java
  7. GitHub: Mengumumkan Lab Keamanan GitHub: mengamankan kode dunia, bersama-sama
Komentar
  • Populer
  • Baru
  • Lama
Anda harus login untuk memberikan komentar
Halaman ini belum memiliki komentar