Mengeraskan API FastAPI dengan pertahanan berlapis: pencegahan SQLi dengan parameterized query, mitigasi XSS dan CSRF, security headers lengkap, validasi input sebagai gerbang pertama, serta audit dependency.

Setelah di episode 17 API kita punya error handling dan logging yang rapi, sekarang kita rangkai semuanya menjadi satu tema: keamanan. Selama 17 episode kita sudah menabur kebiasaan yang benar — validasi Pydantic, parameterized query, bcrypt, JWT — episode ini merangkumnya menjadi peta pertahanan berlapis, plus menutup celah yang belum dibahas.
Mengapa episode ini penting? Karena kebanyakan serangan tidak canggih — mereka mengeksploitasi hal-hal dasar: query yang dirangkai string, input yang tidak divalidasi, header yang hilang. Pertahanan yang benar dimulai dari menghilangkan celah dasar, bukan memasang "alarm super mahal" di atas celah yang masih terbuka.
Keamanan bekerja berlapis — jika satu lapis tembus, lapis berikutnya masih menahan:
SQL Injection terjadi saat input user dirangkai langsung ke query SQL. Contoh rentan (jangan ditiru):
@app.get("/items/")
def search(name: str, db: Session = Depends(get_db)) -> list:
query = f"SELECT * FROM items WHERE name = '{name}'"
return db.execute(text(query)).all()Input ' OR '1'='1 akan membuat query mengembalikan seluruh tabel. Pertahanannya: selalu parameterized. Dengan SQLAlchemy ORM:
from sqlalchemy import select
@app.get("/items/")
def search(name: str, db: Session = Depends(get_db)) -> list:
statement = select(models.Item).where(
models.Item.name == name
)
return db.execute(statement).scalars().all()ORM where(models.Item.name == name) mengirim name sebagai parameter terikat, bukan string yang di-splice — nilai input tidak pernah ditafsirkan sebagai SQL.
Important
Aturan besi: jangan pernah menyusun query SQL dengan f-string atau +. Kalau kalian terpaksa memakai text() SQL mentah, gunakan placeholder :name + bind parameter. ORM SQLAlchemy yang kita pakai sejak episode 9 sudah aman secara default — pertahankan itu.
XSS (Cross-Site Scripting) terjadi saat input user di-render sebagai HTML/JS. FastAPI sebagai pure API lebih kebal, karena ia mengirim JSON, bukan HTML. Tapi dua jalur tetap perlu dijaga:
HTMLResponse), escape semua input yang di-render.Untuk input teks yang disimpan lalu ditampilkan, validasi di Pydantic membantu membatasi pola berbahaya:
import re
from pydantic import BaseModel, field_validator
class CommentCreate(BaseModel):
body: str
@field_validator("body")
@classmethod
def batasi_script(cls, v: str) -> str:
if re.search(r"<script", v, re.IGNORECASE):
raise ValueError("teks mengandung tag terlarang")
return vPola field_validator ini adalah bentuk validasi domain yang kita bangun di episode 5 — sekarang dipakai sebagai gerbang keamanan.
CSRF mengeksploitasi cookie session: situs jahat memaksa browser korban mengirim request ke API kita dengan cookie otomatis. Mitigasi utamanya:
Origin/Referer.Karena FastAPI auth kita berbasis Bearer token (bukan cookie), serangan CSRF klasik otomatis netral. Ini salah satu alasan pola episode 12 bagus.
Tip
Bonus keamanan token-vs-cookie: cek juga header Origin untuk request yang berbahaya. Middleware sederhana yang memverifikasi Origin terhadap daftar sah adalah lapis pertahanan murah yang jarang dipakai — dan cocok dengan pola middleware episode 11.
Header keamanan memberitahu browser cara memperlakukan response. Gunakan SecureHeadersMiddleware (library secure) atau tulis manual:
pip install securefrom secure import SecureHeaders
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import FastAPI, Request
secure_headers = SecureHeaders()
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
response = await call_next(request)
secure_headers.set_headers(response.headers)
return response
app = FastAPI()
app.add_middleware(SecurityHeadersMiddleware)Header yang dihasilkan meliputi:
| Header | Fungsi |
|---|---|
X-Frame-Options: DENY | Cegah clickjacking |
X-Content-Type-Options: nosniff | Cegah MIME sniffing |
Content-Security-Policy | Batasi sumber script/style |
Strict-Transport-Security | Paksa HTTPS (hanya di produksi) |
Semua ini gratis untuk dipasang dan menutup banyak kelas serangan browser — kombinasikan dengan CORS yang sudah benar dari episode 11.
Aplikasi seaman dependensinya. Library dengan kerentanan adalah backdoor diam-diam. Audit rutin:
pip install pip-audit
pip-auditpip-audit membandingkan package terinstall dengan database CVE. Di CI (episode 24), jadikan ini langkah wajib sebelum build:
pip-audit -o audit.json --fail-on anyWarning
Jangan asal pip install — pin versi dependensi (atau minimal major) dan audit rutin. FastAPI, Pydantic, dan Starlette punya sejarah CVE sendiri. Kebiasaan pip freeze > requirements.txt dari episode 0 dan audit berkala adalah dua sisi dari disiplin yang sama.
| Lapisan | Implementasi | Episode |
|---|---|---|
| Gerbang input | Pydantic model + validators | 5, 6 |
| Query aman | SQLAlchemy ORM parameterized | 9 |
| Autentikasi | OAuth2 + JWT + bcrypt | 12 |
| Host & origin | TrustedHost, CORS, Origin check | 11, 18 |
| Output | response_model filter | 7 |
| Browser | Security headers | 18 |
| Dependency | pip-audit | 18 |
| Pitfall | Solusi |
|---|---|
| f-string SQL | Parameterized query / ORM |
| Validasi hanya di frontend | Validasi server dengan Pydantic |
| Mengandalkan cookie tanpa CSRF | Bearer token + Authorization header |
| Header keamanan hilang | Pasang SecurityHeadersMiddleware |
| Deprecated library dengan CVE | pip-audit rutin + pin versi |
Inti yang harus dibawa pulang:
pip-audit) di CI.Di episode 19 selanjutnya kita akan membahas rate limiting & DoS protection — melindungi API dari banjir request dengan slowapi, throttling per-IP dan per-user, serta konfigurasi reverse proxy untuk mencegah DoS. Pertahanan kalian kini berlapis dari jaringan sampai aplikasi!