Inkonsistensi validasi boundary pada arsitektur multi-runtime sering memicu celah keamanan. Ketika logika ingest data diduplikasi secara manual ke runtime JavaScript (Node.js) dan Python, perbedaan semantik bahasa—seperti evaluasi truthy/falsy, penanganan representasi Unicode string, hingga perbedaan parsing tipe data primitif—membuka celah payload abuse. Penyerang dapat mengeksploitasi payload yang lolos di satu layer validasi tetapi memicu perilaku tak terduga di layer komputasi lainnya.
Temper Language (temperlang.dev) menyelesaikan masalah ini melalui pendekatan single source of truth. Logika domain dan kontrak boundary ditulis sekali dalam Temper, lalu dikompilasi langsung ke target runtime seperti JavaScript dan Python dengan mempertahankan invariant data yang identik.
Risiko Inkonsistensi Validasi Input Lintas Runtime
Menduplikasi parser dan validator payload di JavaScript dan Python memicu diskrepansi struktural:
- Length & Encoding Semantics: Properti
String.prototype.lengthdi JavaScript menghitung UTF-16 code units, sementaralen()di Python 3 menghitung code points Unicode. String dengan karakter astral (seperti emoji atau multi-byte script) akan menghasilkan kalkulasi panjang yang berbeda, membuka celah buffer overflow semantik atau lolosnya batas kuota karakter. - Coercion & Type Juggling: JavaScript mentoleransi konversi implisit (misalnya
[] == falsebernilaitrue), sementara Python melemparTypeError. Perbedaan ini memudahkan penyerang menyusupkan non-string payload pada pipeline JavaScript jika pemeriksaan tipe tidak dilakukan secara ketat. - Bypass Deserialisasi: Objek dinamis hasil parsing JSON mentah rentan terhadap prototype pollution di ekosistem JavaScript atau serangan attribute injection di Python jika skema tidak langsung dikonversi menjadi entitas bertipe statis (strong typing).
Mendefinisikan Invariant dan Pre-conditions di Temper
Temper mengandalkan sistem tipe statis dan konstruksi kontrol alur yang ketat untuk menjamin validitas boundary. Alih-alih mengandalkan sanitasi pasif setelah data masuk ke memori aplikasi, Temper mewajibkan validasi terjadi saat instansiasi objek.
Jika sebuah pre-condition dilanggar, alur eksekusi dihentikan seketika menggunakan mekanisme bubble (propagasi error bawaan Temper) atau lemparan exception eksplisit. Hal ini mencegah keberadaan instance objek dengan status parsial (illegal state) di dalam runtime target.
Implementasi Modul Ingest Payload
Berikut adalah modul validasi boundary di Temper yang mengecek batasan panjang string, pola secret/token, ukuran muatan numerik, serta mencegah injeksi data mentah:
// File: secure_payload.temper
export class IngestPayload {
public let tenantId: String;
public let apiSecret: String;
public let payloadBytes: Int;
public init(tenantId: String, apiSecret: String, payloadBytes: Int) {
// 1. Batas ukuran string (mencegah DoS memori)
if (tenantId.length < 3 || tenantId.length > 32) {
bubble("Validation failed: tenantId must be between 3 and 32 characters");
}
// 2. Proteksi karakter ilegal (hanya alphanumeric dan dash)
if (!IngestPayload.isValidIdentifier(tenantId)) {
bubble("Validation failed: tenantId contains invalid characters");
}
// 3. Validasi format secret/token (prefix guard dan panjang minimum)
if (!apiSecret.startsWith("sec_live_") || apiSecret.length != 41) {
bubble("Validation failed: invalid apiSecret format");
}
// 4. Batas kuota ukuran payload (maksimal 5 MB per batch)
if (payloadBytes <= 0 || payloadBytes > 5242880) {
bubble("Validation failed: payloadBytes exceeds allowed limit");
}
this.tenantId = tenantId;
this.apiSecret = apiSecret;
this.payloadBytes = payloadBytes;
}
private static isValidIdentifier(value: String): Boolean {
let mut i = 0;
while (i < value.length) {
let ch = value[i];
let isLower = ch >= 'a' && ch <= 'z';
let isUpper = ch >= 'A' && ch <= 'Z';
let isDigit = ch >= '0' && ch <= '9';
let isDash = ch == '-';
if (!isLower && !isUpper && !isDigit && !isDash) {
return false;
}
i = i + 1;
}
return true;
}
}
Analisis Hasil Kompilasi ke JavaScript dan Python
Saat kode Temper di atas dikompilasi ke target JavaScript dan Python, compiler mengonversi pre-conditions menjadi instruksi runtime native yang ekivalen tanpa menghilangkan logika pengujian batasan.
1. Target Output JavaScript
Pada target JavaScript/TypeScript, compiler Temper memetakan tipe dan logika validasi ke dalam constructor kelas ES6 standar:
// Compiled JS artifact (ilustrasi logika ekivalen)
class IngestPayload {
constructor(tenantId, apiSecret, payloadBytes) {
if (typeof tenantId !== 'string' || tenantId.length < 3 || tenantId.length > 32) {
throw new Error("Validation failed: tenantId must be between 3 and 32 characters");
}
if (!IngestPayload.isValidIdentifier(tenantId)) {
throw new Error("Validation failed: tenantId contains invalid characters");
}
if (typeof apiSecret !== 'string' || !apiSecret.startsWith("sec_live_") || apiSecret.length !== 41) {
throw new Error("Validation failed: invalid apiSecret format");
}
if (typeof payloadBytes !== 'number' || payloadBytes <= 0 || payloadBytes > 5242880) {
throw new Error("Validation failed: payloadBytes exceeds allowed limit");
}
this.tenantId = tenantId;
this.apiSecret = apiSecret;
this.payloadBytes = payloadBytes;
Object.freeze(this); // Mencegah modifikasi properti pasca-instansiasi
}
}
2. Target Output Python
Pada target Python, logika yang sama diterjemahkan ke dalam metode __init__ dengan tipe native Python dan error handling berbasis RuntimeError atau exception spesifik:
# Compiled Python artifact (ilustrasi logika ekivalen)
class IngestPayload:
def __init__(self, tenant_id: str, api_secret: str, payload_bytes: int) -> None:
if not isinstance(tenant_id, str) or len(tenant_id) < 3 or len(tenant_id) > 32:
raise ValueError("Validation failed: tenantId must be between 3 and 32 characters")
if not IngestPayload._is_valid_identifier(tenant_id):
raise ValueError("Validation failed: tenantId contains invalid characters")
if not isinstance(api_secret, str) or not api_secret.startswith("sec_live_") or len(api_secret) != 41:
raise ValueError("Validation failed: invalid apiSecret format")
if not isinstance(payload_bytes, int) or payload_bytes <= 0 or payload_bytes > 5242880:
raise ValueError("Validation failed: payloadBytes exceeds allowed limit")
self._tenant_id = tenant_id
self._api_secret = api_secret
self._payload_bytes = payload_bytes
Menghindari Runtime Bypass pada Layer Aplikasi
Meskipun modul validasi telah dikompilasi, integrasi yang ceroboh pada layer framework backend (seperti Express/Fastify di Node.js atau FastAPI/Flask di Python) tetap dapat merusak keamanan. Terapkan prinsip integrasi berikut:
- Jangan Lewatkan Raw JSON Langsung ke State Internal: Jangan menyimpan payload sebelum instansiasi kelas Temper berhasil. Selalu bungkus instansiasi dalam blok
try-catch(JS) atautry-except(Python). - Hindari Manual Mutation: Jangan membuka akses setter mutabel. Di JavaScript, terapkan pembekuan objek atau gunakan hanya getter. Di Python, manfaatkan
@propertyprivat agar nilai yang telah tervalidasi tidak diubah secara implisit di middleware hilir. - Fail Fast di Layer Ingest: Tolak request di boundary HTTP begitu kelas Temper melemparkan error validasi, kembalikan HTTP 400 Bad Request tanpa mengekspos internal stack trace ke klien.
Validasi boundary bukan sekadar memeriksa tipe data, melainkan mengunci invarian state sejak data pertama kali memasuki sistem. Menggunakan Temper memastikan invariant ini dieksekusi secara konsisten tanpa anomali perilaku antar bahasa pemrograman.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!