Anatomi Kerentanan Session & Auth di Hanami
Pengamanan autentikasi berbasis session pada Hanami 3.0 bertumpu pada dua layer utama: proteksi state transport melalui Rack session cookie dan integritas payload melalui contract schema. Mengabaikan konfigurasi cookie mengekspos session ID terhadap serangan Cross-Site Scripting (XSS) dan Cross-Site Request Forgery (CSRF). Di sisi lain, parsing parameter yang longgar memicu eksploitasi parameter tampering.
1. Hardening Rack Session Middleware
Hanami memanfaatkan ekosistem Rack untuk manajemen session. Konfigurasi session cookie dilakukan di level slice atau base app (config/app.rb). Kunci keamanan cookie meliputi tiga flag: HttpOnly untuk mencegah akses JavaScript, Secure untuk membatasi cookie hanya pada HTTPS, dan SameSite untuk mencegah pengiriman cookie pada cross-site requests.
# config/app.rb
module MyApp
class App < Hanami::App
config.actions.sessions = :cookie, {
key: "_my_app_session",
secret: settings.session_secret,
expire_after: 60 * 60 * 24, # 24 jam
path: "/",
httponly: true,
secure: Hanami.env?(:production),
same_site: :lax
}
end
endGunakan same_site: :strict jika aplikasi Anda adalah portal backend tertutup tanpa integrasi tautan masuk dari domain pihak ketiga. Gunakan :lax jika user memerlukan session tetap aktif saat mengklik tautan eksternal langsung ke aplikasi.
2. Sanitasi dan Pengelolaan Secrets Runtime
Hanami 3.0 mengelola konfigurasi runtime via dynamic setting contracts (config/settings.rb). Hindari membaca ENV langsung di dalam controller atau action untuk mencegah secret leakage saat debugging atau unhandled exception.
# config/settings.rb
module MyApp
class Settings < Hanami::Settings
setting :session_secret, constructor: Types::String
end
endPastikanSESSION_SECRETmemiliki entropi minimal 64 karakter acak. Nilai dapat di-generate dengan commandbundle exec hanami secretatauopenssl rand -hex 32.
3. Validasi Schema Payload Autentikasi
Validasi input login harus ditangani langsung pada boundary action sebelum menyentuh domain auth. Hanami Actions terintegrasi dengan dry-validation/dry-schema untuk casting tipe data dan validasi boundary secara otomatis.
# slices/main/actions/sessions/create.rb
module Main
module Actions
module Sessions
class Create < Main::Action
params do
required(:email).filled(:string, format?: /\A[^@\s]+@[^@\s]+\z/)
required(:password).filled(:string, min_size?: 12)
end
def handle(req, res)
halt 422 unless req.params.valid?
# ponytail: validasi credential langsung tanpa abstraksi domain service tambahan
user = Authenticator.call(req.params[:email], req.params[:password])
halt 401 unless user
# Regenerasi session ID untuk mitigasi Session Fixation
res.session.clear
res.session[:user_id] = user.id
res.redirect_to "/dashboard"
end
end
end
end
end4. Verifikasi Keamanan via Assert Test
Gunakan testing berbasis Rack/Hanami::Action untuk memastikan payload invalid ditolak dan header Set-Cookie membawa flag proteksi yang diwajibkan.
# test/actions/sessions/create_test.rb
require "test_helper"
class CreateSessionTest < Minitest::Test
def test_rejects_invalid_email_format
action = Main::Actions::Sessions::Create.new
response = action.call(email: "invalid-format", password: "short")
assert_equal 422, response.status
refute response.params.valid?
assert response.params.errors.key?(:email)
assert response.params.errors.key?(:password)
end
def test_cookie_flags_in_rack_response
app = MyApp::App.rack_app
env = Rack::MockRequest.env_for("/sessions", method: "POST", params: {
email: "[email protected]",
password: "SecurePassword123!"
})
status, headers, _body = app.call(env)
cookie_header = headers["set-cookie"] || headers["Set-Cookie"]
if cookie_header
assert_includes cookie_header, "HttpOnly"
assert_includes cookie_header, "SameSite=Lax"
end
end
endTrade-offs dan Tips Operasional
- Session Cookie Size: Cookie store Rack memiliki batasan ukuran 4KB. Jangan menyimpan state besar di session; simpan hanya identifier integer/UUID primitif.
- Subdomain Sharing: Jika session harus shared antar-subdomain, eksplisitkan opsi
domain: :allpada konfigurasi cookies, namun batasi scope domain seminimal mungkin. - Handling Invalidation:
res.session.clearhanya menghapus key pada cookie yang dibawa request. Jika state persistence di sisi server dibutuhkan (misal: single sign-out terpusat), gunakan storage Redis atau cache store sebagai pengganti cookie session murni.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!