Panduan Amankan API FastAPI dari OWASP Top 10 untuk Production
Latar Belakang & Lanskap Ancaman OWASP API Top 10
FastAPI populer. Performa ASGI tinggi. Validasi data otomatis via Pydantic. Kecepatan tanpa isolasi keamanan: hancur. Penyerang eksploitasi endpoint mudah. Wajib pasang proteksi bertapis. OWASP API Security Top 10 acuan standar. Identifikasi celah fatal backend API modern:
- API1:2023 Broken Object Level Authorization (BOLA): Endpoint terima ID objek tanpa cek kepemilikan resource. Pengguna A ubah ID di path URL (misal
/api/v1/orders/99ke/api/v1/orders/100). Data Pengguna B bocor. Query ORM lupa filteruser_id. - API2:2023 Broken Authentication: Token JWT tidak ditandatangani benar. Algoritma
nonediizinkan.SECRET_KEYpendek, mudah dibobol brute-force. Expiry time absen. - API3:2023 Broken Object Property Level Authorization: Eksploitasi Mass Assignment & Excessive Data Exposure. Payload JSON terima field terlarang
is_admin: true. Database update otomatis. Endpoint kembalikan seluruh kolom DB termasukpassword_hash. - API4:2023 Unrestricted Resource Consumption: Endpoint tanpa limitasi request. Tanpa pagination limit. Penyerang minta 1.000.000 record sekaligus. CPU 100%, RAM habis. Server Denial of Service (DoS).
- API5:2023 Broken Function Level Authorization (BFLA): Pentransferan hak akses fungsi gagal. User reguler panggil endpoint administratif
DELETE /api/v1/users/5. Otorisasi role absen pada route handler. - API6:2023 Unrestricted Access to Sensitive Business Flows: Bot eksploitasi alur bisnis sensitif. Pembelian tiket, claim voucher, registrasi akun massal. Otomatisasi tanpa bot detection.
- API7:2023 Server Side Request Forgery (SSRF): Endpoint terima URL dari input user. Server lakukan HTTP request ke URL tersebut tanpa filter. Penyerang paksa server akses metadata cloud internal (
http://169.254.169.254/latest/meta-data/). - API8:2023 Security Misconfiguration: CORS dikonfigurasi
*. Swagger UI aktif di production. Header keamanan HTTP absen. Log aplikasi bocorkan token JWT. - API9:2023 Improper Inventory Management: Endpoint versi lama (
/api/v1/) tetap aktif tanpa patch. Lingkungan staging terakses publik tanpa otentikasi. - API10:2023 Unsafe Consumption of APIs: Aplikasi percaya penuh data dari API pihak ketiga. Tidak ada sanitasi output pihak ketiga sebelum masuk ke database internal.
Praktek Middleware: Strict CORS, Trusted Host, & Rate Limiting
Amankan layer terdepan FastAPI. Cegah request jahat sebelum masuk route handler.
1. Restriksi Strict CORS & Trusted Host
Wildcard origin (*) pada production: bahaya fatal. Izinkan domain terverifikasi saja. Tambah TrustedHostMiddleware cegah HTTP Host Header Attack.
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
app = FastAPI(docs_url=None, redoc_url=None)
# Restricted domain origin
ALLOWED_ORIGINS = [
"https://app.domainanda.com",
"https://admin.domainanda.com"
]
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
allow_headers=["Authorization", "Content-Type", "X-Request-ID"],
)
# Host header attack prevention
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["api.domainanda.com", "*.domainanda.com"]
)2. Rate Limiting Terdistribusi dengan Slowapi
Membatasi request per IP. Cegah brute-force login dan serangan DoS resource intensive. Integrasi slowapi dengan backend Redis.
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
from fastapi import Request, FastAPI
limiter = Limiter(key_func=get_remote_address, storage_uri="redis://localhost:6379/0")
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.post("/api/v1/auth/login")
@limiter.limit("5/minute")
async def login(request: Request):
return {"status": "proses_otentikasi"}
@app.get("/api/v1/reports/export")
@limiter.limit("2/hour")
async def export_report(request: Request):
return {"status": "proses_export"}Otentikasi Strict JWT & Proteksi BOLA/BFLA
Ekstraksi klaim JWT. Validasi signature. Terapkan verifikasi kepemilikan resource langsung di query layer (Pencegahan BOLA).
import jwt
from datetime import datetime, timezone
from typing import Annotated
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel
SECRET_KEY = "c8f3b2d1e4a5f67890123456789abcdef0123456789abcdef0123456789abcde" # Min 64 hex chars
ALGORITHM = "HS256"
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
class TokenPayload(BaseModel):
sub: int
role: str
exp: int
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> TokenPayload:
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Kredensial otentikasi tidak valid",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM], options={"verify_exp": True})
user_id: int = payload.get("sub")
role: str = payload.get("role")
exp: int = payload.get("exp")
if user_id is None or role is None:
raise credentials_exception
return TokenPayload(sub=user_id, role=role, exp=exp)
except jwt.PyJWTError:
raise credentials_exception
def require_role(required_role: str):
def role_checker(current_user: TokenPayload = Depends(get_current_user)):
if current_user.role != required_role and current_user.role != "admin":
raise HTTPException(status_code=403, detail="Akses ditolak: Hak akses tidak mencukupi")
return current_user
return role_checker
@app.get("/api/v1/orders/{order_id}")
async def get_order(
order_id: int,
current_user: Annotated[TokenPayload, Depends(get_current_user)]
):
# Query DB wajib memuat pengkondisian user_id (Mencegah BOLA)
# SIMULASI: Fetch dari DB menggunakan parameter terisolasi
order = await fetch_order_by_id_and_owner(order_id=order_id, owner_id=current_user.sub)
if not order:
# Kembalikan 404 bukan 403 agar tidak membocorkan keberadaan ID objek milik user lain
raise HTTPException(status_code=404, detail="Order tidak ditemukan")
return order
@app.delete("/api/v1/users/{target_user_id}", dependencies=[Depends(require_role("admin"))])
async def delete_user(target_user_id: int):
# Mencegah BFLA dengan penegakan peran (Role-Based Access Control)
return {"status": f"User {target_user_id} berhasil dihapus"}Validasi Input Pydantic V2 & Prevent Mass Assignment
Sanitasi data masuk. Mencegah Injection Attack (SQLi, Command Injection, XSS) dan Mass Assignment (API3:2023). Gunakan Pydantic V2 `extra='forbid'`.
from pydantic import BaseModel, Field, EmailStr, ConfigDict, field_validator
import re
class RegisterInputSchema(BaseModel):
# Tolak semua field yang tidak terdefinisi di skema (Anti Mass Assignment)
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
username: str = Field(..., min_length=3, max_length=30, pattern="^[a-zA-Z0-9_]+$")
email: EmailStr
password: str = Field(..., min_length=12, max_length=128)
@field_validator("password")
@classmethod
def validate_password_complexity(cls, v: str) -> str:
if not re.search(r"[A-Z]", v):
raise ValueError("Password wajib mengandung minimal 1 huruf besar")
if not re.search(r"[a-z]", v):
raise ValueError("Password wajib mengandung minimal 1 huruf kecil")
if not re.search(r"[0-9]", v):
raise ValueError("Password wajib mengandung minimal 1 angka")
if not re.search(r"[^a-zA-Z0-9]", v):
raise ValueError("Password wajib mengandung minimal 1 karakter spesial")
return v
class UserResponseSchema(BaseModel):
# Hanya field ini yang dipublikasikan ke HTTP response
model_config = ConfigDict(from_attributes=True)
id: int
username: str
email: EmailStr
# password_hash, salt, is_admin TIDAK dimasukkan di siniPengerasan HTTP Response Security Headers
Injeksi HTTP response headers standar industri. Proteksi browser-side dari XSS, Clickjacking, MIME sniffing.
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import Request, Response
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next) -> Response:
response: Response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload"
response.headers["Content-Security-Policy"] = "default-src 'none'; frame-ancestors 'none'; sandbox"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
response.headers["Permissions-Policy"] = "geolocation=(), microphone=(), camera=()"
# Hapus informasi server bawaan
response.headers["Server"] = "Protected-API"
return response
app.add_middleware(SecurityHeadersMiddleware)Checklist Hardening Production & CI/CD Security Pipeline
- Nonaktifkan Swagger UI & ReDoc: Pastikan dokumentasi interaktif nonaktif di lingkungan produksi. Set parameter
docs_url=None,redoc_url=None, danopenapi_url=Nonepada penginstansiasi kelasFastAPI()jika lingkungan berjalan pada moda production. - Isolasi Rahasia via Variable Lingkungan: Jangan pernah menyimpan kredensial database atau
SECRET_KEYsecara hardcoded di codebase. Manfaatkan modulpydantic-settingsuntuk membaca nilai dari file.envterisolasi yang diinjeksikan oleh secret manager. - Jalankan Aplikasi dengan User Non-Root: Konfigurasi
Dockerfileuntuk membuat grup dan pengguna sistem khusus tanpa hak akses root. Eksekusi proses Uvicorn atau Gunicorn menggunakan identitas non-privileged tersebut guna membatasi dampak jika terjadi pemutusan container (container breakout). - Terapkan Reverse Proxy & TLS Termination: Tempatkan Nginx, Traefik, atau Cloudflare di depan server ASGI Uvicorn. Konfigurasikan TLS 1.3, SSL Certificate renewal otomatis, HTTP request size limiting, serta pembatasan koneksi di tingkat proxy.
- Audit Vulnerability Dependensi Python: Integrasikan pengujian keamanan otomatis pada pipeline CI/CD menggunakan alat seperti
pip-audit,safety, atautrivyuntuk mendeteksi pustaka terinstal yang memiliki kerentanan CVE sebelum deployment dilakukan. - Sanitasi System Logging & Handling Data Sensitif: Terapkan kustomisasi formatter pada modul logging FastAPI. Pastikan parameter sensitif seperti token otentikasi Bearer, kata sandi, data nomor kartu kredit, dan PII (Personally Identifiable Information) tersanitasi atau di-masking sebelum ditulis ke berkas log server.
Kesimpulan
Pengamanan API FastAPI tingkat production butuh pendekatan Defense-in-Depth. Skema validasi Pydantic V2 ketat. Middleware CORS dan Rate Limiting terisolasi. Otorisasi data-layer pencegah BOLA. Terapkan seluruh lapisan keamanan. Sistem backend aman dari ancaman lanskap OWASP API Security Top 10.


