Episode ini membahas keamanan aplikasi Ruby: kerentanan umum seperti SQL injection, command injection, XSS, dan unsafe deserialization, serta alat keamanan Brakeman dan bundler-audit serta penanganan secrets melalui environment variables.

Setelah berhasil membangun REST API di episode 14, pertanyaan berikutnya adalah: apakah aplikasi kalian aman? Keamanan sering dianggap sebagai lapisan terakhir, padahal justru harus dipikirkan sejak awal. Episode 15 ini membahas security best practices dalam pengembangan Ruby.
Kita akan membedah kerentanan umum yang sering ditemukan di aplikasi Ruby — SQL injection, command injection, XSS, dan unsafe deserialization — lalu mengenal dua alat keamanan penting: Brakeman untuk static analysis dan bundler-audit untuk memeriksa gem yang rentan. Terakhir, kita bahas cara menangani secrets melalui environment variables.
SQL injection terjadi ketika input pengguna disisipkan langsung ke dalam string SQL. Bila kalian menulis kueri dengan interpolasi string, penyerang bisa menyuntikkan SQL berbahaya:
user = params["username"]
User.where("name = '#{user}'")Bila user berisi '; DROP TABLE users; --, kueri di atas bisa merusak tabel. Cara aman: gunakan placeholder parameter. ActiveRecord mengamankan otomatis saat kalian memakai sintaks parameterized:
User.where("name = ?", params["username"])
User.where(name: params["username"])User.where("name = ?", ...) mengganti nilai parameter dengan aman tanpa interpolasi langsung. Prinsipnya: jangan pernah menempelkan input mentah ke string SQL. Data pengguna hanya boleh masuk sebagai parameter terikat.
Command injection mirip dengan SQL injection, tetapi targetnya adalah shell sistem. Menjalankan perintah sistem dengan input pengguna yang terinterpolasi sangat berbahaya:
system("ls #{params["folder"]}")Bila folder berisi ; rm -rf /, shell akan mengeksekusi perintah setelah titik koma. Gunakan bentuk array agar argumen tidak diterjemahkan oleh shell:
system("ls", params["folder"])system("ls", params["folder"]) menjalankan perintah tanpa melewati shell, sehingga karakter shell tidak pernah dievaluasi. Untuk kebutuhan yang kompleks, gunakan library seperti Open3 dan validasi input terlebih dahulu.
XSS terjadi ketika data pengguna dirender sebagai HTML tanpa escaping. Di Rails, ERB meng-escape output otomatis dengan sintaks <%= %>, selama kalian tidak menandainya sebagai raw:
<%= user.comment %>
<%== user.comment %>Baris pertama di-escape aman oleh Rails, sedangkan <%== %> melewatkan escaping dan memicu XSS bila isinya input pengguna. Aturan sederhana: jangan pernah menampilkan input mentah ke browser tanpa sanitasi, dan pastikan Content-Security-Policy aktif di header response.
Deserialization dari data yang tidak dipercaya adalah celah serius. Marshal.load dan YAML.load dapat mengeksekusi kode saat memuat objek berbahaya:
data = Marshal.load(payload)
safe = YAML.safe_load(payload)Marshal.load tidak boleh dipakai untuk data dari pengguna. Gunakan format aman seperti JSON untuk pertukaran data, atau YAML.safe_load yang menolak objek selain tipe dasar. Sebagai aturan: deserialize hanya data yang kalian produksi sendiri.
Brakeman adalah static security scanner khusus Rails. Ia membaca source code dan melaporkan kerentanan tanpa menjalankan aplikasi:
gem install brakeman
brakeman --quietbrakeman --quiet menghasilkan laporan berwarna dengan ringkasan peringatan dan file yang bermasalah. Integrasikan Brakeman ke CI dengan --exit-on-warn agar build gagal ketika ada peringatan baru. Semakin sering dijalankan, semakin cepat kerentanan ditemukan.
Kerentanan juga bisa bersembunyi di gem pihak ketiga. bundler-audit memeriksa Gemfile.lock terhadap database advisories:
gem install bundler-audit
bundle audit check --updatebundle audit check --update memperbarui database advisory lalu melaporkan gem dengan versi rentan beserta referensi CVEs. Jadwalkan audit ini di CI pada setiap perubahan dependency, bukan hanya sekali-sekali.
Jangan pernah menulis kunci API, password database, atau token ke dalam source code. Simpan semuanya di environment variables, dan baca dengan ENV:
db_password = ENV["DATABASE_PASSWORD"]
raise "DATABASE_PASSWORD belum diatur" if db_password.nil?ENV["DATABASE_PASSWORD"] mengambil nilai dari environment. Di pengembangan, gunakan gem seperti dotenv dengan file .env yang masuk gitignore; di production, pasang secrets langsung di environment host atau vault. Rails sendiri menyediakan bin/rails credentials untuk menyimpan secrets terenkripsi.
Warning
Keamanan adalah proses, bukan status. Kombinasi Brakeman, bundler-audit, parameterized query, dan secrets di environment variables menutup sebagian besar celah umum, tetapi tetap lakukan penetration test dan pantau advisories secara berkala.
Inti yang harus dibawa pulang:
system dan exec.Marshal.load dan YAML.unsafe_load tidak boleh untuk data tak tepercaya.Gemfile.lock.Di episode 16 kita akan mendalami penanganan data yang aman: encoding UTF-8 dan transkoding string, validasi dan sanitasi input, strong params di Rails, hashing password dengan bcrypt, serta prinsip least privilege. Keamanan dari sisi data melengkapi fondasi yang kita bangun sekarang.