Jawaban langsung: Craft CMS 4.0.0 sampai 4.18.8 dan 5.0.0 sampai 5.11.1 punya SQL injection tanpa login, CVE-2026-123488. Penyerang bisa menjadikan akun mana pun admin. Hari ini, cek versi, lalu update ke 5.11.5.1 atau 4.18.9.
VulnCheck memberi CVSS 3.1 9.8 dan CVSS 4.0 9.3. Patch sudah keluar 17 September 2026, 50 hari sebelum catatan CVE terbit.
Kondisi utama: situs Craft 4 atau 5 yang rentan bisa diakses dari internet. Batas: artikel ini tidak memuat payload, kode serangan, atau urutan serangan.
Kami membaca catatan CVE-2026-123488, catatan CVE-2026-123491, catatan CVE-2026-123490, catatan CVE-2026-123489, advisory GitHub GHSA-qcvp-ggx3-97r6, advisory GitHub GHSA-mjj9-pjh4-r48g, daftar advisory keamanan craftcms/cms, advisory VulnCheck, riwayat rilis di Packagist, dan CISA KEV pada 7 November 2026, pukul 13.30 sampai 14.10 WIB. Kami menguji 3 skrip cek dan 2 langkah sementara di pod podman dengan image resmi craftcms/cli 8.2 dan MariaDB 11.8, di Craft 5.11.1 dan 5.11.5.1. Kami tidak menguji PostgreSQL, Craft 4 di container, dan serangan.
Masalah: 1 request tanpa login bisa membuat admin baru
Craft CMS memegang konten, akun editor, dan sering juga data pelanggan. Jadi 1 celah di database membuka seluruh situs.
Craft CMS adalah CMS PHP buatan PixelPixelPotongan kode di halaman web yang memberi tahu platform iklan bahwa seseorang membuka halaman atau melakukan tindakan.Apa itu Meta Pixel? & Tonic. Agensi dan tim marketing memakai Craft untuk situs perusahaan, katalog produk, dan toko Craft Commerce. Database Craft menyimpan akun admin, hash password, dan isi formulir.
Catatan CVE terbit 6 November 2026 pukul 15.00 UTC. Advisory GitHub sudah publik sejak 22 Oktober, dan patch sudah ada sejak 17 September.

Artinya, situs yang update rutin sudah aman. Situs yang tidak update sejak awal September masih rentan, dan detail teknis celah sudah publik selama 16 hari.
Cara kerja: ringkasan advisory
Bagian ini memakai ringkasan resmi dari catatan CVE. Tujuannya membantu Anda memilih langkah yang tepat, bukan menjelaskan cara menyerang.
Ringkasan CVE-2026-123488
Menurut catatan CVE-2026-123488, endpoint pencarian elemen menyambung parameter attribute ke query SQL tanpa pembersihan. Prepared statement emulasi PDO mengizinkan beberapa perintah SQL dalam 1 query. Akibatnya, penyerang tanpa akun bisa membaca dan menulis database, lalu menjadi admin.
Advisory advisory GitHub GHSA-qcvp-ggx3-97r6 memberi judul "SQL injection leading to privilege escalation". Advisory tidak menulis syarat lain selain situs bisa diakses.
3 CVE lain di rilis yang sama
| CVE | Ringkasan | CVSS 3.1 | CVSS 4.0 |
|---|---|---|---|
| CVE-2026-123488 | SQL injection di endpoint pencarian elemen. Tanpa login, penyerang bisa mengubah tabel users | 9.8 | 9.3 |
| CVE-2026-123491 | SQL injection lewat GraphQL eager-load. Tanpa login, penyerang bisa membaca hash password dan token sesi | 8.2 | 8.8 |
| CVE-2026-123490 | Hak admin naik lewat getOwners(). Butuh akun control panel, hanya 5.8.17 sampai 5.11.1 | 8.8 | 8.7 |
| CVE-2026-123489 | Blind SQL injection lewat getNext() dan getPrev(). Butuh akun control panel | 7.1 | 7.1 |

Rilis 4.18.9 dan 5.11.2 menutup 12 advisory GitHub. 3 di antaranya berujung RCE, tetapi butuh akun control panel. CVE-2026-123490 hanya ada di 5.8.17 sampai 5.11.1.
Status eksploitasi per 7 November 2026
Kami tidak menemukan laporan serangan atau kode serangan publik. Keempat CVE belum masuk CISA KEV. CISA menulis "Exploitation: none" untuk CVE-2026-123489 dan CVE-2026-123491, dan "Automatable: yes" untuk CVE-2026-123491.
Analisa Rama Digital: 4 CVE Craft lain sudah masuk CISA KEV, yaitu CVE-2024-56145, CVE-2025-23209, CVE-2025-35939, dan CVE-2025-32432. Yang terakhir masuk 20 Maret 2026. Craft sudah berulang kali menjadi target, jadi anggap celah ini mendesak.
Siapa yang paling berisiko
Semua situs Craft 4 dan 5 yang rentan terpapar CVE-2026-123488. Kondisi di bawah menambah dampak.
| Kondisi Craft | Risiko | Alasan |
|---|---|---|
| Situs rentan yang bisa diakses publik | Tertinggi | CVE-2026-123488: tanpa login, akun bisa diubah jadi admin |
| Database MySQL atau MariaDB | Tinggi | Advisory: query bertumpuk bisa jalan, jadi data bisa ditulis |
| Skema GraphQL publik aktif | Tinggi | CVE-2026-123491: tanpa login, hash password dan token sesi bisa dibaca |
| Banyak akun control panel (editor, penulis) | Periksa | CVE-2026-123489 dan CVE-2026-123490: akun CP bisa naik jadi admin |
| Sudah 5.11.2 sampai 5.11.4 | Periksa | 6 advisory 2 November, 2 berisiko tinggi |
| Sudah 5.11.5.1 atau 4.18.9 | Rendah | Tetap cari tanda dari waktu sebelum update |

Skema GraphQL publik mati secara bawaan. Menurut dokumentasi GraphQL Craft, admin harus menyalakan token publik dan memberi izin baca. Situs headless dan aplikasi mobile sering menyalakannya.
Versi rentan dan versi aman
Yang menentukan adalah versi Craft yang berjalan di vendor/, bukan versi di composer.json.
| Versi | Status per 7 November 2026 | Tindakan |
|---|---|---|
| 5.8.17 sampai 5.11.1 | Rentan, 4 CVE | Update ke 5.11.5.1 hari ini |
| 5.0.0 sampai 5.8.16 | Rentan, 3 CVE | Update ke 5.11.5.1 hari ini |
| 4.0.0 sampai 4.18.8 | Rentan, 3 CVE | Update ke 4.18.9 hari ini |
| 5.11.2 sampai 5.11.4 | Aman untuk 4 CVE. Kena advisory 2 November | Update ke 5.11.5.1 minggu ini |
| 3.x dan lebih lama | Di luar advisory. Tanpa patch keamanan | Rencanakan naik ke Craft 5 |
| 5.11.5.1 dan 4.18.9 | Aman untuk 4 CVE | Jalankan langkah 5 |

Menurut riwayat rilis di Packagist, 4.18.9 dan 5.11.2 terbit 17 September 2026. 5.11.4 terbit 1 Oktober, 5.11.5 terbit 7 Oktober, dan 5.11.5.1 terbit 8 Oktober. Kami menyarankan 5.11.5.1, karena versi itu juga menutup 6 advisory 2 November.
Analisa Rama Digital: composer audit membaca database advisory Packagist. Di uji kami pada 7 November 2026, perintah itu tidak menemukan 4 CVE ini di 5.11.1. Jangan pakai hasil kosong itu sebagai bukti aman.
Prasyarat sebelum mulai
- Akses SSH ke server Craft, atau akses ke repo bila situs deploy dari git.
- PHP CLI dan Composer di server atau di mesin kerja.
bash,curl, dan klienmysqlataumariadbuntuk skrip cek.- Ruang disk untuk backup database dan salinan folder proyek.
- Daftar plugin Craft dan versinya. Plugin bisa menahan update Composer.
- Jendela update 15 sampai 30 menit. Situs bisa tampil error sebentar saat migrasi.
Langkah 1: Cek versi Craft
Simpan skrip ini sebagai cek-versi-craft.sh. Skrip membaca versi di folder vendor/ dan di composer.lock, atau versi yang Anda ketik.
#!/usr/bin/env bash
# Cek versi Craft CMS untuk CVE-2026-123488 (SQL injection tanpa login) dan 3 CVE lain di 4.18.9 dan 5.11.2.
# Skrip hanya membaca. Butuh bash, grep, dan sort.
# Pakai: bash cek-versi-craft.sh /var/www/craft (folder proyek Craft)
# atau ketik versi manual: bash cek-versi-craft.sh 5.11.1
set -u
AMAN4=4.18.9
AMAN5=5.11.2
TERBARU5=5.11.5.1
# lebih_kecil A B: benar bila A < B
lebih_kecil() { [ "$1" != "$2" ] && [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n 1)" = "$1" ]; }
nilai() {
local v
v=$(printf '%s' "$1" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n 1)
[ -z "$v" ] && { echo "PERIKSA: versi ${1:-kosong} tidak dikenal"; return; }
case "$v" in
5.*)
if lebih_kecil "$v" 5.8.17; then echo "RENTAN: 3 CVE, termasuk CVE-2026-123488. Update ke $TERBARU5"
elif lebih_kecil "$v" $AMAN5; then echo "RENTAN: 4 CVE, termasuk CVE-2026-123488. Update ke $TERBARU5"
elif lebih_kecil "$v" 5.11.5; then echo "AMAN untuk 4 CVE ini. Update ke $TERBARU5 untuk advisory 2 November"
else echo "AMAN untuk 4 CVE ini"; fi ;;
4.*)
if lebih_kecil "$v" $AMAN4; then echo "RENTAN: 3 CVE, termasuk CVE-2026-123488. Update ke $AMAN4"
else echo "AMAN untuk 4 CVE ini"; fi ;;
3.*|2.*) echo "DI LUAR ADVISORY: advisory hanya menyebut 4.x dan 5.x. Rencanakan naik ke $TERBARU5" ;;
*) echo "PERIKSA: versi $v di luar cabang 4 dan 5" ;;
esac
}
ARG=${1:-.}
if printf '%s' "$ARG" | grep -qE '^[0-9]+\.[0-9]+'; then
echo "Craft $ARG: $(nilai "$ARG")"; exit 0
fi
[ -d "$ARG" ] || { echo "Folder $ARG tidak ada"; exit 1; }
DIR=$(cd "$ARG" && pwd)
echo "== Folder: $DIR"
# Versi kode yang berjalan ada di vendor. composer.lock bisa berbeda bila composer install belum jalan.
RUN=$(grep -oE "'version' => '[^']+'" "$DIR/vendor/craftcms/cms/src/config/app.php" 2>/dev/null | head -n 1 | cut -d"'" -f4)
LOCK=$(grep -A 1 '"name": "craftcms/cms"' "$DIR/composer.lock" 2>/dev/null | grep -oE '"version": "[^"]+"' | cut -d'"' -f4)
[ -z "$RUN$LOCK" ] && { echo "Folder ini bukan proyek Craft (tidak ada vendor/craftcms/cms atau composer.lock)"; exit 1; }
echo "vendor/craftcms/cms: ${RUN:-tidak ada}"
echo "composer.lock: ${LOCK:-tidak ada}"
[ -n "$RUN" ] && [ -n "$LOCK" ] && [ "$RUN" != "$LOCK" ] && echo "PERIKSA: vendor dan composer.lock berbeda. Jalankan composer install, lalu cek lagi"
echo "Craft ${RUN:-$LOCK}: $(nilai "${RUN:-$LOCK}")"
# Folder proyek Craft (berisi berkas craft, composer.lock, dan vendor/)
bash cek-versi-craft.sh /var/www/craft
# Versi yang Anda salin dari Utilities > System Report di control panel
bash cek-versi-craft.sh 4.18.8
# composer audit belum mengenal 4 CVE ini. Di uji kami, hasilnya kosong untuk 5.11.1.
composer audit --locked

Cek hasil: "RENTAN" berarti lanjut ke langkah 2 dan 3 hari ini. "AMAN ... Update ke 5.11.5.1" berarti rencanakan update minggu ini. "AMAN" tetap berarti jalankan langkah 5, karena celah ada sebelum Anda update.
Langkah 2: Cek bagian yang terpapar
Langkah ini menjawab 4 pertanyaan: apakah situs bisa diakses, database apa yang dipakai, apakah GraphQL menjawab tanpa token, dan berapa akun admin yang ada.
Simpan skrip ini sebagai cek-paparan-craft.sh. Skrip hanya membaca .env dan mengirim 2 query {ping} ke GraphQL. Skrip tidak mengirim payload.
#!/usr/bin/env bash
# Cek bagian Craft CMS yang terpapar 4 CVE di 4.18.9 dan 5.11.2. Skrip hanya membaca. Butuh bash, curl, dan php.
# Pakai: bash cek-paparan-craft.sh <folder proyek Craft> <URL publik situs>
# Contoh: bash cek-paparan-craft.sh /var/www/craft https://www.contoh.id
set -u
DIR=${1:?folder proyek Craft, contoh /var/www/craft}
URL=${2:?URL publik situs, contoh https://www.contoh.id}
URL=${URL%/}
TERPAPAR=0; PERIKSA=0
terpapar() { echo " TERPAPAR: $1"; TERPAPAR=$((TERPAPAR + 1)); }
periksa() { echo " PERIKSA: $1"; PERIKSA=$((PERIKSA + 1)); }
info() { echo " INFO: $1"; }
# Baca 1 nilai dari .env tanpa menjalankan isi berkas.
envval() { grep -E "^$1=" "$DIR/.env" 2>/dev/null | tail -n 1 | cut -d= -f2- | tr -d '"'"'"; }
[ -f "$DIR/craft" ] || { echo "Folder $DIR bukan proyek Craft (tidak ada berkas craft)"; exit 1; }
echo "== 1. Pencarian elemen (CVE-2026-123488, tanpa login)"
terpapar "semua versi rentan. Advisory tidak memberi syarat lain selain situs bisa diakses"
echo "== 2. Database"
DRIVER=$(envval CRAFT_DB_DRIVER); DRIVER=${DRIVER:-mysql}
info "driver: $DRIVER"
if [ "$DRIVER" = mysql ]; then
if grep -qs 'MYSQL_ATTR_MULTI_STATEMENTS' "$DIR/config/db.php"; then
info "config/db.php menolak beberapa perintah SQL dalam 1 query (langkah 4a terpasang)"
else
periksa "MySQL atau MariaDB tanpa langkah 4a. Advisory menulis query bertumpuk bisa jalan"
fi
fi
echo "== 3. GraphQL publik (CVE-2026-123491, tanpa login)"
GQL=$(envval CRAFT_ENABLE_GQL)
[ "$GQL" = false ] && info "CRAFT_ENABLE_GQL=false di .env"
for P in /actions/graphql/api /api; do
BODY=$(curl -s -m 15 -X POST "$URL$P" -H 'Content-Type: application/json' --data '{"query":"{ping}"}' 2>/dev/null | head -c 300)
case "$BODY" in
*pong*) terpapar "$URL$P menjawab query tanpa token. Cek izin skema publik di langkah 4b" ;;
*) info "$URL$P tidak menjawab query tanpa token" ;;
esac
done
echo "== 4. Control panel (CVE-2026-123489 dan CVE-2026-123490, butuh akun CP)"
CP=$(envval CRAFT_CP_TRIGGER); CP=${CP:-admin}
CODE=$(curl -s -o /dev/null -m 15 -w '%{http_code}' "$URL/$CP/login" 2>/dev/null)
if [ "$CODE" = 200 ]; then periksa "halaman login $URL/$CP/login terbuka dari mesin ini (HTTP 200)"
else info "halaman login $URL/$CP/login menjawab HTTP ${CODE:-000}"; fi
ADMINS=$(cd "$DIR" && php craft users/list-admins 2>/dev/null | grep -cE '^ *- ')
info "jumlah akun admin: ${ADMINS:-tidak terbaca}"
echo "== Hasil: TERPAPAR $TERPAPAR, PERIKSA $PERIKSA"
exit 0
# Jalankan di server Craft. Skrip membaca .env dan memanggil URL publik situs.
bash cek-paparan-craft.sh /var/www/craft https://www.contoh.id

Cek hasil: catat setiap baris "TERPAPAR" dan "PERIKSA". Bila update belum bisa jalan hari ini, pasang langkah 4a untuk baris database dan langkah 4b untuk baris GraphQL.
Langkah 3: Backup, lalu update ke 5.11.5.1 atau 4.18.9
Update adalah satu-satunya langkah yang menutup 4 CVE ini. Baca panduan update Craft bila Anda melewati banyak versi.
# 3a. Backup database. Simpan juga salinan folder proyek dan .env.
php craft db/backup
# 3b. Craft 5: update ke 5.11.5.1 (diuji)
composer require craftcms/cms:5.11.5.1 -W
# Craft 4: update ke 4.18.9 (tidak diuji)
composer require craftcms/cms:4.18.9 -W
# 3c. Jalankan migrasi dan kosongkan cache
php craft up --interactive=0
# 3d. Cek lagi versi yang berjalan
bash cek-versi-craft.sh /var/www/craft

Di uji kami, update dari 5.11.1 ke 5.11.5.1 selesai tanpa migrasi database baru, dan halaman depan tetap menjawab 200. Opsi -W mengizinkan Composer menaikkan dependensi lain. Bila Composer menolak, cek batas versi plugin di pesan error.
Situs yang deploy dari git
Banyak agensi menjalankan Composer di mesin kerja, lalu deploy lewat git. Di alur ini, versi Craft di server mengikuti composer.lock di repo.
# Situs yang deploy dari git: update di mesin kerja, lalu kirim composer.lock
git add composer.json composer.lock
git commit -m "Update Craft CMS ke 5.11.5.1 untuk CVE-2026-123488"
# Sesudah deploy, di server:
composer install --no-dev && php craft up --interactive=0

Cek hasil: skrip versi menulis "AMAN untuk 4 CVE ini", vendor dan composer.lock sama, dan halaman depan menjawab 200. Buka juga control panel dan 1 halaman dengan formulir.
Langkah 4: Kurangi dampak sampai update selesai
Langkah ini mengurangi dampak, tetapi tidak membuat situs aman. CVE tetap terbuka sampai Anda update.

4a. Tolak query bertumpuk di koneksi MySQL atau MariaDB
Analisa Rama Digital: advisory menulis prepared statement emulasi mengizinkan beberapa perintah dalam 1 query. Kami mematikan opsi PDO itu lewat pdoAttributes di kode sumber DbConfig. Kondisi: update belum bisa jalan hari ini, dan database memakai MySQL atau MariaDB. Batas: injeksi di 1 perintah SELECT bisa tetap membaca data.
Simpan berkas ini sebagai db-craft.php. Bila config/db.php sudah ada, tambah baris ->pdoAttributes(...) ke DbConfig::create() yang ada. Kami hanya menguji berkas di bawah, di Craft 5.11.1.
<?php
// config/db.php: langkah 4a, sementara sampai update selesai (MySQL dan MariaDB).
// Koneksi menolak beberapa perintah SQL dalam 1 query. Nilai CRAFT_DB_* di .env tetap dipakai.
return craft\config\DbConfig::create()
->pdoAttributes([PDO::MYSQL_ATTR_MULTI_STATEMENTS => false]);
# 4a. Uji sebelum dan sesudah. Hasil "1" berarti query bertumpuk masih jalan.
php craft exec 'count(Craft::$app->getDb()->createCommand("SELECT 1; SELECT 2")->queryAll())'
# Pasang berkas di atas sebagai config/db.php. Hasil yang benar: Error SQLSTATE[42000] ... 1064.
cp db-craft.php config/db.php
# Query biasa harus tetap jalan
php craft exec 'Craft::$app->getDb()->createCommand("SELECT COUNT(*) FROM {{%users}}")->queryScalar()'

Di uji kami, query uji bertumpuk mendapat hasil 1 sebelum berkas ini, lalu SQLSTATE[42000] 1064 sesudahnya. Query biasa tetap mendapat 2, dan halaman depan tetap 200. Hapus langkah ini sesudah update bila Anda tidak butuh lagi.
4b. Matikan GraphQL bila situs tidak memakainya
# 4b. Situs tidak memakai GraphQL: matikan endpoint GraphQL
echo CRAFT_ENABLE_GQL=false >> .env
# Cek lagi. Hasil yang benar: 2 baris "tidak menjawab query tanpa token".
bash cek-paparan-craft.sh /var/www/craft https://www.contoh.id | sed -n '/== 3/,/== 4/p'

Menurut referensi general config Craft, enableGql mengatur endpoint GraphQL, dan Craft membaca nilai itu dari env CRAFT_ENABLE_GQL. Situs headless tidak boleh memakai langkah ini. Matikan token publik di menu GraphQL > Schemas, lalu pakai token privat. Kami tidak menguji jalur menu itu.
4c. Kurangi akun control panel
CVE-2026-123489 dan CVE-2026-123490 butuh akun control panel. Suspend akun editor yang tidak aktif, dan nyalakan 2FA untuk semua admin. Langkah ini tidak menyentuh CVE-2026-123488.
Cek hasil: skrip paparan menulis "langkah 4a terpasang" dan 2 baris GraphQL "tidak menjawab query tanpa token".
Langkah 5: Cari tanda pemakaian
Celah ini ada sebelum Anda update, jadi cari tanda dari waktu sebelum update juga. Mulai minimal dari 17 September 2026, atau lebih awal bila log masih ada.
Simpan skrip ini sebagai cek-tanda-craft.sh. Skrip membaca akun admin, user baru, dan perubahan password di database Craft. Skrip juga mencari kata SQL dan titik koma di query string log akses.
#!/usr/bin/env bash
# Cari tanda penyalahgunaan CVE-2026-123488 dan 3 CVE lain di Craft CMS. Skrip hanya membaca.
# Butuh bash, awk, zgrep, dan klien mysql atau mariadb. Hanya untuk database MySQL atau MariaDB.
# Pakai: bash cek-tanda-craft.sh <folder proyek Craft> <tanggal YYYY-MM-DD> [folder log akses]
# Log akses: berkas access.log* (termasuk .gz) dari nginx atau Apache, format combined.
set -u
DIR=${1:?folder proyek Craft, contoh /var/www/craft}
SINCE=${2:?tanggal mulai, contoh 2026-09-01}
LOGDIR=${3:-}
printf '%s' "$SINCE" | grep -qE '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' || { echo "Tanggal harus YYYY-MM-DD"; exit 1; }
TANDA=0; PERIKSA=0; BUTA=0
tanda() { echo " TANDA: $1"; TANDA=$((TANDA + 1)); }
periksa() { echo " PERIKSA: $1"; PERIKSA=$((PERIKSA + 1)); }
buta() { echo " TIDAK BISA DIPASTIKAN: $1"; BUTA=$((BUTA + 1)); }
# Buang karakter kontrol dan potong baris, agar isi log tidak bisa mengubah tampilan terminal.
bersih() { LC_ALL=C tr -d '\000-\010\013-\037\177' | cut -c1-160; }
envval() { grep -E "^$1=" "$DIR/.env" 2>/dev/null | tail -n 1 | cut -d= -f2- | tr -d '"'"'"; }
echo "== 1. Akun di database (mulai $SINCE)"
CLI=$(command -v mariadb || command -v mysql || true)
DRIVER=$(envval CRAFT_DB_DRIVER)
if [ -n "$DRIVER" ] && [ "$DRIVER" != mysql ]; then
buta "driver $DRIVER. Skrip ini hanya membaca MySQL atau MariaDB"
elif [ -z "$CLI" ]; then
buta "klien mysql atau mariadb tidak ada"
else
P=$(envval CRAFT_DB_TABLE_PREFIX); PORT=$(envval CRAFT_DB_PORT); PORT=${PORT:-3306}
# Password dikirim lewat env MYSQL_PWD, bukan argumen. Argumen tampil di daftar proses (ps).
q() { MYSQL_PWD="$(envval CRAFT_DB_PASSWORD)" "$CLI" -N -B -h "$(envval CRAFT_DB_SERVER)" -P "$PORT" \
-u "$(envval CRAFT_DB_USER)" "$(envval CRAFT_DB_DATABASE)" -e "$1" 2>/dev/null; }
if ! q "SELECT 1" >/dev/null; then
buta "login database gagal. Cek CRAFT_DB_* di .env"
else
while IFS=$'\t' read -r id user mail login ip; do
[ "$login" = NULL ] && login=""; [ "$ip" = NULL ] && ip=""
periksa "admin id $id $user <$mail>, login terakhir ${login:-belum ada}, IP ${ip:-tidak tercatat}. Pastikan Anda kenal akun ini"
done < <(q "SELECT id, username, email, lastLoginDate, lastLoginAttemptIp FROM ${P}users WHERE admin = 1 ORDER BY id" | bersih)
while IFS=$'\t' read -r id user tgl adm; do
[ "$adm" = 1 ] && tanda "admin baru id $id $user dibuat $tgl" || periksa "user baru id $id $user dibuat $tgl"
done < <(q "SELECT id, username, dateCreated, admin FROM ${P}users WHERE dateCreated >= '$SINCE' ORDER BY dateCreated" | bersih)
while IFS=$'\t' read -r id user tgl; do
periksa "password id $id $user berubah $tgl"
done < <(q "SELECT id, username, lastPasswordChangeDate FROM ${P}users WHERE lastPasswordChangeDate >= '$SINCE' ORDER BY lastPasswordChangeDate" | bersih)
info=$(q "SELECT COUNT(*) FROM ${P}users WHERE admin = 1")
echo " INFO: $info akun admin"
fi
fi
echo "== 2. Log akses (${LOGDIR:-tidak diberi folder}, mulai $SINCE)"
if [ -n "$LOGDIR" ]; then
FILES=$(find "$LOGDIR" -type f -name 'access.log*' 2>/dev/null | sort)
echo " $(printf '%s' "$FILES" | grep -c .) berkas access.log"
# awk membaca tanggal [07/Nov/2026:10:00:00 +0700]. Baris tanpa tanggal yang terbaca dihitung terpisah.
HASIL=$(printf '%s\n' "$FILES" | xargs -r zcat -f 2>/dev/null | awk -v since="${SINCE//-/}" '
BEGIN { split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", m, " "); for (i = 1; i <= 12; i++) mon[m[i]] = sprintf("%02d", i) }
{
if (!match($0, /\[[0-9][0-9]\/[A-Z][a-z][a-z]\/[0-9][0-9][0-9][0-9]:/)) { print "BUTA"; next }
d = substr($0, RSTART + 1, 11); tgl = substr(d, 8, 4) mon[substr(d, 4, 3)] substr(d, 1, 2)
if (tgl < since) next
if (!match($0, /"[A-Z]+ [^ "]+/)) next
req = substr($0, RSTART + 1, RLENGTH - 1); low = tolower(req)
if (low ~ /graphql|\/api/ && req ~ /^POST/) { print "GQL " $1; next }
q = index(low, "?"); if (q == 0) next
qs = substr(low, q)
if (qs ~ /(union|select|sleep|benchmark|information_schema|extractvalue|updatexml)(\+|%20|%09|%0a|%28|\(|\/\*|%2f\*)/ || qs ~ /%3b|;/)
print "SQL " $1 " " substr(req, 1, 120)
}')
N_BUTA=$(printf '%s\n' "$HASIL" | grep -c '^BUTA')
[ "$N_BUTA" -gt 0 ] && buta "$N_BUTA baris log tanpa tanggal yang terbaca. Baca baris itu manual"
while IFS= read -r l; do tanda "pola SQL di query: ${l#SQL }"; done < <(printf '%s\n' "$HASIL" | grep '^SQL ' | bersih | head -n 10)
echo " POST GraphQL per IP (5 teratas):"
printf '%s\n' "$HASIL" | grep '^GQL ' | awk '{print $2}' | sort | uniq -c | sort -rn | head -n 5 | bersih | sed 's/^ */ INFO: /'
fi
echo "== Hasil: TANDA $TANDA, PERIKSA $PERIKSA, TIDAK BISA DIPASTIKAN $BUTA"
[ "$BUTA" -gt 0 ] && echo "Hasil belum lengkap. Hasil 0 tanda belum berarti bersih."
[ "$TANDA" -gt 0 ] && echo "Simpan log dan dump database sebagai bukti, lalu ikuti langkah 5 di artikel."
exit 0
# Database Craft dan log akses nginx, mulai 1 September 2026
bash cek-tanda-craft.sh /var/www/craft 2026-09-01 /var/log/nginx

Di uji ini, skrip menemukan 4 TANDA: 1 admin baru dummy dan 3 query string dengan pola SQL dari 2 IP dummy. 1 dari 3 baris itu ada di berkas .gz. Skrip juga menulis 3 PERIKSA untuk 2 admin dan 1 perubahan password.

Analisa Rama Digital: SQL injection lewat POST GraphQL tidak tampil di log akses biasa, karena isi body tidak tercatat. Jadi skrip hanya menghitung POST GraphQL per IP. Lonjakan dari 1 IP asing perlu Anda periksa.
Cek hasil: skrip menulis "TANDA 0", dan setiap PERIKSA punya penjelasan dari tim Anda. Log bisa dihapus penyerang, jadi 0 tanda belum berarti aman.
Bila ada 1 tanda saja, simpan log dan dump database sebagai bukti. Lalu hapus admin yang tidak Anda kenal, ganti password semua akun CP, ganti password database dan kunci APIAPIPintu resmi yang dipakai 2 sistem untuk saling mengirim data, tanpa orang menyalin data secara manual.Buka glosarium di .env, dan update ke 5.11.5.1.
Bila situs menyimpan data pelanggan, UU 27/2022 tentang Pelindungan Data Pribadi mewajibkan pemberitahuan tertulis paling lambat 3 kali 24 jam sesudah kebocoran data pribadi. Baca juga panduan security, privacy, dan UU PDP untuk perusahaan.
Contoh: 1 siang respons untuk 3 situs Craft agensi
Simulasi ini memakai data dummy. Nama situs, waktu, dan hasil bukan data pelanggan.
| Waktu (WIB) | Situs dummy | Yang dicek | Hasil | Tindakan |
|---|---|---|---|---|
| 13.00 | situs-profil (Craft 5.11.1, MySQL) | Langkah 1 dan 2 | RENTAN, TERPAPAR 1, PERIKSA 2 | Langkah 3 sekarang |
| 13.30 | katalog-headless (Craft 5.9.3, GraphQL publik) | Langkah 1 dan 2 | RENTAN, TERPAPAR 2 | Langkah 4a sekarang, update sore ini |
| 14.00 | toko-lama (Craft 4.16.2) | Langkah 1 | RENTAN, 3 CVE | Update ke 4.18.9 |
| 16.00 | Ketiga situs | Langkah 3 dan 5 | AMAN, 1 admin tidak dikenal di toko-lama | Simpan bukti, ganti password |
Pelajaran dari simulasi: situs headless tidak bisa memakai langkah 4b, jadi update harus jalan hari itu juga.
Siapa yang memasang patch
Craft adalah paket Composer di proyek Anda, jadi penyedia hosting biasanya tidak memasang patch ini.
| Jenis layanan | Pemegang patch | Yang Anda kerjakan |
|---|---|---|
| Craft di VPS atau shared hosting | Tim Anda | Langkah 1 sampai 5 |
| Craft yang deploy dari git (termasuk Craft Cloud) | Tim Anda, lewat composer.lock | Update di mesin kerja, deploy, lalu langkah 5 |
| Situs buatan agensi dengan kontrak maintenance | Agensi, sesuai kontrak | Minta versi Craft tertulis dan hasil langkah 5 |
| Situs tanpa kontrak maintenance | Pemilik situs | Cari developer hari ini, atau pakai langkah 4 dulu |
Rekomendasi Rama Digital: pantau rilis keamanan semua CMS PHP Anda dengan jadwal tetap. Minggu ini kami juga menulis peringatan WordPress CVE-2026-119977, peringatan PolyShell Magento CVE-2026-48356, peringatan PHP2Shell CVE-2026-109618, dan peringatan Rails Active Storage CVE-2026-122006.
Checklist hari ini
- Buat daftar semua situs Craft, termasuk staging. Pemilik: tim IT atau agensi. Bukti: daftar nama dan versi.
- Cek versi dengan langkah 1. Pemilik: developer. Bukti: output skrip.
- Cek paparan dengan langkah 2. Pemilik: developer. Bukti: output skrip.
- Pasang langkah 4 di setiap situs yang belum bisa update hari ini. Pemilik: developer. Bukti: output skrip paparan.
- Backup, lalu update ke 5.11.5.1 atau 4.18.9. Pemilik: developer. Bukti: output skrip versi sesudah update.
- Cari tanda dengan langkah 5. Pemilik: developer dan tim keamanan. Bukti: output skrip.
- Suspend akun CP yang tidak aktif dan nyalakan 2FA admin. Pemilik: admin situs. Bukti: daftar akun.
Tanya jawab
Apakah celah ini sudah dipakai untuk menyerang?
Per 7 November 2026, kami tidak menemukan laporan serangan, dan 4 CVE ini belum masuk CISA KEV. Tetapi detail advisory sudah publik sejak 22 Oktober, jadi update hari ini.
Situs saya tidak memakai GraphQL. Apakah saya aman?
Tidak. CVE-2026-123488 ada di endpoint pencarian elemen, bukan di GraphQL. Mematikan GraphQL hanya menutup jalur CVE-2026-123491.
Saya memakai PostgreSQL. Apakah langkah 4a berlaku?
Tidak. Opsi di langkah 4a hanya untuk MySQL dan MariaDB, dan kami tidak menguji PostgreSQL. Update ke 5.11.5.1 atau 4.18.9 adalah jalan Anda.
composer audit bilang tidak ada masalah. Kenapa saya tetap rentan?
Di uji kami pada 7 November 2026, composer audit tidak menemukan 4 CVE ini di 5.11.1. Pakai skrip langkah 1, karena skrip itu membaca versi yang berjalan.
Saya masih di Craft 3. Apa yang harus saya kerjakan?
Advisory hanya menyebut Craft 4 dan 5, dan Craft 3 tidak lagi mendapat patch keamanan. Rencanakan naik ke Craft 5, dan batasi akses ke situs sampai migrasi selesai.
Apakah update menghapus konten atau plugin saya?
Tidak, bila Anda memakai database dan folder yang sama. Di uji kami, update 5.11.1 ke 5.11.5.1 tidak membawa migrasi baru. Tetap backup dulu.
Apakah skrip cek mengubah sesuatu di server saya?
Tidak. Ketiga skrip hanya membaca. Perintah yang mengubah sesuatu hanya ada di langkah 3 dan 4, dan setiap perintah ditulis terpisah.
Langkah berikutnya
Batas yang tetap berlaku: langkah 4 tidak menggantikan update, dan update tidak membatalkan akses yang sudah terjadi. Bila situs Craft Anda menunjukkan tanda di langkah 5, atau Anda butuh bantuan update dan audit, pakai Vibe Code Rescue. Untuk 1 masalah mendesak hari ini, jadwalkan sesi 30 menit.
Sumber
- catatan CVE-2026-123488: deskripsi resmi, versi terdampak, CVSS 3.1 9.8 dan CVSS 4.0 9.3, 6 November 2026.
- catatan CVE-2026-123491, catatan CVE-2026-123490, dan catatan CVE-2026-123489: deskripsi dan skor 3 CVE lain.
- advisory GitHub GHSA-qcvp-ggx3-97r6 dan advisory GitHub GHSA-mjj9-pjh4-r48g: advisory Craft untuk 2 celah tanpa login.
- daftar advisory keamanan craftcms/cms: 12 advisory 22 dan 23 Oktober, serta 6 advisory 2 November.
- advisory VulnCheck: ringkasan VulnCheck sebagai CNA.
- riwayat rilis di Packagist: tanggal rilis 4.18.9, 5.11.2, 5.11.4, 5.11.5, dan 5.11.5.1.
- kode sumber DbConfig, referensi general config Craft, dokumentasi GraphQL Craft, dan panduan update Craft: dasar langkah 3, 4a, dan 4b.
- CISA KEV: status 4 CVE ini dan 4 CVE Craft lama per 7 November 2026.
- UU 27/2022 tentang Pelindungan Data Pribadi: kewajiban lapor kebocoran data pribadi.




