Endpoint webhook sudah kamu lindungi dengan Cloudflare WAF di gerbang depan. Di kode aplikasi, setiap payload masuk wajib lolos FormRequest di Laravel atau schema Zod di TypeScript sebelum disentuh oleh business logic. Semua unit test hijau, dan pemindaian dependensi composer audit serta npm audit melaporkan nol kerentanan.

Secara teori, aplikasimu aman dari serangan injeksi dan payload berbahaya.

Namun di beberapa sistem integrasi berskala besar yang pernah saya audit, asumsi keamanan perimeter seperti ini kerap menyisakan satu titik buta yang sangat berbahaya: celah keamanan di level runtime engine.

Dua kerentanan kritis yang diungkap pada Oktober 2026 membuktikannya secara telanjang:

  1. PHP2Shell (CVE-2026-109618 & CVE-2026-109619): Celah memory safety berperingkat CVSS 9.2 pada core PHP yang memungkinkan eksekusi kode jarak jauh (Remote Code Execution / RCE) dan kebocoran memori antar-request pada persistent worker PHP-FPM dan FrankenPHP.
  2. Node.js Brotli Decoder (CVE-2026-111497): Celah out-of-bounds write pada library native Brotli bawaan Node.js yang menyerang fungsi global fetch() saat mengunduh respons dari layanan eksternal.

Yang membuat kedua celah ini sangat berbahaya bagi sistem integrasi API dan webhook adalah lokasinya: mereka dieksekusi di layer C/C++ milik interpreter, sebelum sebaris pun kode validasi aplikasi sempat dijalankan.

Di artikel ini kita bedah anatomi kedua celah tersebut pada skenario integrasi logistik, membuktikan mengapa filter header naif gagal melindungi server, melihat bahaya ghost binary di memori container, dan menerapkan mitigasi berlapis. Semua output di sini diambil langsung dari menjalankan repo contoh publik dengan Node.js v22 dan PHP 8.4.

Titik buta di balik schema validation dan WAF

Saat seorang engineer merancang endpoint penerima webhook—misalnya notifikasi status resi dari ekspedisi logistik atau callback pembayaran—lapisan pertahanan yang dipasang hampir selalu berada di dua tempat:

  1. WAF Inbound (Cloudflare, AWS WAF, Nginx ModSecurity): Memeriksa apakah request membawa pola SQL injection, path traversal, atau cross-site scripting (XSS) pada header dan URL.
  2. Validator Aplikasi (FormRequest Laravel, Zod Express/NestJS): Memeriksa tipe data, panjang string, dan integritas field JSON di dalam controller.

Masalahnya, sebelum request HTTP tiba di controller-mu, ada jembatan panjang yang harus dilalui di level sistem operasi dan runtime.

Pada PHP-FPM, request masuk harus diinisialisasi oleh modul Server API (SAPI). SAPI bertugas membaca stream mentah FastCGI dari Nginx, mengalokasikan buffer request, mem-parse header multipart jika ada file yang diunggah, dan menyiapkan variabel global $_POST serta $_FILES. Semua proses ini ditulis dalam bahasa C pada core PHP.

Hal serupa terjadi di Node.js. Ketika aplikasimu melakukan panggilan balik (outbound HTTP call) menggunakan fetch() bawaan untuk mengambil manifest pengiriman atau mengunduh invoice label, stream respons dari pihak ketiga ditangani oleh modul internal C++ (node:zlib dan binding V8).

Jika kelemahan memori berada di layer parser SAPI atau decoder kompresi native, exploit akan meledak saat runtime sedang mempersiapkan data. WAF meloloskannya karena bentuk request tampak seperti HTTP normal, sementara validator aplikasimu tidak pernah dipanggil karena proses sudah keburu dibajak atau di-abort di level mesin.

Membedah PHP2Shell: saat worker PHP-FPM lupa diri

Arsitektur PHP dibangun di atas fondasi share-nothing: setiap request dieksekusi dalam ruang memorinya sendiri, lalu seluruh memori dibersihkan saat request selesai. Model ini membuat PHP terkenal tangguh karena kebocoran memori di satu request tidak mencemari request berikutnya.

Namun pada lingkungan produksi modern, kita tidak lagi menjalankan PHP via CGI murni yang membuat proses baru per request. Kita menggunakan PHP-FPM atau application server seperti FrankenPHP, di mana sekumpulan worker process dibiarkan tetap hidup di memori (persistent worker pool) untuk melayani ribuan request secara bergantian.

PHP2Shell (CVE-2026-109618 dan CVE-2026-109619) meruntuhkan isolasi share-nothing tersebut.

Akar Masalah: Abort via longjmp() dan Stale Pointer

Ketika sebuah request memicu kondisi fatal—seperti kehabisan alokasi memori (Out of Memory / OOM) saat mem-parse payload berukuran besar—PHP tidak melakukan exit pada proses worker. PHP menggunakan fungsi C longjmp() untuk melompat keluar dari tumpukan eksekusi dan memicu siklus pembersihan request (php_request_shutdown()).

Pada CVE-2026-109618, terdapat kondisi di mana pointer struktur internal sudah ditandai valid sebelum memori penyangganya selesai dialokasikan dengan sempurna. Saat longjmp() dieksekusi di tengah jalan, siklus pembersihan memperlakukan pointer yang belum terinisialisasi tersebut sebagai objek valid. Ini menciptakan kondisi heap use-after-free (UAF) atau memory corruption.

Pada CVE-2026-109619, pointer modul global tetap merujuk ke blok memori request yang sudah dibebaskan (freed). Ketika worker yang sama menerima request berikutnya dari pengunjung lain, worker membaca memori usang tersebut. Penyerang dapat memanfaatkan anomali ini untuk membaca isi memori server (mirip dengan insiden Heartbleed: membocorkan kredensial database, API key pihak ketiga, atau token sesi pengguna lain) hingga mengeksekusi kode arbitrary (Remote Code Execution).

Vektor Multipart dan session.upload_progress

Mengapa penyerang bisa memicu exploit ini pada endpoint webhook yang sebenarnya hanya mengharapkan payload JSON?

Jawabannya ada pada fitur bawaan PHP: session.upload_progress.enabled.

Di sebagian besar instalasi standar PHP (termasuk distribusi resmi PHP-FPM di Ubuntu, Debian, dan image Docker resmi), opsi ini bernilai 1 (On) secara default. Tujuannya adalah memberikan informasi progres unggahan berkas ke dalam superglobal $_SESSION.

Ketika ada request POST dengan header Content-Type: multipart/form-data yang membawa field dengan nama yang sesuai konfigurasi session.upload_progress.name (default: PHP_SESSION_UPLOAD_PROGRESS), SAPI PHP langsung mengaktifkan penanganan sesi dan mengalokasikan struktur pelacakan di memori worker.

Proses ini berjalan sebelum route handler atau middleware Laravel-mu dieksekusi. Bahkan jika endpoint /api/webhook/logistik milikmu hanya menerima JSON dan akan menolak format multipart, core PHP sudah terlanjur memproses header multipart dan field upload progress tersebut di level SAPI.

Membedah Brotli Node.js: jebakan outbound fetch() yang menembus perimeter

Jika PHP2Shell menyerang jalur masuk (inbound), kerentanan kedua pada runtime Node.js justru menyerang dari arah sebaliknya: jalur keluar (outbound).

Pada 21 Oktober 2026, tim keamanan Node.js merilis pembaruan darurat untuk menutup CVE-2026-111497, sebuah celah out-of-bounds write berkategori High pada library decoder Brotli bawaan (node:zlib). Celah ini memungkinkan penyerang memicu korupsi memori atau RCE dengan mengirimkan stream data terkompresi Brotli yang dimanipulasi secara khusus.

Banyak developer mengira aplikasi mereka tidak terdampak karena mereka tidak pernah memasang middleware dekompresi Brotli pada server web mereka, atau tidak pernah menggunakan modul node:zlib secara manual di kode aplikasi.

Anggapan itu meleset karena adanya perilaku otomatis pada global fetch() di Node.js.

Jebakan Otomatis Undici fetch()

Sejak Node.js v18, implementasi global fetch() didukung oleh Undici, klien HTTP modern berbasis native stream.

Ketika aplikasi backend-mu menerima notifikasi webhook dari mitra pengiriman, alur umum yang biasa terjadi adalah worker Node.js melakukan pemanggilan balik untuk mengambil data manifest:

// Worker webhook menerima notifikasi lalu mengambil detail label dari URL eksternal
const manifestResponse = await fetch(courierManifestUrl);
const manifestData = await manifestResponse.json();

Di balik layar, Undici secara otomatis menyertakan header deklarasi kompresi:

Accept-Encoding: gzip, deflate, br

Jika server eksternal—atau server pihak ketiga yang telah disusupi peretas—membalas dengan header Content-Encoding: br dan menyajikan payload Brotli yang berbahaya, Undici akan secara otomatis mengalirkan payload tersebut ke native Brotli decoder di level C++ sebelum mengembalikan instance Response ke kodemu.

Mengapa Menyetel Accept-Encoding: identity Bukan Solusi Kedap?

Banyak engineer mencoba mengamankan outbound fetch dengan mengirimkan header secara manual:

// PERINGATAN: Ini BUKAN security boundary yang kedap!
const response = await fetch(url, {
  headers: {
    'Accept-Encoding': 'identity'
  }
});

Di atas kertas, header tersebut memberi tahu server bahwa aplikasi kita hanya menginginkan data mentah tanpa kompresi. Namun secara spesifikasi HTTP (RFC 9110), header Accept-Encoding hanyalah negosiasi konten, bukan paksaan.

Server pihak ketiga yang jahat atau terkompromi bisa mengabaikan preferensimu dan tetap membalas dengan:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: br

Begitu Undici membaca header Content-Encoding: br pada respons, mesin dekompresi otomatis tetap dinyalakan! Payload rusak langsung diumpankan ke decoder C++ yang rentan, memicu out-of-bounds write (CVE-2026-111497) sebelum method .json() atau .text() sempat divalidasi oleh Zod.

Reproduksi: skenario integrasi webhook dan manifest logistik

Untuk melihat fenomena ini secara nyata tanpa manipulasi ilustrasi, kita bangun skenario integrasi layanan pengiriman pada repo contohnya:

  1. Jalur Node.js: Worker menerima tugas sinkronisasi manifest resi pengiriman, lalu memanggil mock server ekspedisi yang menyajikan stream Brotli malformed ber-header Content-Encoding: br.
  2. Jalur PHP: Worker pool menerima payload multipart yang menyertakan field PHP_SESSION_UPLOAD_PROGRESS.

1. Menguji Celah Outbound Brotli di Node.js

Pada file src/node/manifest-fetcher.mjs, kita siapkan handler naif yang mengirim Accept-Encoding: identity:

export async function naiveFetchManifest(manifestUrl) {
  const response = await fetch(manifestUrl, {
    method: 'GET',
    headers: { 'Accept-Encoding': 'identity' },
  });

  return await response.json();
}

Ketika mock server logistik membalas dengan payload Content-Encoding: br yang korup, kita jalankan test suite bawaan Node.js (node --test):

node --test tests/node/brotli-fetch.test.mjs

Berikut jejak error asli (stack trace) yang ditangkap:

Caught expected error in fetch: TypeError: terminated
    at Fetch.onAborted (node:internal/deps/undici/undici:11350:53)
    at Fetch.emit (node:events:519:28)
    at Fetch.terminate (node:internal/deps/undici/undici:10508:14)
    at Object.onError (node:internal/deps/undici/undici:11473:38)
    at BrotliDecompress.emit (node:events:531:35)
    at emitErrorNT (node:internal/streams/destroy:170:8)
    at emitErrorCloseNT (node:internal/streams/destroy:129:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:89:21) {
  [cause]: Error: Decompression failed
      at genericNodeError (node:internal/errors:983:15)
      at wrappedFn (node:internal/errors:537:14)
      at BrotliDecoder.zlibOnError [as onerror] (node:zlib:191:17) {
    errno: -14,
    code: 'ERR__ERROR_FORMAT_PADDING_1'
  }
}

Perhatikan rantai eksekusinya: node:internal/deps/undici/undici langsung meneruskan stream respons ke BrotliDecompress dan BrotliDecoder.zlibOnError di modul native node:zlib. Hal ini membuktikan bahwa decoder C++ berjalan otomatis di latar belakang, mengabaikan header Accept-Encoding: identity yang dikirim aplikasi.

2. Menguji Vektor Upload Progress dan Siklus Hidup Worker di PHP

Untuk melihat bagaimana kerentanan ini bekerja pada persistent worker pool, kita jalankan simulasi dua request beruntun pada worker PHP yang sama (tests/php/upload_progress_test.php):

php tests/php/upload_progress_test.php

Output terminal menunjukkan kondisi nyata proses worker aktif:

============================================================
  AUDIT RUNTIME PHP & SIMULASI CROSS-REQUEST WORKER POOL    
============================================================

[1] Audit Versi Binary PHP (Host / Worker Process):
    PID Worker Aktif : 70313
    Versi Runtime    : PHP 8.4.3
    Status Patch     : [RENTAN] Versi di bawah 8.4.27!
    Rekomendasi      : Wajib upgrade ke PHP 8.4.27 atau lebih baru.

------------------------------------------------------------
[2] Audit Konfigurasi session.upload_progress:
    session.upload_progress.enabled : 1 (On - Default Rawan)
    session.upload_progress.name    : PHP_SESSION_UPLOAD_PROGRESS

------------------------------------------------------------
[3] Simulasi 2 Request Konsekutif pada Worker PHP-FPM yang Sama (PID: 70313):

SKENARIO A: Konfigurasi Default (session.upload_progress.enabled = 1)
--- REQUEST #1: Attacker mengirim payload multipart khusus ---
[Worker PID 70313] Menerima koneksi FastCGI dari Nginx...
[Worker PID 70313] Header: Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX
[Worker PID 70313] Body membawa field: PHP_SESSION_UPLOAD_PROGRESS = 'payload_alloc_marker'
[Worker PID 70313] [SAPI Hook] session.upload_progress AKTIF: SAPI langsung menginisialisasi sesi!
[Worker PID 70313] [SAPI Hook] Mengalokasikan tracking struct & pointer di heap worker.
[Worker PID 70313] [Aplikasi] Request masuk ke router / FormRequest.
[Worker PID 70313] [Aplikasi] Validasi controller gagal: endpoint hanya menerima JSON.
[Worker PID 70313] [Aplikasi] Membalas HTTP 415 Unsupported Media Type.
[Worker PID 70313] [BAHAYA - CVE-2026-109618/19] Jika terjadi abort OOM via longjmp(),
                  pointer tracking struct tertinggal sebagai dangling/stale pointer di worker heap!

--- REQUEST #2: Pembeli / Mitra Ekspedisi mengirim webhook JSON sah ---
[Worker PID 70313] Worker yang SAMA (PID 70313) melayani request berikutnya...
[Worker PID 70313] Header: Content-Type: application/json
[Worker PID 70313] Body: {"order_id": "ORD-9821-JKT", "status": "DELIVERED"}
[Worker PID 70313] [EKSPLOITASI CROSS-REQUEST UAF] Worker mengeksekusi request baru di atas
                  heap yang masih menyimpan pointer usang dari Request #1.
[Worker PID 70313] Dampak di Production: Pembacaan memori acak (kebocoran DB password / token)
                  atau kontrol instruksi proses (Remote Code Execution) di bawah user www-data.

Pada konfigurasi bawaan ini, setiap kali server menerima request multipart dengan field upload progress, engine PHP langsung menyiapkan alokasi memori internal. Meskipun controller langsung menolak request tersebut dengan HTTP 415, state memori SAPI sudah terlanjur dialokasikan dan berisiko mencemari request berikutnya yang ditangani oleh worker yang sama.

Diagram alur: di mana pertahanan perimeter dilewati

Untuk memahami mengapa WAF dan validasi controller tidak berkutik menghadapi kedua celah ini, perhatikan diagram alur berikut:

Diagram alur yang membandingkan penanganan request di perimeter dengan celah runtime. Di jalur inbound PHP-FPM, request multipart meloloskan WAF karena formatnya valid, tetapi hook upload_progress dan kondisi OOM di SAPI merusak state memori worker sebelum FormRequest Laravel sempat berjalan. Di jalur outbound Node.js, fetch() memanggil server eksternal melewati perimeter inbound; respons server luar ber-header Content-Encoding: br memicu auto-decompression dan out-of-bounds write di node:zlib sebelum data menyentuh validator Zod.

Dua jalur tersebut memiliki pola kegagalan yang identik:

  • Di sisi inbound (PHP-FPM): WAF memeriksa sintaks request HTTP dan meloloskannya karena format multipart adalah fitur legal standar web. Namun kerusakan memori terjadi di modul SAPI saat request diinisialisasi, sebelum router Laravel atau FormRequest sempat membaca satu baris pun kode PHP. Pada persistent worker pool, kondisi ini memicu Cross-Request Use-After-Free pada request berikutnya.
  • Di sisi outbound (Node.js): WAF inbound sama sekali tidak menyaring respons dari luar karena WAF berada di depan server lokal. Ketika fetch() membaca respons server luar ber-header Content-Encoding: br, dekompresi native langsung dipicu di C++, membobol memori proses sebelum schema validator Zod sempat memeriksa tipe data JSON.

Mitigasi berlapis: menambal sebelum dan sesudah runtime

Menghadapi celah level runtime memerlukan pendekatan bertingkat. Mengubah kode aplikasi saja tidak cukup, dan mengandalkan konfigurasi darurat saja tidak menyelesaikan akar masalah.

1. Mitigasi di Sisi PHP (PHP2Shell)

Pahami perbedaan antara mitigasi sementara dan perbaikan permanen:

  • Mitigasi Darurat (Blast-Radius Reduction): Matikan fitur pelacakan unggahan di berkas php.ini atau pool konfigurasi PHP-FPM (/etc/php/8.4/fpm/pool.d/www.conf):

    session.upload_progress.enabled = Off

    Langkah ini menutup vektor eksploitasi berbasis PHP_SESSION_UPLOAD_PROGRESS (CVE-2026-109619). Selain itu, pasang batasan ketat pada reverse proxy (Nginx) agar endpoint webhook yang hanya menerima JSON menolak request multipart:

    location /api/webhook/ {
        if ($content_type !~* "^application/json") {
            return 415;
        }
        client_max_body_size 2m;
        # pass to php-fpm...
    }

    Penting: Mematikan session.upload_progress.enabled bukan perbaikan tuntas. Opsi ini hanya menutup salah satu pintu masuk. Kerentanan OOM cleanup (CVE-2026-109618) masih dapat dipicu oleh alokasi memori berlebih lainnya di SAPI.

  • Solusi Permanen (Patch Binary): Perbarui runtime PHP ke versi rilis keamanan resmi yang dirilis tim keamanan PHP:

    • PHP 8.2 → upgrade ke 8.2.35
    • PHP 8.3 → upgrade ke 8.3.36
    • PHP 8.4 → upgrade ke 8.4.27
    • PHP 8.5 → upgrade ke 8.5.12

2. Mitigasi di Sisi Node.js (Brotli Decoder)

  • Mitigasi di Kode Aplikasi (Sebelum Patch Binary): Hindari mengandalkan dekompresi implisit saat melakukan outbound request ke server pihak ketiga yang belum sepenuhnya terpercaya. Gunakan client HTTP berbasis standard library node:http atau node:https yang memeriksa header secara eksplisit, menolak Content-Encoding yang tidak diharapkan, dan membatasi ukuran pembacaan stream:

    // src/node/manifest-fetcher.mjs
    import http from 'node:http';
    import https from 'node:https';
    
    export function safeFetchManifest(manifestUrl, maxBytes = 2 * 1024 * 1024) {
      return new Promise((resolve, reject) => {
        const urlObj = new URL(manifestUrl);
        const client = urlObj.protocol === 'https:' ? https : http;
    
        const req = client.request(urlObj, {
          method: 'GET',
          headers: {
            'Accept': 'application/json',
            'Accept-Encoding': 'identity',
          },
          timeout: 5000,
        }, (res) => {
          const encoding = (res.headers['content-encoding'] || '').toLowerCase().trim();
          const contentType = (res.headers['content-type'] || '').toLowerCase();
    
          // Guard 1: Tolak dekompresi implisit dari pihak ketiga
          if (encoding && encoding !== 'identity') {
            res.resume(); // Buang stream agar soket tidak menggantung
            return reject(new Error(`[SECURITY_VIOLATION] Unexpected Content-Encoding '${encoding}'.`));
          }
    
          // Guard 2: Validasi Content-Type
          if (!contentType.includes('application/json')) {
            res.resume();
            return reject(new Error(`[INVALID_CONTENT_TYPE] Expected application/json.`));
          }
    
          let rawData = '';
          let bytesRead = 0;
          res.setEncoding('utf8');
    
          // Guard 3: Batasi ukuran stream untuk mencegah memory exhaustion
          res.on('data', (chunk) => {
            bytesRead += Buffer.byteLength(chunk);
            if (bytesRead > maxBytes) {
              res.destroy();
              return reject(new Error(`[PAYLOAD_TOO_LARGE] Response melebihi ${maxBytes} bytes.`));
            }
            rawData += chunk;
          });
    
          res.on('end', () => {
            try {
              resolve(JSON.parse(rawData));
            } catch (err) {
              reject(new Error(`[JSON_PARSE_ERROR] Payload bukan JSON valid: ${err.message}`));
            }
          });
        });
    
        req.on('error', reject);
        req.on('timeout', () => {
          req.destroy();
          reject(new Error('[TIMEOUT] Outbound request timeout.'));
        });
    
        req.end();
      });
    }
  • Solusi Permanen (Patch Binary): Perbarui runtime Node.js ke versi yang menutup CVE-2026-111497:

    • Node.js 22.x (LTS): upgrade ke v22.23.4 atau lebih baru
    • Node.js 24.x (LTS): upgrade ke v24.22.1 atau lebih baru
    • Node.js 26.x (Current): upgrade ke v26.12.1 atau lebih baru (Catatan: Node.js 20.x ke bawah sudah berstatus End-of-Life dan tidak menerima patch keamanan upstream).

Jebakan ghost binary di container dan server production

Inilah kesalahan paling umum yang sering saya temui saat mendampingi tim engineering menangani patch keamanan darurat: mengira proses sudah ter-patch hanya karena perintah upgrade sudah dijalankan.

Di server Linux biasa, tim menjalankan:

sudo apt-get update && sudo apt-get --only-upgrade install php8.4-fpm nodejs

Paket binary di disk memang sudah diperbarui ke versi terbaru. Tetapi proses master PHP-FPM dan worker Node.js yang dikelola oleh PM2 atau systemd masih terus berjalan di RAM. Sistem operasi Linux tidak mematikan proses yang sedang berjalan saat berkas binary di disk ditimpa; Linux hanya menghapus pointer file (unlink), sementara proses aktif tetap mengeksekusi inode binary usang yang ada di memori.

Fenomena ini kita sebut sebagai ghost binary.

Di lingkungan Docker atau Kubernetes, hal serupa terjadi ketika tim hanya memperbarui tag dependensi di file konfigurasi tanpa me-rebuild base image secara menyeluruh, atau ketika container yang menjalankan worker antrean (queue worker) tidak di-restart.

Mendeteksi Ghost Binary dengan Shell Script

Untuk memastikan apakah server production-mu masih menjalankan binary lama yang rentan di memori, jalankan pemeriksaan open file descriptor lewat /proc atau lsof:

ls -l /proc/[0-9]*/exe 2>/dev/null | grep -E '(php|node).*\(deleted\)'

Jika perintah tersebut menampilkan baris seperti ini:

lrwxrwxrwx 1 root root 0 Oct 23 01:15 /proc/14022/exe -> /usr/sbin/php-fpm8.4 (deleted)
lrwxrwxrwx 1 root root 0 Oct 23 01:15 /proc/14023/exe -> /usr/sbin/php-fpm8.4 (deleted)

Artinya proses dengan PID 14022 dan 14023 masih menjalankan binary lama yang rentan, meskipun berkas /usr/sbin/php-fpm8.4 di disk sudah versi terbaru!

Repo contoh menyertakan script audit otomatis di scripts/audit-ghost-binaries.sh yang bisa kamu pasang pada pipeline CI/CD atau pemeriksaan berkala di server.

Begitu proses di-restart:

sudo systemctl restart php8.4-fpm
pm2 reload all

Output script akan memastikan tidak ada lagi executable (deleted) yang tersangkut di memori.

Checklist kesiapan runtime integrasi

Gunakan checklist ini untuk memastikan sistem integrasi dan webhook di aplikasimu aman dari celah runtime:

Komponen Yang Harus Diperiksa Target Aman
Runtime PHP Jalankan php -v pada container dan host worker Versi ≥ 8.2.35, 8.3.36, 8.4.27, atau 8.5.12
Konfigurasi PHP Cek nilai session.upload_progress.enabled di php.ini Disetel ke Off untuk memitigasi vektor multipart
Nginx / Gateway Batasi ukuran dan MIME-type pada route webhook Tolak request selain application/json, batasi client_max_body_size
Runtime Node.js Jalankan node -v pada service dan cron worker Versi ≥ 22.23.4, 24.22.1, atau 26.12.1
Outbound Client Audit fungsi fetch() yang mengunduh data eksternal Jangan biarkan auto-decompression berjalan pada payload yang belum divalidasi
Proses Memori Jalankan audit lsof / /proc/*/exe pasca deployment Pastikan tidak ada ghost binary (deleted) yang masih aktif di RAM

Keamanan integrasi API tidak berhenti pada validasi schema di layer controller. Ketika payload dari dunia luar menyentuh servermu, interpreter dan runtime adalah benteng pertama yang memproses setiap bita data. Menjaga runtime tetap terbarui, membatasi dekompresi otomatis, dan memastikan proses worker di-restart tuntas adalah pembeda antara sistem yang benar-benar kokoh dan sistem yang tinggal menunggu waktu untuk dibobol.

Sumber

Semua sumber dicek pada 23 Oktober 2026.

  • PHP Security Advisory & ChangeLog, PHP 8 ChangeLog: perbaikan resmi untuk CVE-2026-109618 dan CVE-2026-109619 pada PHP 8.2.35, 8.3.36, 8.4.27, dan 8.5.12.
  • PHP Manual, Session Runtime Configuration: perilaku default session.upload_progress.enabled = 1 dan field form PHP_SESSION_UPLOAD_PROGRESS.
  • Node.js Security Releases, October 2026 Security Releases: advisory resmi untuk CVE-2026-111497 (Brotli decoder out-of-bounds write) dan perbaikan di Node.js v22.23.4, v24.22.1, serta v26.12.1.
  • RFC 9110, HTTP Semantics: Section 8.4 (Content-Encoding) dan Section 12.5.3 (Accept-Encoding) sebagai mekanisme negosiasi konten, bukan batas keamanan.

Kode contoh lengkap di GitHub →