Belajar Flask - Security Best Practices
Episode 17 of 27

Belajar Flask - Security Best Practices

Mengeraskan aplikasi Flask dari serangan web yang umum: XSS dengan autoescape dan sanitasi, CSRF dengan token, SQL injection lewat ORM dan parameter binding, clickjacking, hingga secure headers dengan Flask-Talisman dan praktik hardening menyeluruh.

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

Pendahuluan

Aplikasi web tidak hidup sendirian — ia menghadapi internet yang berisi penyerang. Episode 17 mengubah pola pikir dari "membuat fitur" menjadi "mengamankan fitur": kita membahas lima serangan paling umum terhadap web app — XSS, CSRF, SQL injection, clickjacking, dan header tidak aman — beserta pertahanannya di Flask.

Sebagian pertahanan ini sudah kita sentuh di episode sebelumnya (autoescape di episode 6, CSRF di episode 12). Episode 17 merangkai semuanya menjadi strategi keamanan yang menyeluruh.

XSS: Cross-Site Scripting

XSS terjadi saat input pengguna dirender sebagai kode HTML/JavaScript. Contoh klasik: kolom komentar yang menerima <script> lalu mengeksekusi-nya di browser pengunjung lain.

Pertahanan berlapis di Flask:

Lapisan 1 — Autoescape (sudah bawaan). Jinja2 meng-escape output {{ }} secara default (episode 6). Jangan matikan tanpa alasan:

HTMLAutoescape default
<p>{{ comment.content }}</p>

Lapisan 2 — Jangan pakai |safe untuk input pengguna. Untuk konten yang memang HTML (artikel markdown), sanitisasi di server dengan library seperti bleach:

PythonSanitasi HTML dengan bleach
import bleach
 
ALLOWED_TAGS = ["p", "b", "i", "a", "ul", "ol", "li", "code", "pre"]
ALLOWED_ATTRS = {"a": ["href", "title"]}
 
clean_html = bleach.clean(raw_html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRS)

bleach.clean() menghapus tag dan atribut di luar daftar — menghilangkan <script> dan onerror sebelum HTML sampai ke template.

CSRF: Sudah Otomatis oleh Flask-WTF

CSRF (episode 12) membuat browser korban mengirim request yang tidak dikehendaki. Pertahanan Flask-WTF (CSRFProtect) sudah aktif otomatis di aplikasi kita:

PythonCSRFProtect aktif
from flask_wtf import CSRFProtect
 
csrf = CSRFProtect()
csrf.init_app(app)   # di create_app()

Jika ada endpoint API yang memakai cookie auth dan dikonsumsi browser dari domain lain, pastikan token CSRF tetap diverifikasi — jangan hanya mengandalkan CORS (episode 14).

SQL Injection: ORM + Parameter Binding

SQL injection terjadi saat input pengguna disisipkan langsung ke string SQL. Karena kita memakai SQLAlchemy ORM, sebagian besar sudah aman — asalkan tidak menulis SQL mentah tanpa parameter:

PythonPola aman vs berbahaya
# AMAN - parameter binding lewat ORM
users = db.session.scalars(
    db.select(User).where(User.username == form.username.data)
).all()
 
# AMAN - text() dengan parameter
rows = db.session.execute(
    db.text("SELECT * FROM users WHERE email = :email"),
    {"email": email},
)
 
# BERBAHAYA - f-string ke dalam SQL
rows = db.session.execute(db.text(f"SELECT * FROM users WHERE email = '{email}'"))

Aturan emas: jangan pernah membangun query SQL dengan f-string atau concatenation. Selalu parameter binding (:nama atau ?) atau ORM. Dengan ORM, input pengguna diperlakukan sebagai data, bukan struktur query.

Warning

Bahaya SQL injection tidak terbatas pada ORM — menyangkut seluruh permukaan database. Waspada juga di query text() mentah, filter dinamis dari request (sorting, search), dan tooling admin. Satu aturan: jika nilainya berasal dari luar kode, ia hanya boleh masuk sebagai parameter, bukan string SQL.

Clickjacking: X-Frame-Options

Clickjacking memaksa pengguna mengklik elemen tak terlihat di dalam iframe berbahaya. Pertahanannya satu header:

PythonCegah clickjacking
@app.after_request
def add_security_headers(response):
    response.headers["X-Frame-Options"] = "DENY"
    response.headers["Content-Security-Policy"] = "frame-ancestors 'none'"
    return response

X-Frame-Options: DENY dan Content-Security-Policy: frame-ancestors 'none' mencegah halaman kalian ditampilkan di iframe situs lain. Kita kembangkan header lengkap di bawah.

Secure Headers dengan Flask-Talisman

Semua header keamanan bisa dirapikan dengan Flask-Talisman, extension khusus:

Install Flask-Talisman
pip install flask-talisman
PythonAktifkan Talisman
from flask_talisman import Talisman
 
talisman = Talisman(
    app,
    force_https=True,           # redirect semua HTTP ke HTTPS
    strict_transport_security=True,   # HSTS
    session_cookie_secure=True,
    content_security_policy={
        "default-src": "'self'",
        "script-src": "'self'",
        "style-src": "'self'",
    },
    frame_options="DENY",
)

Sekaligus mengatur: paksa HTTPS, HSTS, cookie aman, dan Content-Security-Policy (CSP). CSP adalah salah satu pertahanan terkuat melawan XSS: ia membatasi sumber yang boleh dimuat browser. Efek sampingnya langsung terasa — script/style dari CDN atau inline akan diblokir, jadi sesuaikan dengan kebutuhan halaman kalian (kita sentuh lagi di episode 20).

Strategi Keamanan Menyeluruh

Rangkuman lapisan pertahanan yang sudah dan akan kita bangun:

SeranganPertahananEpisode
XSSAutoescape + sanitasi + CSP6, 17
CSRFCSRFProtect + token di semua form12
SQL injectionORM + parameter binding10, 17
ClickjackingX-Frame-Options + frame-ancestors17
Password bocorgenerate_password_hash (scrypt)13
Brute force loginRate limit15
Session palsuSECRET_KEY kuat + cookie flags8, 20
DoS via formMAX_FORM_MEMORY_SIZE/MAX_CONTENT_LENGTH3, 17

Dua konfigurasi dari Flask 3.1 yang sebaiknya selalu diaktifkan:

PythonBatasi ukuran request
class BaseConfig:
    MAX_CONTENT_LENGTH = 16 * 1024 * 1024      # 16 MB maksimal
    MAX_FORM_MEMORY_SIZE = 1024 * 1024         # 1 MB form di memori
    MAX_FORM_PARTS = 100                       # maks 100 bagian form

MAX_CONTENT_LENGTH membatasi besar body (menolak upload gede otomatis dengan 413); MAX_FORM_MEMORY_SIZE dan MAX_FORM_PARTS (baru di 3.1) membatasi form yang bisa membanjiri memori.

Common Pitfalls Security

  • |safe pada input pengguna: membuka XSS — sanitasikan dulu dengan bleach.
  • SQL mentah dengan f-string: undangan SQL injection — selalu parameter binding.
  • CSP kosong/tidak ada: melemahkan pertahanan XSS — pasang minimal default-src 'self'.
  • Header keamanan absen: klikjacking, MIME sniffing — pasang Talisman.
  • MAX_CONTENT_LENGTH tak diset: request raksasa bisa membebani server.

Penutup

Pada episode 17 ini, kalian telah mengeraskan aplikasi dengan keamanan berlapis.

Inti yang harus dibawa pulang:

  • XSS: autoescape + bleach untuk konten HTML + CSP — jangan |safe input pengguna.
  • CSRF: CSRFProtect otomatis; token wajib di semua form POST.
  • SQL injection: ORM dan parameter binding; tidak ada f-string dalam SQL.
  • Clickjacking: X-Frame-Options dan frame-ancestors.
  • Talisman merapikan force HTTPS, HSTS, CSP, dan cookie flags.
  • Batasi ukuran request dengan konfigurasi 3.1 (MAX_CONTENT_LENGTH, MAX_FORM_*).

Di episode 18 selanjutnya, kita melengkapi autentikasi untuk API modern: JWT & OAuth — Flask-JWT-Extended dengan access/refresh token dan RBAC, serta social login dengan Authlib untuk SPA dan mobile. Sampai jumpa di episode 18!

Belajar Flask - Security Best Practices | Belajar Flask