Come codificare correttamente i parametri di query nelle API REST
I parametri di query delle API REST devono essere codificati in percent-encoding secondo l'RFC 3986 per includere in sicurezza caratteri speciali come &, =, spazi e Unicode. Usa encodeURIComponent() in JavaScript, urllib.parse.quote() in Python o URLEncoder.encode() in Java.
Perché i parametri di query hanno bisogno della codifica
Le query string nelle API REST usano un formato strutturato in cui le coppie chiave-valore sono separate da & e le chiavi sono separate dai valori tramite =. Se il valore di un parametro contiene uno di questi caratteri delimitatori, oppure altri caratteri riservati come #, + o gli spazi, la struttura della query string si rompe e il server riceve dati errati.
Considera questo esempio: vuoi cercare "salt & pepper" in un'API. Senza codifica, l'URL /search?q=salt & pepper comunica al server che ci sono due parametri: q=salt e pepper (senza valore). Con una codifica corretta, /search?q=salt%20%26%20pepper invia correttamente un unico parametro q con il valore "salt & pepper".
Oltre alla correttezza, una codifica appropriata previene anche le vulnerabilità di sicurezza. L'input dell'utente non codificato negli URL può portare ad attacchi di injection, cache poisoning e altri exploit. Codifica sempre i valori dei parametri prima di includerli negli URL.
Codifica nei diversi linguaggi
Ogni linguaggio di programmazione importante fornisce funzioni integrate per la codifica degli URL. Ecco come codificare correttamente i parametri di query nei linguaggi più diffusi.
// JavaScript
// Usa encodeURIComponent() per i valori dei singoli parametri
const query = 'price >= 100 & category = books';
const url = 'https://api.example.com/search?q=' + encodeURIComponent(query);
// "https://api.example.com/search?q=price%20%3E%3D%20100%20%26%20category%20%3D%20books"
// Oppure usa URLSearchParams per più parametri
const params = new URLSearchParams({
q: 'price >= 100 & category = books',
page: '1',
sort: 'price'
});
const url2 = 'https://api.example.com/search?' + params.toString();
# Python
from urllib.parse import urlencode, quote
# Uso di urlencode per più parametri
params = {
'q': 'price >= 100 & category = books',
'page': 1,
'sort': 'price'
}
query_string = urlencode(params)
url = f'https://api.example.com/search?{query_string}'
# Uso di quote per un singolo valore
value = quote('price >= 100 & category = books', safe='')
url = f'https://api.example.com/search?q={value}'
// Java
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
String query = "price >= 100 & category = books";
String encoded = URLEncoder.encode(query, StandardCharsets.UTF_8);
// "price+%3E%3D+100+%26+category+%3D+books"
// Nota: URLEncoder usa + per gli spazi (form encoding)
// Per la codifica secondo l'RFC 3986, sostituisci + con %20
String rfc3986 = encoded.replace("+", "%20");
String url = "https://api.example.com/search?q=" + rfc3986;
// C#
using System.Net;
using System.Web;
string query = "price >= 100 & category = books";
// Uri.EscapeDataString segue l'RFC 3986
string encoded = Uri.EscapeDataString(query);
// "price%20%3E%3D%20100%20%26%20category%20%3D%20books"
// HttpUtility.UrlEncode usa il form encoding (+ per gli spazi)
string formEncoded = HttpUtility.UrlEncode(query);
// "price+%3e%3d+100+%26+category+%3d+books"
// Go
package main
import (
"fmt"
"net/url"
)
func main() {
// url.QueryEscape codifica per le query string (+ per gli spazi)
encoded := url.QueryEscape("price >= 100 & category = books")
// "price+%3E%3D+100+%26+category+%3D+books"
// url.PathEscape codifica per i segmenti di path (%20 per gli spazi)
pathEncoded := url.PathEscape("my file name.pdf")
// "my%20file%20name.pdf"
// Uso di url.Values per costruire le query string
params := url.Values{}
params.Set("q", "price >= 100 & category = books")
params.Set("page", "1")
queryString := params.Encode()
fmt.Println(queryString)
}
Codifica di array e oggetti annidati
Le API REST hanno spesso bisogno di accettare array o oggetti annidati come parametri di query. Non esiste uno standard unico per la codifica di questi tipi complessi, quindi API diverse usano convenzioni diverse.
// Chiavi ripetute (l'approccio più ampiamente supportato)
// GET /api/items?tag=javascript&tag=python&tag=go
const params = new URLSearchParams();
['javascript', 'python', 'go'].forEach(tag => params.append('tag', tag));
// Notazione con parentesi (comune in PHP, Rails, Express con qs)
// GET /api/items?tags[]=javascript&tags[]=python&tags[]=go
// Deve essere codificata manualmente:
const tags = ['javascript', 'python', 'go'];
const qs = tags.map(t => 'tags[]=' + encodeURIComponent(t)).join('&');
// Separati da virgole (semplice ma limitato)
// GET /api/items?tags=javascript,python,go
const tagList = encodeURIComponent('javascript,python,go');
// Oggetti annidati (notazione con parentesi)
// GET /api/search?filter[status]=active&filter[min_price]=10
// In Python:
params = {
'filter[status]': 'active',
'filter[min_price]': '10'
}
Controlla sempre la documentazione della tua API per conoscere il formato atteso. Quando progetti una nuova API, l'approccio con le chiavi ripetute è il più portabile tra i vari linguaggi di programmazione e le librerie HTTP.
Best practice per la decodifica lato server
Nella gestione delle richieste in arrivo lato server, la maggior parte dei framework web decodifica automaticamente i parametri di query al posto tuo. Tuttavia, ci sono considerazioni importanti da tenere a mente per una gestione lato server robusta.
- La maggior parte dei framework (Express, Django, Spring, ASP.NET) decodifica automaticamente i parametri di query. Non decodificarli di nuovo manualmente, altrimenti otterrai una doppia decodifica.
- Valida i valori decodificati rispetto ai tipi e agli intervalli attesi prima di utilizzarli.
- Imposta limiti massimi di lunghezza degli URL a livello di web server (comunemente 2048 o 8192 caratteri).
- Gestisci il caso in cui
+potrebbe significare sia uno spazio sia un segno più letterale, a seconda del content type. - Registra nei log l'URL originale (codificato) per il debug, poiché i valori decodificati possono risultare fuorvianti nei log.
// Express.js - i parametri vengono decodificati automaticamente
app.get('/search', (req, res) => {
const query = req.query.q; // Già decodificato
// NON fare: decodeURIComponent(req.query.q)
// Valida l'input
if (typeof query !== 'string' || query.length > 200) {
return res.status(400).json({ error: 'Invalid query parameter' });
}
// Usa in sicurezza il valore decodificato
const results = search(query);
res.json(results);
});
Considerazioni sulla sicurezza
Una corretta codifica dei parametri di query è un elemento chiave della sicurezza delle API. Ecco le pratiche di sicurezza più importanti da seguire.
- Non fidarti mai della codifica lato client. Valida e sanifica sempre i parametri decodificati sul server, anche se il client li ha codificati correttamente.
- Attenzione agli attacchi di doppia codifica. Un attaccante potrebbe codificare due volte i caratteri malevoli per aggirare i filtri di input che decodificano una sola volta. Assicurati che i tuoi filtri di sicurezza vedano i valori completamente decodificati.
- Previeni il path traversal. Sequenze codificate come
%2e%2e%2f(../) nei parametri di path possono essere usate per accedere a file al di fuori della directory prevista. Decodifica e valida i path prima delle operazioni sui file. - Usa query parametrizzate. Anche dopo la decodifica dell'URL, non concatenare mai l'input dell'utente direttamente nelle query SQL o NoSQL. Usa query parametrizzate o un ORM.
- Limita la lunghezza degli URL. Query string estremamente lunghe possono causare un denial-of-service. Configura il tuo web server per rifiutare gli URL che superano una lunghezza ragionevole.
- Codifica anche l'output. Quando riporti i valori dei parametri di query nelle risposte HTML, applica la codifica delle entità HTML per prevenire gli attacchi di cross-site scripting (XSS).