Ecco una sequenza dettagliata di soluzioni per mitigare il rischio di SQL Injection in un’applicazione che utilizza SQL:
1. Utilizzare Query Parametrizzate (Prepared Statements)
🔍 Perché è importante?
Le query SQL dinamiche concatenano direttamente gli input utente alla query, permettendo a un attaccante di inserire codice malevolo.
🔴 Esempio di codice vulnerabile (Python + SQLite)
pythonCopia codiceusername = input("Inserisci il nome utente: ")
query = "SELECT * FROM utenti WHERE username = '" + username + "'"
cursor.execute(query) # ❌ VULNERABILE A SQL INJECTION!
Se l’utente inserisce:
bashCopia codice' OR '1'='1
La query diventa:
sqlCopia codiceSELECT * FROM utenti WHERE username = '' OR '1'='1'
E l’attaccante ottiene tutti gli utenti del database.
✅ Soluzione: Query parametrizzate
I Prepared Statements separano la query dai dati, evitando l’iniezione SQL.
pythonCopia codiceimport sqlite3
conn = sqlite3.connect("database.db")
cursor = conn.cursor()
username = input("Inserisci il nome utente: ")
query = "SELECT * FROM utenti WHERE username = ?"
cursor.execute(query, (username,)) # ✅ SICURO!
result = cursor.fetchall()
print(result)
conn.close()
🔵 Vantaggi:
✔ I parametri vengono trattati come dati e non come codice SQL.
✔ Gli attacchi basati su SQL Injection non funzionano.
2. Utilizzare ORM (Object-Relational Mapping)
🔍 Perché è importante?
Un ORM gestisce l’accesso al database in modo sicuro, astrarre le query SQL e prevenire errori umani che portano a vulnerabilità.
✅ Esempio con SQLAlchemy (Python)
pythonCopia codicefrom sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from models import Utente # Classe ORM per la tabella utenti
engine = create_engine("sqlite:///database.db")
Session = sessionmaker(bind=engine)
session = Session()
username = input("Inserisci il nome utente: ")
utente = session.query(Utente).filter_by(username=username).first()
print(utente)
🔵 Vantaggi:
✔ Genera query sicure automaticamente.
✔ Protegge da SQL Injection.
✔ Fornisce funzioni avanzate per la gestione dei dati.
3. Convalidare e Sanificare gli Input Utente
🔍 Perché è importante?
Se il database accetta input arbitrari, un attaccante può manipolare la query. Validare e sanificare l’input evita questo rischio.
✅ Soluzione: Validazione con regex in Python
pythonCopia codiceimport re
def valida_username(username):
return re.match(r"^[a-zA-Z0-9_]{3,20}$", username) is not None
username = input("Inserisci il nome utente: ")
if valida_username(username):
print("Input valido")
else:
print("Formato non valido")
🔵 Vantaggi:
✔ Permette solo caratteri accettabili (es. lettere, numeri, underscore).
✔ Evita caratteri pericolosi come '
, --
, ;
, "
.
4. Limitare i Privilegi del Database
🔍 Perché è importante?
Se l’account del database ha troppi privilegi, un attacco SQL Injection potrebbe distruggere o modificare dati sensibili.
✅ Soluzione: Creare un account con privilegi minimi
In MySQL:
sqlCopia codiceCREATE USER 'app_user'@'localhost' IDENTIFIED BY 'password_sicura';
GRANT SELECT, INSERT, UPDATE ON nome_database.* TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
🔵 Vantaggi:
✔ Anche se un attaccante ottiene accesso, non può eliminare il database.
5. Utilizzare un Web Application Firewall (WAF)
🔍 Perché è importante?
Un WAF protegge da attacchi comuni prima che raggiungano il database.
✅ Soluzione: Usare ModSecurity con Apache/Nginx
- Apache
bashCopia codicesudo apt install libapache2-mod-security2
sudo a2enmod security2
sudo systemctl restart apache2
- Nginx
bashCopia codicesudo apt install libnginx-mod-security
🔵 Vantaggi:
✔ Blocca tentativi di SQL Injection in tempo reale.
✔ Protegge anche da altri attacchi web (XSS, CSRF).
6. Evitare l’Output di Errori del Database
🔍 Perché è importante?
Se il database mostra errori dettagliati, un attaccante può dedurre la struttura delle tabelle.
✅ Soluzione: Disabilitare il debug in produzione
- In PHP:
phpCopia codiceini_set('display_errors', 0);
- In Django (Python):
pythonCopia codiceDEBUG = False
🔵 Vantaggi:
✔ Nasconde informazioni sensibili agli attaccanti.
7. Usare Stored Procedures Sicure
🔍 Perché è importante?
Le Stored Procedures predefiniscono le query SQL, impedendo la modifica dinamica delle istruzioni SQL.
✅ Esempio in SQL Server
sqlCopia codiceCREATE PROCEDURE GetUtente
@username NVARCHAR(50)
AS
BEGIN
SELECT * FROM utenti WHERE username = @username
END
E nel codice Python:
pythonCopia codicecursor.execute("EXEC GetUtente @username = ?", (username,))
🔵 Vantaggi:
✔ Il database esegue solo query predefinite.
✔ Non è possibile alterare dinamicamente la query.
8. Monitorare e Loggare le Attività
🔍 Perché è importante?
I log aiutano a rilevare tentativi di attacco.
✅ Soluzione: Abilitare il log delle query sospette
- MySQL
sqlCopia codiceSET GLOBAL general_log = 'ON';
- PostgreSQL
sqlCopia codiceALTER SYSTEM SET log_statement = 'all';
🔵 Vantaggi:
✔ Permette di rilevare e bloccare attacchi in tempo reale.
9. Crittografare i Dati Sensibili
🔍 Perché è importante?
Se un hacker accede al database, le password devono rimanere illeggibili.
✅ Soluzione: Hashing con bcrypt in Python
pythonCopia codiceimport bcrypt
password = "password123"
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
print(hashed) # Salva solo l'hash nel database
🔵 Vantaggi:
✔ Anche se il database viene compromesso, le password rimangono protette.
🔹 Conclusione
Le misure più efficaci per prevenire SQL Injection sono:
✔ Query Parametrizzate
✔ Uso di ORM
✔ Validazione dell’input
✔ Limitazione dei privilegi del database
✔ Uso di un WAF