Belajar Ruby on Rails - Rate Limiting & DoS Protection
Episode 20 of 27

Belajar Ruby on Rails - Rate Limiting & DoS Protection

Membedah rate limiting dan proteksi DoS di Rails: Rack::Attack untuk throttling login & API, perlindungan brute-force, dan pertahanan berlapis di level aplikasi

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 19 kalian mengelola kerentanan dependency, episode 20 membahas ancaman yang tidak memerlukan exploit sama sekali: serangan volume — brute-force login, spam API, dan DoS (Denial of Service). Semua bisa dihentikan oleh satu mekanisme sederhana: rate limiting.

Mengapa episode ini penting? Karena endpoint login tanpa throttle adalah undangan brute-force, dan endpoint publik tanpa limit bisa di-banjiri hingga server kolaps. Rate limiting adalah pertahanan murah dengan dampak besar — dan Rails menawarkan Rack::Attack, middleware yang bisa dikonfigurasi dalam hitungan menit.

Mengapa Rate Limiting

Bayangkan endpoint login: penyerang bisa mencoba jutaan kombinasi password per jam. Tanpa limit, hanya masalah waktu sebelum password lemah tertembus. Rate limiting membatasi jumlah request dari satu sumber dalam periode waktu tertentu:

100%

Response 429 memberi tahu client (dan penyerang) bahwa mereka melampaui batas. Untuk bot, 429 plus jeda adalah sinyal pindah target; untuk user normal, batas yang wajar tidak akan pernah tersentuh.

Menginstall Rack::Attack

Install Rack::Attack
bundle add rack-attack
bin/rails g rack_attack:install

Generator membuat config/initializers/rack_attack.rb. Aktifkan middleware di aplikasi:

Rubyconfig/application.rb
config.middleware.use Rack::Attack

Throttling Login (Brute-Force Protection)

Pola paling penting: batasi percobaan login per IP:

Rubyconfig/initializers/rack_attack.rb
Rack::Attack.throttle("logins/ip", limit: 5, period: 1.minute) do |req|
  req.post? && req.path == "/login" && req.ip
end

Maksimal 5 percobaan login per menit per IP. Percobaan ke-6 mendapat 429. Untuk pencegahan brute-force yang lebih pintar, kombinasikan dengan pembatasan per email — tetapi hati-hati: membatasi per email bisa menjadi alat untuk lockout attack (penyerang mengunci korban dengan sengaja). Kombinasi IP + email dengan limit lebih longgar di email adalah praktik umum.

Throttling API per Token

Untuk API, batasi berdasarkan token autentikasi, bukan IP (IP bisa shared di belakang NAT):

RubyThrottle API per token
Rack::Attack.throttle("api/token", limit: 300, period: 1.minute) do |req|
  req.headers["Authorization"] if req.path.start_with?("/api/v1/")
end

300 request per menit per token — batas awal yang masuk akal untuk API publik, disesuaikan dengan kebutuhan.

Safelist & Blocklist

Rack::Attack mendukung tiga kategori kontrol:

RubySafelist dan blocklist
Rack::Attack.safelist("allow-healthcheck") do |req|
  req.path == "/healthcheck"
end
 
Rack::Attack.blocklist("block-known-bad-ip") do |req|
  req.ip == "203.0.113.66"
end
  • safelist — request yang selalu diizinkan (healthcheck, webhook internal).
  • blocklist — request yang selalu diblokir (IP terindikasi serangan).
  • throttle — request dibatasi (brute-force, API abuse).

Menyesuaikan Response 429

Rack::Attack memberi blocklist/throttle default response 403/429 dengan body polos. Sesuaikan agar client paham:

RubyCustom response 429
Rack::Attack.throttled_responder = lambda do |req|
  headers = {
    "Retry-After" => "60",
    "Content-Type" => "application/json"
  }
  [429, headers, [{ error: { code: "rate_limited", message: "Terlalu banyak request, coba lagi dalam 60 detik" } }.to_json]]
end

Header Retry-After memberi tahu client kapan boleh mencoba lagi — penting untuk integrasi API yang baik.

Note

Rack::Attack menyimpan counter di Rack middleware state secara default — artinya counter tidak di-share antar instance. Untuk production multi-instance, aktifkan Redis store: Rack::Attack.cache.store = ActiveSupport::Cache::RedisCacheStore.new. Tanpa ini, throttling bisa di-bypass dengan membagi request ke beberapa server.

Pertahanan Berlapis

Rate limiting di aplikasi adalah satu lapisan dari pertahanan total melawan DoS. Urutan lapisan dari luar ke dalam:

LapisanToolMelindungi Dari
CDN/WAFCloudflare, AWS WAFVolumetric attacks, DDoS besar
Reverse proxyNginx/CaddyConnection limits, bandwidth
Rack::AttackAplikasiBrute-force, API abuse
Database poolpool: configConnection exhaustion
App scalingAuto-scaling (episode 24)Kapasitas membludak

Rack::Attack tidak bisa menghentikan DDoS 100 Gbps — itu tugas CDN/WAF. Yang bisa ia lakukan: menghentikan serangan aplikasi-level (brute-force, spam, abuse) yang paling sering terjadi dan paling murah dilakukan penyerang.

Praktik Lengkap: Melindungi Endpoint Publik

Konfigurasi lengkap yang masuk akal untuk app blog:

RubyRack::Attack production-grade
Rack::Attack.throttle("req/ip", limit: 300, period: 5.minutes) do |req|
  req.ip if req.path == "/"
end
 
Rack::Attack.throttle("logins/ip", limit: 5, period: 1.minute) do |req|
  req.post? && req.path == "/login" && req.ip
end
 
Rack::Attack.throttle("comments/ip", limit: 10, period: 1.minute) do |req|
  req.post? && req.path =~ %r{\A/posts/\d+/comments} && req.ip
end
  • Root dibatasi 300 request / 5 menit per IP.
  • Login 5 percobaan / menit per IP.
  • Komentar 10 submit / menit per IP (anti-spam).

Common Pitfalls

  • Throttling per email saja — bisa dimanfaatkan untuk lockout korban; kombinasikan dengan IP.
  • Counter tidak shared (multi-instance) — throttle bisa di-bypass; pakai Redis store.
  • Limit terlalu ketat — user normal kena 429; ukur lalu tetapkan batas.
  • Response 429 tanpa Retry-After — client bingung kapan boleh coba lagi.
  • Hanya rate limit di aplikasi — DDoS tetap ada; lengkapi dengan CDN/WAF.
  • Throttle route yang tidak dibatasi — login/API aman tapi halaman publik tidak; terapkan proporsional.

Penutup

Episode 20 membekali kalian rate limiting Rails: Rack::Attack sebagai middleware yang bisa dikonfigurasi, throttling login per IP untuk anti brute-force, throttling API per token, safelist/blocklist, response 429 yang informatif, serta model pertahanan berlapis melawan DoS.

Inti yang harus dibawa pulang:

  • Rate limiting: 429 ketika request melewati batas dalam periode waktu.
  • Rack::Attack: throttle, safelist, blocklist dalam satu middleware.
  • Login: 5/menit/IP; API: per token, bukan per IP.
  • Multi-instance → Redis store agar counter ter-share.
  • Pertahanan berlapis: CDN/WAF → proxy → Rack::Attack → DB pool.

Di episode 21 selanjutnya kita akan membedah advanced Active Record — query optimization, select/group/aggregations, raw SQL, composite PK (Rails 8.1), dan praktik reporting query dengan indexing. Sampai jumpa di episode 21!