HTTP Request Framing Desynchronization Mengubah Perbedaan Parser Menjadi Kegagalan Proxy Boundary
Reverse proxy dapat memvalidasi HTTP request, meneruskannya ke origin, tetapi tetap membuat origin memproses urutan request yang berbeda dari yang disetujui proxy. Kegagalan ini bukan akibat hilangnya encryption atau authentication check yang tidak ada. Ia muncul ketika dua recipient mengonsumsi byte pada connection yang sama dengan aturan berbeda untuk menentukan tempat satu message berakhir dan message berikutnya dimulai.
HTTP request smuggling adalah konsekuensi keamanan dari ketidaksepakatan tersebut. Boundary penentunya adalah message framing: jika edge server dan downstream server tidak sepakat mengenai body length, field syntax, atau malformed input, byte yang diperlakukan sebagai body data oleh satu komponen dapat ditafsirkan sebagai request baru oleh komponen berikutnya.
Spesifikasi HTTP modern memuat framing dan error-handling rule eksplisit yang ditujukan untuk mencegah kondisi ini. Risiko deployment tetap ada ketika intermediary melakukan normalization, translation, atau toleransi input secara berbeda dari sistem di belakangnya.
Persistent connection membuat framing state sensitif terhadap keamanan
HTTP/1.1 mengizinkan beberapa request-response exchange pada satu persistent connection. Reuse tersebut bergantung pada setiap recipient mengonsumsi tepat byte yang termasuk current message sebelum mem-parsing request berikutnya.
Untuk request yang membawa content, framing biasanya ditentukan oleh Content-Length atau Transfer-Encoding. RFC 9112 memberi kedua signal ini precedence order yang eksplisit. Message yang berisi kedua field tersebut mencurigakan: Transfer-Encoding mengungguli Content-Length, dan spesifikasi menyatakan message seperti itu dapat mengindikasikan request smuggling. Server boleh menolaknya; jika tetap memproses request, server harus menutup connection setelah mengirim response. Intermediary yang meneruskannya harus menghapus Content-Length yang diterima sebelum meneruskan message ke downstream.
Requirement tersebut ada karena meneruskan representasi ambigu mempertahankan dua interpretasi yang mungkin. Jika front end mengonsumsi chunked body sementara back end mengandalkan conflicting length, parsing position keduanya menyimpang. Byte sisa tidak lagi memiliki satu arti bersama.
Defect keamanannya karena itu bukan sekadar malformed request mencapai aplikasi. Defect-nya adalah connection state berhenti tersinkronisasi antar-recipient.
Kesepakatan parser lebih penting daripada strictness parser secara individual
Sebuah parser dapat konsisten secara internal tetapi tetap terlibat dalam desynchronization ketika peer-nya menerapkan aturan berbeda.
RFC 9112 mengizinkan sebagian recipient mem-parsing komponen request-line pada whitespace-delimited boundary alih-alih hanya menerima bentuk single-space pada grammar, tetapi juga memperingatkan bahwa lenient parsing dapat menciptakan exposure request smuggling ketika beberapa recipient menafsirkan robustness secara berbeda. Risiko serupa muncul pada field syntax, duplicate framing metadata, obsolete form, dan normalization yang dilakukan antar-hop.
Hal ini menciptakan architectural constraint bagi proxy chain: tolerance bukan perilaku lokal. Edge component yang menerima syntax tidak biasa dan menulis ulang menjadi canonical request harus memastikan byte yang ditolak, diabaikan, atau diteruskan tidak dapat memperoleh interpretasi kedua di downstream.
Strict origin di belakang permissive proxy tidak otomatis aman. Strict proxy juga tidak cukup jika translation layer merekonstruksi ambiguous HTTP/1.1 message untuk backend. Properti yang relevan adalah framing deterministik di sepanjang seluruh path.
Conflicting length signal adalah protocol error, bukan routing hint
Content-Length memiliki arti framing langsung dalam HTTP/1.1. RFC 9110 mendefinisikannya sebagai jumlah octet dalam bentuk desimal, dan RFC 9112 memakai value yang valid untuk menentukan request body length ketika Transfer-Encoding tidak ada.
Transfer-Encoding mengubah aturan tersebut. Ketika chunked transfer coding adalah coding terakhir, chunk structure menentukan boundary body. Sender tidak boleh mengirim Content-Length pada message yang juga memiliki Transfer-Encoding.
Memperlakukan kedua field sebagai harmless metadata membuka peluang divergent parsing. Satu komponen mungkin mengikuti required precedence sementara implementation, adapter, atau legacy service lain justru memprioritaskan numeric length. Proxy yang menghapus salah satu field hanya setelah membuat routing atau security decision juga dapat menciptakan mismatch antara message yang diperiksa dan message yang diterima origin.
Boundary yang lebih aman adalah rejection atau standards-conforming canonicalization sebelum downstream reuse. Canonicalization harus menghasilkan satu message yang unambiguous dan membuang conflicting framing state, bukan hanya memilih preferred value sambil mempertahankan contradictory input.
HTTP version translation menciptakan parser boundary lain
HTTP/2 tidak menentukan boundary message memakai HTTP/1.1 chunk syntax. Message content dibawa di dalam frame, dan RFC 9113 mendefinisikan message sebagai malformed ketika supplied content-length tidak sama dengan jumlah payload DATA frame untuk message yang membawa content.
Gateway yang menerima HTTP/2 dari client lalu berbicara HTTP/1.1 ke origin harus menerjemahkan kedua framing model tersebut. Translation ini adalah security boundary karena gateway membangun downstream request syntax.
Keberadaan binary framing pada client-facing hop tidak menghilangkan risiko desynchronization dari deployment. Jika gateway memetakan malformed atau ambiguous HTTP/2 field menjadi representasi HTTP/1.1 yang di-parsing backend secara berbeda, ketidaksepakatan hanya berpindah ke translation point.
Prinsip yang sama berlaku pada protocol bridge mana pun: downstream message yang dihasilkan harus memiliki framing semantics yang valid untuk hop tersebut, independen dari syntax yang diterima upstream.
Connection reuse memperbesar satu ketidaksepakatan
Framing discrepancy menjadi sangat penting ketika backend connection digunakan ulang untuk request dari client berbeda.
Reverse proxy umumnya mempertahankan pool persistent connection ke origin. Jika origin berhenti mem-parsing sebuah request pada byte offset yang berbeda dari proxy, residual byte dapat tertinggal di connection stream. Byte berikutnya yang dikirim proxy kemudian dapat digabung dengan residual state tersebut dari perspektif origin.
Hal ini dapat melintasi boundary request dan user. Konsekuensi tepatnya bergantung pada routing, authentication placement, connection pooling, application behavior, dan parser discrepancy yang terlibat. Efek yang mungkin termasuk downstream request melewati edge decision, request berikutnya berubah, atau response diasosiasikan dengan urutan request yang tidak diharapkan.
Tidak satu pun hasil tersebut boleh diasumsikan hanya karena ada dua proxy. Exploitability memerlukan parsing disagreement konkret dan connection-state effect yang dapat digunakan. Architectural risk berasal dari shared state ditambah inconsistent framing, bukan dari proxying itu sendiri.
Edge authorization tidak dapat melindungi byte yang diparsing secara berbeda
Banyak deployment menempatkan security control sebelum aplikasi: authentication, path filtering, request-size limit, web application firewall, atau tenant routing. Control tersebut bekerja pada representasi request milik edge parser.
Jika downstream parsing menghasilkan request boundary lain, origin dapat menerima semantics yang tidak pernah dievaluasi edge control sebagai independent request. Ini adalah general trust-boundary failure. Policy decision hanya valid untuk message representation tempat policy itu diterapkan.
Karena itu request normalization sebaiknya mendahului security decision ketika normalization dapat mengubah semantics, dan downstream emission harus mempertahankan normalized interpretation yang sama. Komponen sebaiknya menolak syntax yang tidak dapat direpresentasikan tanpa ambiguity alih-alih mencoba compatibility transformation yang efeknya berbeda antar-peer.
Origin juga mendapat manfaat dari authorization control-nya sendiri. Defense pada application layer tidak memperbaiki parser disagreement, tetapi mengurangi ketergantungan pada asumsi bahwa setiap request yang mencapai backend sudah melewati edge decision yang ekuivalen.
Menutup connection adalah bagian dari error containment
Connection closure dapat menjadi tindakan keamanan ketika framing tidak pasti.
RFC 9112 mewajibkan connection closure setelah server merespons request yang memuat Transfer-Encoding dan Content-Length, bahkan ketika server memilih tetap memproses request tersebut. RFC yang sama juga mewajibkan closure pada beberapa framing fault ketika buffered byte dapat keliru dianggap sebagai message berikutnya.
Tujuan operasionalnya adalah membuang ambiguous stream state. Melanjutkan reuse sebuah connection setelah framing error mengasumsikan setiap participant sepakat mengenai byte offset tempat parsing dapat dimulai kembali dengan aman, padahal justru properti itulah yang diragukan.
Hal ini berbeda dari ordinary malformed-field handling. Recipient kadang dapat menolak satu request lalu melanjutkan dengan aman ketika parser memiliki boundary yang unambiguous. Framing fault dapat mencemari connection itu sendiri, sehingga error recovery harus mempertimbangkan stream position, bukan hanya HTTP status.
Backend topology memengaruhi exposure
Direct application server dan layered ingress path tidak memiliki desynchronization surface yang sama.
Setiap HTTP-speaking intermediary tambahan memperkenalkan parser, normalization policy, dan kemungkinan protocol translation lain. CDN, load balancer, service mesh, API gateway, reverse proxy, dan application server semuanya dapat benar secara individual tetapi kombinasi yang tidak didukung dapat mengekspos disagreement.
Ini tidak berarti jumlah layer lebih sedikit selalu lebih aman. Intermediary yang matang dapat memberlakukan strict syntax dan melindungi origin yang kurang robust. Security property berasal dari compatible behavior, controlled translation, dan failure handling yang diuji di seluruh chain yang nyata.
Operational inventory karena itu sebaiknya mencatat protocol version pada setiap hop, connection reuse policy, framing normalization, dan komponen yang bertanggung jawab menolak malformed request. Architecture diagram yang hanya memberi label HTTP pada setiap link menyembunyikan perbedaan yang penting untuk failure mode ini.
Testing harus mempertahankan parser chain yang sebenarnya
Framework-level test sering langsung mengirim parsed request object ke application code. Test seperti ini tidak dapat menguji byte-level framing disagreement karena HTTP parser sudah selesai bekerja.
Security validation untuk desynchronization berada pada interface yang mempertahankan production path: edge listener, protocol translator, backend connection pooling, dan origin parser. Tujuannya bukan menyemprot malformed traffic ke sistem arbitrer. Tujuannya adalah memastikan unsupported atau conflicting framing ditolak secara konsisten dan error path tidak meninggalkan reusable connection dalam state yang tidak pasti.
Production telemetry juga dapat memperlihatkan symptom seperti response 400 yang abnormal, backend connection reset, malformed-request counter, atau mismatch antara edge dan origin request log. Signal ini bersifat diagnostic, bukan bukti serangan; ordinary client bug dan interoperability fault dapat menghasilkan event serupa.
Satu byte stream membutuhkan satu urutan request
Request smuggling sering dijelaskan melalui kombinasi header tertentu, tetapi kombinasi tersebut hanyalah instance dari invariant yang lebih luas. Setiap recipient yang berbagi persistent HTTP path harus sepakat mengenai urutan message yang direpresentasikan oleh byte yang melintasinya.
Standards-conforming framing rule mengurangi ambiguity, strict rejection membatasi parser differential, dan connection closure membatasi state setelah framing fault. Protocol translation harus mempertahankan invariant yang sama ketika wire representation berubah antar-hop.
Security boundary gagal ketika intermediary menyetujui satu urutan message sementara downstream recipient mengeksekusi urutan lain. Melindungi boundary tersebut bergantung pada parser agreement di sepanjang chain, bukan pada apparent validity satu komponen secara terpisah.