Sécurité

SQL Injection: Prevenzione completa

Guida alla prevenzione delle SQL injection. Query parametrizzate, ORM, validazione e contromisure per applicazioni web sicure.

18 mars 20264 min de lectureWarDek Team

La SQL injection è una delle vulnerabilità più antiche e pericolose del web. Nonostante sia nota da oltre 25 anni, continua a comparire nella OWASP Top 10 perché sviluppatori commettono ancora lo stesso errore: concatenare dati utente nelle query SQL.

Come funziona una SQL injection

Il principio

Quando un'applicazione costruisce query SQL concatenando l'input dell'utente, un attaccante può iniettare istruzioni SQL aggiuntive.

Codice vulnerabile:

// MAI FARE QUESTO
const query = `SELECT * FROM utenti WHERE email = '${userInput}'`

Input malevolo: ' OR '1'='1' --

Query risultante:

SELECT * FROM utenti WHERE email = '' OR '1'='1' --'

La condizione '1'='1' è sempre vera — l'attaccante ottiene tutti gli utenti.

I rischi concreti

| Attacco | Impatto | |---------|---------| | Esfiltrazione dati | Lettura di tutti i dati del database | | Modifica dati | Alterazione di prezzi, permessi, password | | Cancellazione | DROP TABLE, DELETE massivi | | Bypass autenticazione | Login come qualsiasi utente | | Esecuzione comandi | In alcuni DBMS, esecuzione comandi sul server |

La difesa: query parametrizzate

Il principio

Le query parametrizzate (prepared statements) separano la struttura SQL dai dati. Il database tratta i parametri come dati puri, mai come istruzioni SQL.

PostgreSQL con Node.js

// SICURO — query parametrizzata
const result = await pool.query(
  'SELECT * FROM utenti WHERE email = $1',
  [userEmail]
)

Il $1 è un placeholder. Il driver PostgreSQL invia l'email come parametro separato — impossibile iniettare SQL.

MySQL con Node.js

// SICURO — prepared statement
const [rows] = await connection.execute(
  'SELECT * FROM utenti WHERE email = ?',
  [userEmail]
)

ORM — Prisma

// SICURO — Prisma parametrizza automaticamente
const utente = await prisma.utente.findFirst({
  where: { email: userEmail }
})

Gli ORM come Prisma, Drizzle e TypeORM generano query parametrizzate automaticamente. Questo è il metodo più sicuro e il più raccomandato.

ORM — Drizzle

// SICURO — Drizzle parametrizza automaticamente
const utente = await db
  .select()
  .from(utenti)
  .where(eq(utenti.email, userEmail))

Attenzione: casi in cui l'ORM non protegge

Raw queries

Se usate query raw nell'ORM, la protezione automatica non si applica:

// PERICOLOSO — raw query con concatenazione
const result = await prisma.$queryRawUnsafe(
  `SELECT * FROM utenti WHERE email = '${userEmail}'`
)

// SICURO — raw query con parametri
const result = await prisma.$queryRaw`
  SELECT * FROM utenti WHERE email = ${userEmail}
`

Nomi di tabelle e colonne

I parametri proteggono solo i valori, non i nomi di tabelle o colonne:

// PERICOLOSO — nome colonna dall'utente
const orderBy = userInput // potrebbe essere "email; DROP TABLE utenti--"
const result = await pool.query(`SELECT * FROM utenti ORDER BY ${orderBy}`)

// SICURO — whitelist
const COLONNE_AMMESSE = ['nome', 'email', 'data_creazione']
const orderBy = COLONNE_AMMESSE.includes(userInput) ? userInput : 'data_creazione'

LIKE con caratteri speciali

// Potenziale issue — % e _ sono metacaratteri SQL
const searchTerm = userInput.replace(/%/g, '\\%').replace(/_/g, '\\_')
const result = await pool.query(
  'SELECT * FROM prodotti WHERE nome LIKE $1',
  [`%${searchTerm}%`]
)

Difesa in profondità

Livello 1: Query parametrizzate (obbligatorio)

Il fondamento. Nessuna eccezione.

Livello 2: Validazione dell'input

Validate gli input prima che raggiungano la query:

import { z } from 'zod'

const filtroSchema = z.object({
  email: z.string().email(),
  limite: z.number().int().min(1).max(100),
  ordine: z.enum(['nome', 'email', 'data']),
})

Livello 3: Principio del minimo privilegio

L'utente del database usato dall'applicazione dovrebbe avere solo i permessi necessari:

-- Creare un utente con permessi limitati
CREATE USER app_user WITH PASSWD 'credenziale_sicura';
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO app_user;
-- NO: GRANT ALL, DROP, CREATE, ALTER

Livello 4: WAF (Web Application Firewall)

Un WAF può bloccare i pattern di SQL injection più comuni come secondo livello di difesa:

Livello 5: Monitoraggio

Monitorate le query anomale:

-- Esempio: log delle query lente o anomale in PostgreSQL
ALTER SYSTEM SET log_min_duration_statement = 1000; -- Log query > 1s
ALTER SYSTEM SET log_statement = 'ddl'; -- Log tutti i DDL

Tipi di SQL injection avanzati

Blind SQL injection

L'attaccante non vede il risultato della query ma deduce informazioni dalle risposte del server (tempi di risposta, messaggi di errore diversi).

Difesa: stesse contromisure + messaggi di errore generici.

Second-order SQL injection

I dati malevoli vengono salvati nel database e attivati successivamente quando usati in un'altra query.

Difesa: parametrizzare TUTTE le query, non solo quelle che ricevono input diretto.

SQL injection in stored procedures

Le stored procedure non sono immuni se usano SQL dinamico internamente.

Difesa: usare query parametrizzate anche all'interno delle stored procedure.

Testing

Strumenti automatizzati

| Strumento | Tipo | Uso | |-----------|------|-----| | sqlmap | Open source | Test automatizzato completo | | OWASP ZAP | Scanner | Rilevamento automatico | | Burp Suite | Proxy | Analisi manuale + automatica |

Test manuali di base

Provate a inserire nei campi del vostro sito:

' OR '1'='1
' UNION SELECT null,null,null--
1; DROP TABLE test--
' AND 1=SLEEP(5)--

Se una di queste stringhe causa un comportamento anomalo, avete una vulnerabilità.

Checklist prevenzione SQL injection

Conclusione

La SQL injection è una vulnerabilità che non dovrebbe più esistere nel 2026. Le query parametrizzate la eliminano alla radice, gli ORM la prevengono automaticamente e la validazione dell'input aggiunge un ulteriore livello di sicurezza. Se usate ancora la concatenazione di stringhe nelle query SQL, fermatevi ora.


Scansiona il tuo sito web gratis con WarDek — OWASP, NIS2, GDPR, AI Act in un'unica scansione.

#sql-injection#sicurezza#database#prevenzione#owasp

Scannez votre site gratuitement

WarDek détecte les vulnérabilités mentionnées dans cet article en quelques secondes.

Retour à Sécurité