Query-Parameter in REST-APIs richtig kodieren
Query-Parameter in REST-APIs müssen gemäß RFC 3986 prozentkodiert werden, um Sonderzeichen wie &, =, Leerzeichen und Unicode sicher übertragen zu können. Verwende encodeURIComponent() in JavaScript, urllib.parse.quote() in Python oder URLEncoder.encode() in Java.
Warum Query-Parameter kodiert werden müssen
Query-Strings in REST-APIs verwenden ein strukturiertes Format, bei dem Schlüssel-Wert-Paare durch & getrennt werden und Schlüssel durch = von ihren Werten getrennt sind. Enthält ein Parameterwert eines dieser Trennzeichen oder andere reservierte Zeichen wie #, + oder Leerzeichen, bricht die Struktur des Query-Strings auseinander und der Server empfängt fehlerhafte Daten.
Betrachten wir ein Beispiel: Du möchtest in einer API nach "salt & pepper" suchen. Ohne Kodierung teilt die URL /search?q=salt & pepper dem Server mit, dass es zwei Parameter gibt: q=salt und pepper (ohne Wert). Mit korrekter Kodierung überträgt /search?q=salt%20%26%20pepper hingegen korrekt einen einzigen Parameter q mit dem Wert "salt & pepper".
Über die Korrektheit hinaus verhindert eine ordnungsgemäße Kodierung auch Sicherheitslücken. Nicht kodierte Benutzereingaben in URLs können zu Injection-Angriffen, Cache Poisoning und anderen Exploits führen. Kodiere Parameterwerte daher stets, bevor du sie in URLs einfügst.
Kodierung in verschiedenen Sprachen
Jede gängige Programmiersprache stellt integrierte Funktionen zur URL-Kodierung bereit. So kodierst du Query-Parameter in den beliebtesten Sprachen korrekt.
// JavaScript
// Verwende encodeURIComponent() für einzelne Parameterwerte
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"
// Oder verwende URLSearchParams für mehrere Parameter
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
# urlencode für mehrere Parameter verwenden
params = {
'q': 'price >= 100 & category = books',
'page': 1,
'sort': 'price'
}
query_string = urlencode(params)
url = f'https://api.example.com/search?{query_string}'
# quote für einen einzelnen Wert verwenden
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"
// Hinweis: URLEncoder verwendet + für Leerzeichen (Form-Kodierung)
// Für RFC-3986-Kodierung + durch %20 ersetzen
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 folgt RFC 3986
string encoded = Uri.EscapeDataString(query);
// "price%20%3E%3D%20100%20%26%20category%20%3D%20books"
// HttpUtility.UrlEncode verwendet Form-Kodierung (+ für Leerzeichen)
string formEncoded = HttpUtility.UrlEncode(query);
// "price+%3e%3d+100+%26+category+%3d+books"
// Go
package main
import (
"fmt"
"net/url"
)
func main() {
// url.QueryEscape kodiert für Query-Strings (+ für Leerzeichen)
encoded := url.QueryEscape("price >= 100 & category = books")
// "price+%3E%3D+100+%26+category+%3D+books"
// url.PathEscape kodiert für Pfadsegmente (%20 für Leerzeichen)
pathEncoded := url.PathEscape("my file name.pdf")
// "my%20file%20name.pdf"
// url.Values zum Aufbau von Query-Strings verwenden
params := url.Values{}
params.Set("q", "price >= 100 & category = books")
params.Set("page", "1")
queryString := params.Encode()
fmt.Println(queryString)
}
Arrays und verschachtelte Objekte kodieren
REST-APIs müssen häufig Arrays oder verschachtelte Objekte als Query-Parameter entgegennehmen. Für die Kodierung dieser komplexen Typen gibt es keinen einheitlichen Standard, weshalb verschiedene APIs unterschiedliche Konventionen verwenden.
// Wiederholte Schlüssel (am weitesten unterstützt)
// GET /api/items?tag=javascript&tag=python&tag=go
const params = new URLSearchParams();
['javascript', 'python', 'go'].forEach(tag => params.append('tag', tag));
// Klammer-Notation (verbreitet in PHP, Rails, Express mit qs)
// GET /api/items?tags[]=javascript&tags[]=python&tags[]=go
// Muss manuell kodiert werden:
const tags = ['javascript', 'python', 'go'];
const qs = tags.map(t => 'tags[]=' + encodeURIComponent(t)).join('&');
// Kommagetrennt (einfach, aber eingeschränkt)
// GET /api/items?tags=javascript,python,go
const tagList = encodeURIComponent('javascript,python,go');
// Verschachtelte Objekte (Klammer-Notation)
// GET /api/search?filter[status]=active&filter[min_price]=10
// In Python:
params = {
'filter[status]': 'active',
'filter[min_price]': '10'
}
Prüfe stets die Dokumentation deiner API auf das erwartete Format. Beim Entwurf einer neuen API ist der Ansatz mit wiederholten Schlüsseln über Programmiersprachen und HTTP-Bibliotheken hinweg am portabelsten.
Best Practices für die serverseitige Dekodierung
Bei der Verarbeitung eingehender Anfragen auf der Serverseite dekodieren die meisten Web-Frameworks Query-Parameter automatisch für dich. Dennoch gibt es wichtige Aspekte, die du für eine robuste serverseitige Verarbeitung beachten solltest.
- Die meisten Frameworks (Express, Django, Spring, ASP.NET) dekodieren Query-Parameter automatisch. Dekodiere sie nicht manuell erneut, sonst kommt es zu einer doppelten Dekodierung.
- Validiere dekodierte Werte gegen erwartete Typen und Wertebereiche, bevor du sie verwendest.
- Lege auf Ebene des Webservers Obergrenzen für die URL-Länge fest (üblicherweise 2048 oder 8192 Zeichen).
- Berücksichtige den Fall, dass
+je nach Content-Type entweder ein Leerzeichen oder ein wörtliches Pluszeichen bedeuten kann. - Protokolliere die ursprüngliche (kodierte) URL zur Fehlersuche, da dekodierte Werte in Logs irreführend sein können.
// Express.js - Parameter werden automatisch dekodiert
app.get('/search', (req, res) => {
const query = req.query.q; // Bereits dekodiert
// NICHT verwenden: decodeURIComponent(req.query.q)
// Eingabe validieren
if (typeof query !== 'string' || query.length > 200) {
return res.status(400).json({ error: 'Invalid query parameter' });
}
// Den dekodierten Wert sicher verwenden
const results = search(query);
res.json(results);
});
Sicherheitsaspekte
Die ordnungsgemäße Kodierung von Query-Parametern ist ein zentraler Bestandteil der API-Sicherheit. Hier sind die wichtigsten Sicherheitspraktiken, die du befolgen solltest.
- Vertraue niemals der clientseitigen Kodierung. Validiere und bereinige dekodierte Parameter stets auf dem Server, selbst wenn der Client sie korrekt kodiert hat.
- Achte auf Double-Encoding-Angriffe. Ein Angreifer könnte bösartige Zeichen doppelt kodieren, um Eingabefilter zu umgehen, die nur einmal dekodieren. Stelle sicher, dass deine Sicherheitsfilter die vollständig dekodierten Werte sehen.
- Verhindere Path Traversal. Kodierte Sequenzen wie
%2e%2e%2f(../) in Pfadparametern können genutzt werden, um auf Dateien außerhalb des vorgesehenen Verzeichnisses zuzugreifen. Dekodiere und validiere Pfade vor Dateioperationen. - Verwende parametrisierte Abfragen. Verkette Benutzereingaben auch nach der URL-Dekodierung niemals direkt in SQL- oder NoSQL-Abfragen. Verwende parametrisierte Abfragen oder ein ORM.
- Begrenze die URL-Länge. Extrem lange Query-Strings können einen Denial-of-Service verursachen. Konfiguriere deinen Webserver so, dass er URLs oberhalb einer angemessenen Länge ablehnt.
- Kodiere auch die Ausgabe. Wenn du Query-Parameterwerte in HTML-Antworten zurückspiegelst, wende HTML-Entity-Kodierung an, um Cross-Site-Scripting-Angriffe (XSS) zu verhindern.