Skip to main content
BlogSecurity

LiteLLM CVE-2026-42208: Fix SQL Injection dan Hardening Gateway

Bagaimana satu header Authorization tanpa autentikasi bisa mencapai database LiteLLM, isi advisory, Sysdig dan CISA, serta langkah patch, deteksi dan hardening yang perlu dijalankan.

Matthews Wong

Software Engineer

Oktober 202611 menit baca
LiteLLM CVE-2026-42208: Fix SQL Injection dan Hardening Gateway

Ringkasan Utama

CVE-2026-42208 adalah SQL injection kritis di proxy LiteLLM: header Authorization tanpa autentikasi bisa mencapai query database pada versi 1.81.16 hingga sebelum 1.83.7. Upgrade ke 1.83.7 atau lebih baru, anggap instance rentan yang terbuka ke internet sudah terekspos, rotasi virtual key, master key dan kredensial provider, lalu jauhkan gateway dari internet publik.

Pertanyaan pertama yang dipaksakan advisory ini adalah server mana saja yang menjalankan proxy LiteLLM, karena LLM gateway adalah satu-satunya layanan yang menyimpan semua API key upstream dalam satu database. Advisory menggambarkan skenario terburuk untuk desain itu: tanpa login, tanpa route khusus, cukup satu header pada request chat completions biasa.

Tulisan ini membaca CVE-2026-42208 dari sudut pandang defender. Dasarnya adalah advisory GitHub, catatan CIRCL yang memuat data NVD dan CISA, tulisan honeypot Sysdig, research note Cloud Security Alliance, dan dokumentasi production LiteLLM sendiri. Tidak ada payload exploit di sini, hanya yang perlu Anda ketahui untuk patch, deteksi dan memperkecil blast radius. Klaim yang tidak bisa saya baca di sumbernya tidak dimasukkan.

Apa isi advisory sebenarnya

Proxy LiteLLM memeriksa API key pada setiap request. Di versi yang terdampak, sebuah query database yang dipakai saat pemeriksaan itu menaruh nilai key dari pemanggil langsung ke teks query, bukan meneruskannya sebagai parameter terpisah. Penyerang tanpa autentikasi bisa mengirim header Authorization yang dirancang khusus ke route LLM mana pun, advisory menyebut POST /chat/completions, dan mencapai query itu lewat jalur error-handling. Hasilnya akses baca ke database proxy dan mungkin akses tulis, yang menurut advisory bisa berujung pada akses tidak sah ke proxy dan kredensial yang dikelolanya.

Tabel berikut berisi fakta yang bisa saya konfirmasi langsung. Satu sumber berbeda soal batas atas rentang versi, dan saya bahas di bagian trade-off.

FaktaIsi sumberSumber
Versi terdampaklitellm di PyPI, 1.81.16 atau lebih baru dan di bawah 1.83.7GitHub advisory GHSA-r75f-5x8p-qvmc
Versi perbaikan1.83.7 (tag v1.83.7-stable); nilai dari pemanggil kini selalu dikirim sebagai parameter terpisahGitHub advisory
Tingkat keparahanCVSS 4.0 skor 9.3 dan CVSS 3.1 skor 9.8, keduanya Critical, serangan lewat jaringan, tanpa privilege, tanpa interaksi penggunaCatatan CVE di CIRCL
Kelas kelemahanCWE-89, SQL injectionGitHub advisory
Status eksploitasiMasuk katalog CISA KEV pada 2026-05-08 dengan batas waktu 2026-05-11; entri SSVC CISA mencatat eksploitasi aktif dan dampak teknis totalCatatan CIRCL, CISA KEV
KreditTencent YunDing Security LabGitHub advisory

Mengapa satu header bisa sampai ke database

Kelas bug-nya lama dan membosankan, dan justru itu intinya. Sebuah lookup yang seharusnya memakai parameter dibangun dengan menempelkan teks ke SQL, dan teks itu berasal dari satu hal yang sepenuhnya dikendalikan pemanggil tanpa autentikasi, yaitu key yang mereka kirim. Kode autentikasi berjalan sebelum otorisasi apa pun, jadi celah di sana bisa dicapai semua orang. Itu sebabnya vektor CVSS menyebut tanpa privilege dan tanpa interaksi pengguna.

# Generic illustration of the bug class. NOT LiteLLM's source: the
# advisory does not publish it.

# Wrong: the caller's string becomes part of the SQL text, so whatever
# the caller sends is parsed as SQL.
query = f"SELECT * FROM tokens WHERE token = '{presented_key}'"
row = await conn.fetchrow(query)

# Right: the SQL text is fixed, the value travels separately and can
# never be parsed as SQL. This is what the advisory says 1.83.7 now does.
row = await conn.fetchrow(
    "SELECT * FROM tokens WHERE token = $1",
    presented_key,
)

Advisory tidak menerbitkan source yang rentan, jadi potongan kode di bawah adalah ilustrasi generik dari pola dan perbaikannya, bukan kode LiteLLM. Yang dikatakan advisory adalah query itu dicapai lewat jalur error-handling, dan mengatur disable_error_logs ke true di bawah general_settings menutup jalur yang dipakai input tanpa autentikasi.

Release notes 1.83.7 memuat satu perubahan yang cocok dengan deskripsi, yaitu pull request berjudul use parameterized query for combined_view token lookup. Notes itu tidak mengaitkannya dengan CVE, jadi anggap kecocokannya sebagai kemungkinan besar, bukan terkonfirmasi. Release yang sama memuat item security lain, antara lain pemblokiran eksekusi perintah arbitrer lewat transport MCP stdio dan validasi input di endpoint management, jadi upgrade memberi lebih dari satu perbaikan.

Apa yang diincar penyerang, dan blast radius-nya

Sysdig menjalankan honeypot dan mencatat percobaan injection pertama pada 2026-04-26 pukul 04:24 UTC, 36 jam 7 menit setelah advisory terindeks di GitHub Advisory Database. Maintainer sudah menerbitkan advisory mereka pada 2026-04-20, jadi dihitung dari momen itu selisihnya sekitar 127 jam. Kedua angka benar; hanya titik awal jamnya yang berbeda. Sysdig melihat 17 percobaan terhadap tiga tabel, lalu alamat sumber kedua memutar ulang kumpulan payload yang sama 21 menit setelah aktivitas pertama.

TabelIsinyaYang didapat penyerang
LiteLLM_VerificationTokenVirtual API key, termasuk master keyMenyamar sebagai pengguna proxy mana pun, dan akses admin dengan master key
litellm_credentialsKredensial provider upstream yang tersimpan; Sysdig menyebut OpenAI, Anthropic dan BedrockMemanggil provider langsung, melewati spend limit, filter dan audit logging proxy
litellm_configEnvironment variable proxy, yang menurut Sysdig biasanya mencakup connection string PostgreSQL, master key, URL webhook callback dan backend cachePijakan kedua: database itu sendiri, webhook dan cache

Sysdig berhati-hati soal apa yang tidak mereka lihat. Tidak ada aktivitas lanjutan yang terautentikasi, tidak ada key yang dibuat, dan tidak ada kredensial hasil ekstraksi yang dipakai, jadi mereka menyebutnya percobaan ekstraksi, bukan kompromi terkonfirmasi. Itu pernyataan tentang satu honeypot. Itu tidak mengatakan apa pun tentang instance Anda, dan note Cloud Security Alliance menegaskan secara praktis bahwa pembacaan data mungkin tidak meninggalkan entri log di level aplikasi sama sekali.

Note yang sama menyatakan bahwa untuk organisasi yang menyimpan kredensial provider level organisasi atau akun root di proxy, blast radius-nya sebanding dengan kompromi akun cloud penuh. Saya tidak akan menyebutnya lebih kuat dari itu, tetapi juga tidak lebih lemah: gateway adalah penyimpanan secret dengan pintu depan HTTP.

Patch, workaround atau pagar: apa yang dilakukan masing-masing

Hanya upgrade yang memperbaiki celahnya. Opsi lain mengurangi eksposur selama Anda menjadwalkannya, dan note CSA eksplisit bahwa workaround config mengurangi risiko tanpa memperbaiki injection-nya. Tabel ini menyusunnya berdasarkan apa yang benar-benar mereka hentikan.

OpsiMenghentikan injection?BiayaSumber
Upgrade ke 1.83.7 atau lebih baruYa, nilai dikirim sebagai parameter terpisahSatu deploy, plus membaca release notes untuk perubahan lainnyaGitHub advisory, release notes
disable_error_logs: true di bawah general_settingsTidak. Ia menutup jalur yang dipakai input tanpa autentikasi untuk mencapai queryException LLM tidak lagi disimpan di databaseGitHub advisory, note CSA
Reverse proxy yang menolak nilai Authorization janggalBukan celahnya, tetapi memblokir bentuk request yang dikenal sebelum sampai ke aplikasiFilter yang harus Anda jaga tetap benar; allowlist bisa menolak key yang sahMitigasi Sysdig
Pagar jaringan: VPN, API gateway atau listener internal sajaTidak, tetapi orang asing tanpa autentikasi tidak bisa lagi mencapai route-nyaKlien di luar pagar butuh jalur masukSysdig, note CSA

Workaround config tidak sebesar pengorbanan yang terdengar. Halaman production LiteLLM sendiri sudah merekomendasikan disable_error_logs: True agar exception tidak membengkakkan spend log saat provider down, dan menyarankan mengirimnya ke logging stack Anda. Kalau Anda mengikuti halaman itu, workaround ini sudah aktif sebelum CVE ada.

# Read the version the RUNNING process reports, not the one in requirements.txt.
pip show litellm | grep -i '^Version'
docker ps --format '{{.Names}}  {{.Image}}' | grep -i litellm
docker exec <container> python -c "import importlib.metadata as m; print(m.version('litellm'))"

# Upgrade; the advisory's patched range is 1.83.7 and above.
pip install --upgrade "litellm>=1.83.7"

# Bridge only, if you cannot deploy today (config.yaml):
# general_settings:
#   disable_error_logs: true

Nomor versi yang sudah dipatch bukan bukti Anda sudah dipatch. Baca versi yang dilaporkan container yang berjalan, bukan yang tertulis di file requirements, dan pastikan setiap replica sudah restart. Satu worker yang terlupa di image lama membuat pintunya tetap terbuka.

Deteksi yang bisa ditambahkan ke nginx hari ini

Panduan deteksi Sysdig adalah mengawasi request POST ke /chat/completions yang Bearer token-nya diawali prefix berakhiran tanda kutip, menandai nilai Authorization yang berisi keyword SQL atau nama tabel, dan mencatat bahwa user agent Python/3.12 aiohttp/3.9.1 muncul di setiap request injection. Sysdig juga mencantumkan dua alamat sumber, 65.111.27.132 dan 65.111.25.67, keduanya di AS200373. Anggap itu indikator dari satu kampanye, bukan daftar lengkap.

Jangan mencatat header Authorization untuk berburu ini. Itu menulis setiap key yang valid ke file log. Config di bawah hanya menandai boolean, memakai allowlist untuk karakter yang boleh ada di Bearer key. Key asli berupa token polos, jadi apa pun dengan tanda kutip, kurung atau spasi pasti salah. Ini rancangan saya sendiri, dibangun dari mitigasi Sysdig yang memblokir tanda kutip dan kurung. Uji dulu terhadap klien asli Anda.

# /etc/nginx/conf.d/llm-gateway.conf
# An allowlist for what a Bearer key may contain. Anything else, such as a
# quote, a parenthesis or a space inside the token, is flagged.
# Tune the character class to the key formats your own clients send.
map $http_authorization $auth_malformed {
    default                            1;
    ""                                 0;  # no header: the app rejects it
    "~^Bearer [A-Za-z0-9._~+/=-]+$"    0;
}

# Log the verdict, never the header: logging Authorization would write
# every valid key to disk.
log_format llm_sec '$remote_addr [$time_local] "$request" $status '
                   'auth_malformed=$auth_malformed ua="$http_user_agent"';

server {
    listen 443 ssl;
    server_name llm.example.internal;

    location / {
        access_log /var/log/nginx/llm-gateway.log llm_sec;
        if ($auth_malformed) { return 400; }   # refuse before the proxy sees it
        proxy_pass http://127.0.0.1:4000;      # your LiteLLM upstream
    }
}

Lalu cari di riwayat yang sudah Anda punya. Pengecekan retroaktif ini murah, dan Sysdig menyebut satu request yang cocok sebelum patching sebagai indikator dengan tingkat keyakinan tinggi.

# Indicators Sysdig reported for one campaign. zgrep reads plain and .gz logs.
zgrep -hE '65\.111\.(27\.132|25\.67)' /var/log/nginx/access.log*
zgrep -h 'Python/3.12 aiohttp/3.9.1' /var/log/nginx/access.log* | grep 'POST /.*chat/completions'

# Your new guard: every refused request since deployment.
grep 'auth_malformed=1' /var/log/nginx/llm-gateway.log | tail -50

Hardening proxy setelah patch

Patch menghilangkan bug ini, bukan masalah desainnya. Note CSA menyarankan threat modelling tier proxy seperti identity provider atau secrets manager, di lingkungan terisolasi dengan secret store sendiri. Config di bawah mengumpulkan setting yang muncul di referensi production dan config LiteLLM. Saya memverifikasi tiap key terhadap halaman itu, dan komentarnya menjelaskan alasannya.

# config.yaml: keys taken from LiteLLM's production and config references.
general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY   # never a literal in the file
  disable_error_logs: true                     # keeps exceptions out of the DB
  trusted_proxy_ranges: ["10.0.0.0/8"]         # [] if clients connect directly
  database_connection_pool_limit: 10
  # allowed_ips: ["203.0.113.10"]              # Enterprise licence only

litellm_settings:
  request_timeout: 600                         # default is 6000 seconds
  set_verbose: false
  json_logs: true

# Environment, set outside the file:
#   LITELLM_MODE=PRODUCTION        do not auto-load credentials from a local .env
#   LITELLM_LOG=ERROR
#   LITELLM_SALT_KEY=...           never change it after adding models, or
#                                  stored API keys become unreadable
#   DATABASE_URL=postgresql://user:pass@host:5432/litellm?sslmode=verify-full&sslrootcert=/certs/global-bundle.pem

Dua hal lagi berada di luar YAML. Pertama, batasi scope provider key yang disimpan proxy: key level project dengan permission minimum dan spend limit, dan untuk Bedrock sebuah IAM role yang dibatasi ke ARN model tertentu, seperti saran note CSA. Kedua, allowed_ips ada tetapi dokumentasi menandainya sebagai fitur Enterprise, dan tidak menjelaskan bagaimana ia membaca alamat klien di belakang load balancer. Saya lebih memilih pagar jaringan yang Anda kendalikan daripada berasumsi ia bekerja sesuai harapan.

Di mana ini salah: trade-off dan kegagalan yang bernama

Kegagalan yang membuat CVE ini mahal bersifat arsitektural, bukan parameter yang hilang. Gateway yang dibuat untuk menyederhanakan pengelolaan key menjadi satu tempat di mana satu bug tanpa autentikasi menghasilkan semua kredensial provider, dan ia sering berada di alamat publik karena developer di luar VPN membutuhkannya. Rekomendasi Sysdig untuk membuat inventaris deployment AI gateway, termasuk yang dipasang di luar review keamanan formal, ditujukan persis untuk ini.

Log yang bersih tidak berarti bersih. Pembacaan data lewat SQL injection mungkin tidak meninggalkan entri di level aplikasi, jadi untuk instance yang terbuka ke internet dan menjalankan versi terdampak, note CSA menyarankan menganggapnya berpotensi terkompromi dan merotasi semuanya. Patch saja tidak membatalkan key yang sudah tersalin.

Ada jebakan yang lebih kecil. Workaround menyembunyikan visibilitas error di database, jadi rencanakan ke mana exception dialihkan. Batas versi juga berbeda antar sumber: advisory, catatan CVE dan note CSA semuanya menyebut di bawah 1.83.7 terdampak, sementara advisory AhnLab menulis 1.83.7 ke bawah. Saya mengikuti advisory, tetapi langkah aman adalah menjalankan 1.83.7 atau lebih baru dan membaca release notes untuk yang terkini. Dan jam eksploitasi berjalan cepat: CISA memberi lembaga federal tiga hari sejak listing KEV, 2026-05-08 sampai 2026-05-11, untuk menerapkan mitigasi.

Yang dikerjakan hari Senin

Kerjakan berurutan. Dua langkah pertama hanya butuh beberapa menit dan memberi tahu apakah sisanya mendesak.

  1. Buat inventaris setiap proxy LiteLLM yang Anda jalankan atau yang dipasang tim tanpa memberi tahu, dan catat versi yang benar-benar dilaporkan masing-masing.
  2. Upgrade semua yang masuk rentang terdampak ke 1.83.7 atau lebih baru, dan restart setiap replica. Kalau belum bisa, set disable_error_logs ke true dan taruh proxy di belakang pagar hari ini.
  3. Jika instance terdampak pernah terbuka ke internet, rotasi dengan urutan: master key, virtual key, lalu setiap kredensial provider yang tersimpan. Cabut di sisi provider, bukan hanya di UI LiteLLM.
  4. Tambahkan guard nginx, lalu cari di log historis dua alamat kampanye dan user agent yang dilaporkan Sysdig.
  5. Periksa billing dan penggunaan provider untuk traffic yang tidak bisa dijelaskan, terutama yang datang dari luar proxy Anda.

Terakhir, perkecil blast radius berikutnya: key provider yang dibatasi scope-nya, spend cap di sisi provider, dan listener yang tidak berada di internet publik. Patch memberi waktu; arsitektur menentukan biaya bug berikutnya.

Aturan yang saya bawa dari CVE-2026-42208: LLM gateway adalah brankas kredensial yang kebetulan mem-proxy request, jadi patch seperti brankas, pagari seperti brankas, dan anggap apa pun yang disimpannya saat terekspos sudah terbaca. Upgrade ke 1.83.7 atau lebih baru, rotasi saat curiga, dan jadikan workaround jembatan, bukan tujuan akhir.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Versi LiteLLM mana yang terdampak CVE-2026-42208?

GitHub advisory mencantumkan litellm 1.81.16 atau lebih baru dan di bawah 1.83.7 sebagai versi terdampak, dengan 1.83.7 sebagai versi yang sudah dipatch. Satu advisory pihak ketiga menulis batasnya sebagai 1.83.7 ke bawah, jadi langkah aman adalah menjalankan 1.83.7 atau lebih baru dan membaca release notes.

Apakah harus login untuk mengeksploitasi SQL injection LiteLLM ini?

Tidak. Advisory menyebut penyerang tanpa autentikasi bisa mengirim header Authorization yang dirancang khusus ke route LLM seperti POST /chat/completions. Query dicapai lewat jalur error-handling, itulah sebabnya vektor CVSS menyebut tanpa privilege dan tanpa interaksi pengguna.

Apakah disable_error_logs memperbaiki CVE-2026-42208?

Tidak, itu hanya workaround. Mengatur disable_error_logs ke true di bawah general_settings menutup jalur yang dipakai input tanpa autentikasi untuk mencapai query rentan, tetapi note Cloud Security Alliance menyebut ini mengurangi risiko tanpa memperbaiki injection-nya. Upgrade ke 1.83.7 atau lebih baru secepatnya.

Apa yang harus dirotasi kalau proxy LiteLLM saya sempat terekspos?

Rotasi master key, setiap virtual key dan setiap kredensial provider upstream yang tersimpan, dan cabut juga di sisi provider, bukan hanya di LiteLLM. Sysdig melihat penyerang mengincar tabel token, kredensial dan config, dan pembacaan data mungkin tidak meninggalkan entri log aplikasi. Periksa juga billing provider untuk penggunaan yang tidak jelas.

Bagaimana mendeteksi percobaan serangan ke gateway LiteLLM saya?

Tandai request POST ke chat completions yang header Authorization-nya berisi tanda kutip, kurung atau keyword SQL, lalu cari di log historis alamat dan user agent yang dilaporkan Sysdig. Catat flag lolos atau gagal, bukan headernya, supaya key yang valid tidak pernah masuk ke file log.