Template Injection (SSTI) — Security

Apa itu Server-Side Template Injection (SSTI)? SSTI terjadi ketika input pengguna disisipkan langsung ke dalam template engine di sisi server. Penyerang bisa…

Template Injection (SSTI) terjadi waktu input dari user ditempel langsung ke dalam template di server, lalu template engine menjalankan isinya. Bayangin surat undangan mail merge: ada template "Halo {nama}," dan mesin mengisi bagian nama. Yang aman: mesin cuma menaruh teks namamu. Yang bahaya: mesin membaca apa pun di kotak nama sebagai perintah, jadi kalau kamu tulis 7*7 di situ, mesinnya betulan menghitung dan mencetak 49.

🧪 Tebakan cepat: di app yang rentan, user mengisi nama dengan {{7*7}}. Yang muncul di halaman Halo 49!, Halo {{7*7}}!, atau error? Tebak dulu, jawabannya ada di bagian "Jalankan di Lab".

Dua istilah dulu

Masalahnya muncul kalau kamu bikin template-nya dari input user, bukan mengisi slot dengan input user. Itu dua hal yang beda, dan bedanya menentukan aman atau tidak.

Kode yang rentan

# RENTAN: input user jadi template-nya
@app.route("/greet")
def greet():
    nama = request.args.get("nama")
    template = f"Halo {nama}!"              # nama ditempel ke string template
    return render_template_string(template) # engine merender string itu

Jalankan di Lab

Di Lab ada halaman Halo <nama>! dengan dua mode. Isi nama dengan {{7*7}}:

Mode Yang terjadi Hasil di halaman
Render input sebagai template (rentan) engine melihat {{7*7}} sebagai ekspresi, lalu menghitung Halo 49!
Kirim input sebagai data (aman) engine cuma menaruh teks apa adanya Halo {{7*7}}!

Jadi jawaban tebakannya: di app yang rentan, muncul Halo 49!. Angka 49 yang muncul itu tanda bahaya: kalau 7*7 saja dievaluasi, berarti engine mau menjalankan ekspresi lain juga. Dari situ penyerang lanjut membaca konfigurasi server, lalu bisa sampai menjalankan perintah sistem. Itu sebabnya SSTI dianggap setara dengan Remote Code Execution (RCE), yaitu penyerang bisa menjalankan kode di server.

Cara memperbaiki: data, bukan template

# AMAN: slot-nya tetap, user cuma mengisi datanya
@app.route("/greet")
def greet():
    nama = request.args.get("nama")
    return render_template("greet.html", nama=nama)
    # greet.html berisi: Halo {{ nama }}!

Aturan satu kalimat: template ditulis developer, input user cuma mengisi slotnya.

⚠️ Jebakan yang sering ketemu

TL;DR: SSTI (Server-Side Template Injection) terjadi saat input user dijadikan template langsung, lalu template engine mengeksekusinya. Kirim {{7*7}} ke app yang rentan, muncul Halo 49!: engine menghitung ekspresinya, dan dari situ bisa sampai Remote Code Execution. Perbaikannya: tulis template tetap (render_template), dan kirim input user sebagai data yang mengisi slot, bukan sebagai template-nya. Jangan andalkan filter karakter.

Yang akan kamu pelajari