Comment encoder correctement les paramètres de requête dans les API REST
Les paramètres de requête des API REST doivent être encodés en pourcentage conformément à la RFC 3986 afin d'inclure sans risque des caractères spéciaux comme &, =, les espaces et l'Unicode. Utilisez encodeURIComponent() en JavaScript, urllib.parse.quote() en Python ou URLEncoder.encode() en Java.
Pourquoi les paramètres de requête doivent être encodés
Les chaînes de requête dans les API REST utilisent un format structuré où les paires clé-valeur sont séparées par & et où les clés sont séparées de leurs valeurs par =. Si la valeur d'un paramètre contient l'un de ces caractères délimiteurs, ou d'autres caractères réservés comme #, + ou des espaces, la structure de la chaîne de requête est rompue et le serveur reçoit des données incorrectes.
Prenons cet exemple : vous souhaitez rechercher « salt & pepper » dans une API. Sans encodage, l'URL /search?q=salt & pepper indique au serveur qu'il existe deux paramètres : q=salt et pepper (sans valeur). Avec un encodage correct, /search?q=salt%20%26%20pepper envoie correctement un unique paramètre q dont la valeur est « salt & pepper ».
Au-delà de la justesse des données, un encodage correct prévient également les failles de sécurité. Une saisie utilisateur non encodée dans les URL peut conduire à des attaques par injection, à l'empoisonnement de cache et à d'autres exploits. Encodez toujours les valeurs des paramètres avant de les inclure dans les URL.
L'encodage dans différents langages
Tous les principaux langages de programmation fournissent des fonctions intégrées pour l'encodage d'URL. Voici comment encoder correctement les paramètres de requête dans les langages les plus populaires.
// JavaScript
// Utilisez encodeURIComponent() pour les valeurs de paramètres individuelles
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"
// Ou utilisez URLSearchParams pour plusieurs paramètres
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
# En utilisant urlencode pour plusieurs paramètres
params = {
'q': 'price >= 100 & category = books',
'page': 1,
'sort': 'price'
}
query_string = urlencode(params)
url = f'https://api.example.com/search?{query_string}'
# En utilisant quote pour une seule valeur
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"
// Remarque : URLEncoder utilise + pour les espaces (encodage de formulaire)
// Pour un encodage conforme à la RFC 3986, remplacez + par %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 suit la RFC 3986
string encoded = Uri.EscapeDataString(query);
// "price%20%3E%3D%20100%20%26%20category%20%3D%20books"
// HttpUtility.UrlEncode utilise l'encodage de formulaire (+ pour les espaces)
string formEncoded = HttpUtility.UrlEncode(query);
// "price+%3e%3d+100+%26+category+%3d+books"
// Go
package main
import (
"fmt"
"net/url"
)
func main() {
// url.QueryEscape encode pour les chaînes de requête (+ pour les espaces)
encoded := url.QueryEscape("price >= 100 & category = books")
// "price+%3E%3D+100+%26+category+%3D+books"
// url.PathEscape encode pour les segments de chemin (%20 pour les espaces)
pathEncoded := url.PathEscape("my file name.pdf")
// "my%20file%20name.pdf"
// En utilisant url.Values pour construire des chaînes de requête
params := url.Values{}
params.Set("q", "price >= 100 & category = books")
params.Set("page", "1")
queryString := params.Encode()
fmt.Println(queryString)
}
Encodage des tableaux et des objets imbriqués
Les API REST doivent souvent accepter des tableaux ou des objets imbriqués comme paramètres de requête. Il n'existe pas de standard unique pour encoder ces types complexes, si bien que les différentes API adoptent des conventions différentes.
// Clés répétées (le plus largement pris en charge)
// GET /api/items?tag=javascript&tag=python&tag=go
const params = new URLSearchParams();
['javascript', 'python', 'go'].forEach(tag => params.append('tag', tag));
// Notation à crochets (courante en PHP, Rails, Express avec qs)
// GET /api/items?tags[]=javascript&tags[]=python&tags[]=go
// Doit être encodée manuellement :
const tags = ['javascript', 'python', 'go'];
const qs = tags.map(t => 'tags[]=' + encodeURIComponent(t)).join('&');
// Séparés par des virgules (simple mais limité)
// GET /api/items?tags=javascript,python,go
const tagList = encodeURIComponent('javascript,python,go');
// Objets imbriqués (notation à crochets)
// GET /api/search?filter[status]=active&filter[min_price]=10
// En Python :
params = {
'filter[status]': 'active',
'filter[min_price]': '10'
}
Vérifiez toujours la documentation de votre API pour connaître le format attendu. Lors de la conception d'une nouvelle API, l'approche par clés répétées est la plus portable d'un langage de programmation et d'une bibliothèque HTTP à l'autre.
Bonnes pratiques de décodage côté serveur
Lors du traitement des requêtes entrantes côté serveur, la plupart des frameworks web décodent automatiquement les paramètres de requête pour vous. Il existe toutefois des points importants à garder à l'esprit pour un traitement côté serveur robuste.
- La plupart des frameworks (Express, Django, Spring, ASP.NET) décodent automatiquement les paramètres de requête. Ne les décodez pas de nouveau manuellement, sous peine de provoquer un double décodage.
- Validez les valeurs décodées par rapport aux types et aux plages attendus avant de les utiliser.
- Définissez des limites de longueur d'URL maximale au niveau du serveur web (généralement 2048 ou 8192 caractères).
- Gérez le cas où
+peut signifier soit un espace, soit un signe plus littéral, selon le type de contenu. - Journalisez l'URL originale (encodée) à des fins de débogage, car les valeurs décodées peuvent être trompeuses dans les journaux.
// Express.js - les paramètres sont décodés automatiquement
app.get('/search', (req, res) => {
const query = req.query.q; // Déjà décodé
// NE PAS faire : decodeURIComponent(req.query.q)
// Validez la saisie
if (typeof query !== 'string' || query.length > 200) {
return res.status(400).json({ error: 'Invalid query parameter' });
}
// Utilisez la valeur décodée en toute sécurité
const results = search(query);
res.json(results);
});
Considérations de sécurité
Un encodage correct des paramètres de requête est un élément clé de la sécurité des API. Voici les pratiques de sécurité les plus importantes à suivre.
- Ne faites jamais confiance à l'encodage côté client. Validez et assainissez toujours les paramètres décodés sur le serveur, même si le client les a correctement encodés.
- Méfiez-vous des attaques par double encodage. Un attaquant peut encoder deux fois des caractères malveillants pour contourner les filtres de saisie qui ne décodent qu'une seule fois. Assurez-vous que vos filtres de sécurité voient les valeurs entièrement décodées.
- Empêchez la traversée de répertoires. Des séquences encodées comme
%2e%2e%2f(../) dans les paramètres de chemin peuvent servir à accéder à des fichiers situés en dehors du répertoire prévu. Décodez et validez les chemins avant toute opération sur les fichiers. - Utilisez des requêtes paramétrées. Même après le décodage d'URL, ne concaténez jamais directement une saisie utilisateur dans des requêtes SQL ou NoSQL. Utilisez des requêtes paramétrées ou un ORM.
- Limitez la longueur des URL. Des chaînes de requête extrêmement longues peuvent provoquer un déni de service. Configurez votre serveur web pour rejeter les URL au-delà d'une longueur raisonnable.
- Encodez aussi la sortie. Lorsque vous réaffichez des valeurs de paramètres de requête dans des réponses HTML, appliquez un encodage des entités HTML pour prévenir les attaques par injection de scripts (XSS).