Verwendung der GET-Request-Methode mit sensiblen Query-Strings
Beschreibung
Verwendung der GET-Request-Methode mit sensiblen Query-Strings tritt auf, wenn eine Anwendung sensible Informationen (Passwörter, Tokens, Session-IDs, persönliche Daten) als URL-Parameter in HTTP-GET-Requests überträgt. GET-Parameter erscheinen in URLs, die in Server-Logs, Browser-Verlauf, Referrer-Headern, Proxy-Logs protokolliert werden und gecacht oder als Lesezeichen gespeichert werden können. Dies exponiert sensible Daten gegenüber unbeabsichtigten Parteien.
Risiko
Sensible Daten in URLs werden in Webserver-Zugriffslogs protokolliert, potenziell zugänglich für Systemadministratoren, Log-Aggregationsdienste oder Angreifer, die Log-Speicher kompromittieren. Browser-Verlauf behält URLs mit sensiblen Parametern. Referrer-Header leaken sensible URLs an Drittanbieter-Sites. Proxies und CDNs können URLs cachen oder protokollieren. Geteilte oder öffentliche Computer exponieren URLs im Browser-Verlauf. URLs können versehentlich als Lesezeichen gespeichert oder geteilt werden.
Lösung
Verwenden Sie POST-Requests mit Daten im Request-Body für sensible Informationen. Implementieren Sie ordnungsgemäße Authentifizierungstokens in Headern (Authorization-Header) statt URLs. Verwenden Sie Session-Cookies statt URL-Parameter für Session-Management. Wenn URL-Parameter unvermeidlich sind, verwenden Sie kurzlebige, einmalige Tokens. Konfigurieren Sie Server, um sensible Parameter aus Logs auszuschließen. Verwenden Sie HTTPS, um Netzwerk-Interception zu verhindern.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Informationsoffenlegung Sensible Daten in Logs, Verlauf und Referrern exponiert. |
| Authentifizierung | Bereich: Credential-Exposure Passwörter und Tokens in URLs sichtbar. |
| Datenschutz | Bereich: Persönliche Datenlecks PII kann durch URL-Parameter exponiert werden. |
Beispielcode + Lösungscode
Verwundbarer Code
<!-- VERWUNDBAR: Login-Formular mit GET -->
<form action="/login" method="GET">
<input type="text" name="username" />
<input type="password" name="password" />
<!-- URL wird: /login?username=alice&password=secret123 -->
<button type="submit">Login</button>
</form>
<!-- VERWUNDBAR: Passwort-Reset mit Token in URL -->
<a href="/reset?token=abc123xyz&[email protected]">
Passwort zurücksetzen
</a>
<!-- Token und E-Mail in URL sichtbar -->
<!-- VERWUNDBAR: API-Schlüssel im Query-String -->
<script>
fetch('/api/data?api_key=sk_live_12345&user_id=789')
.then(response => response.json());
</script>
// VERWUNDBAR: Java-Servlet mit sensiblen GET-Parametern
@WebServlet("/transfer")
public class VulnerableTransferServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
// Sensible Daten aus GET-Parametern
String accountNumber = request.getParameter("account");
String amount = request.getParameter("amount");
String pin = request.getParameter("pin"); // PIN in URL!
// URL: /transfer?account=123456&amount=1000&pin=1234
// Diese URL wird protokolliert, gecacht und ist sichtbar
processTransfer(accountNumber, amount, pin);
}
}
// VERWUNDBAR: Session-Token in URL
public class VulnerableSessionManager {
public String createLoginUrl(String sessionToken) {
// Session-Token in URL exponiert
return "/dashboard?session=" + sessionToken;
}
}
// VERWUNDBAR: URLs mit sensiblen Daten erstellen
public class VulnerableApiClient {
public String buildApiUrl(String apiKey, String userId) {
// API-Schlüssel in URL und Logs sichtbar
return String.format(
"https://api.example.com/users/%s?api_key=%s",
userId, apiKey
);
}
}
# VERWUNDBAR: Flask-Route akzeptiert sensible GET-Parameter
from flask import Flask, request
app = Flask(__name__)
@app.route('/authenticate')
def authenticate_vulnerable():
# Sensible Daten aus GET-Parametern
username = request.args.get('username')
password = request.args.get('password') # Passwort in URL!
# Zugriffs-Log: GET /authenticate?username=admin&password=secret
return validate_credentials(username, password)
# VERWUNDBAR: API mit Schlüssel im Query-String
@app.route('/api/data')
def get_data_vulnerable():
api_key = request.args.get('api_key') # API-Schlüssel wird protokolliert!
return fetch_data(api_key)
# VERWUNDBAR: Passwort-Reset-Token in URL
@app.route('/reset-password')
def reset_password_vulnerable():
token = request.args.get('token')
new_password = request.args.get('new_password') # Passwort in URL!
return process_reset(token, new_password)
// VERWUNDBAR: Client-seitige API-Aufrufe mit sensiblen Parametern
async function authenticateVulnerable(username, password) {
// Passwort in URL sichtbar
const url = `/api/login?username=${username}&password=${password}`;
const response = await fetch(url);
return response.json();
}
// VERWUNDBAR: Token in URL
function redirectWithTokenVulnerable(token) {
// Token in URL, sichtbar im Verlauf und Referrer
window.location.href = `/dashboard?auth_token=${token}`;
}
// VERWUNDBAR: AJAX mit sensiblen Daten in URL
$.ajax({
url: '/api/user',
method: 'GET',
data: {
ssn: '123-45-6789', // Sozialversicherungsnummer in URL!
credit_card: '4111111111111111' // Kreditkarte in URL!
}
});
Lösungscode
<!-- SICHER: Login-Formular mit POST -->
<form action="/login" method="POST">
<input type="text" name="username" />
<input type="password" name="password" />
<!-- Daten werden im Request-Body gesendet, nicht URL -->
<button type="submit">Login</button>
</form>
<!-- SICHER: Passwort-Reset ohne sensible Parameter -->
<a href="/reset?token=abc123xyz">
Passwort zurücksetzen
</a>
<!-- Nur kurzlebiges Token in URL, keine E-Mail/Passwort -->
<!-- SICHER: API-Schlüssel im Header -->
<script>
fetch('/api/data', {
headers: {
'Authorization': 'Bearer sk_live_12345',
'X-User-ID': '789'
}
}).then(response => response.json());
</script>
// SICHER: POST für sensible Operationen verwenden
@WebServlet("/transfer")
public class SafeTransferServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
// Sensible Daten aus POST-Body
String accountNumber = request.getParameter("account");
String amount = request.getParameter("amount");
String pin = request.getParameter("pin");
// Daten nicht in URL, nicht in Zugriffs-Logs protokolliert
processTransfer(accountNumber, amount, pin);
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
// GET-Requests zu Fehler oder Formular umleiten
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
}
// SICHER: Session-Management mit Cookies
public class SafeSessionManager {
public void createSession(HttpServletResponse response, String sessionToken) {
Cookie sessionCookie = new Cookie("session", sessionToken);
sessionCookie.setHttpOnly(true);
sessionCookie.setSecure(true);
sessionCookie.setPath("/");
response.addCookie(sessionCookie);
}
public String redirectToDashboard() {
// Kein Session-Token in URL
return "/dashboard";
}
}
// SICHER: API-Schlüssel in Headern
public class SafeApiClient {
private final String apiKey;
public SafeApiClient(String apiKey) {
this.apiKey = apiKey;
}
public String fetchData(String userId) throws Exception {
URL url = new URL("https://api.example.com/users/" + userId);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// API-Schlüssel im Header, nicht URL
conn.setRequestProperty("Authorization", "Bearer " + apiKey);
conn.setRequestProperty("Content-Type", "application/json");
// Antwort lesen...
return readResponse(conn);
}
}
// SICHER: Request-Body für sensible Daten
public class SafeApiRequest {
public void sendSensitiveData(String creditCard, String cvv)
throws Exception {
URL url = new URL("https://payment.example.com/process");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json");
// Sensible Daten in verschlüsseltem Body
String json = String.format(
"{\"card\":\"%s\",\"cvv\":\"%s\"}",
creditCard, cvv
);
try (OutputStream os = conn.getOutputStream()) {
os.write(json.getBytes(StandardCharsets.UTF_8));
}
}
}
# SICHER: Flask-Route verwendet POST für sensible Daten
from flask import Flask, request, session
app = Flask(__name__)
app.secret_key = 'secure-secret-key'
@app.route('/authenticate', methods=['POST'])
def authenticate_safe():
# Sensible Daten aus POST-Body
data = request.get_json()
username = data.get('username')
password = data.get('password')
# Nicht in Zugriffs-Logs protokolliert
if validate_credentials(username, password):
session['user'] = username
return {'status': 'success'}
return {'status': 'failed'}, 401
# SICHER: API-Schlüssel im Header
@app.route('/api/data')
def get_data_safe():
# API-Schlüssel aus Header, nicht URL
api_key = request.headers.get('Authorization')
if not api_key or not api_key.startswith('Bearer '):
return {'error': 'Unauthorized'}, 401
token = api_key.split(' ')[1]
return fetch_data(token)
# SICHER: Passwort-Reset mit POST
@app.route('/reset-password', methods=['POST'])
def reset_password_safe():
data = request.get_json()
token = data.get('token')
new_password = data.get('new_password')
# Sensible Daten in POST-Body
return process_reset(token, new_password)
# SICHER: Logging konfigurieren um sensible Parameter auszuschließen
import logging
from flask import g
@app.before_request
def log_request_safe():
# Nur sichere Informationen protokollieren
logging.info(f"Request: {request.method} {request.path}")
# Nicht protokollieren: request.args, request.form, request.data
// SICHER: POST-Request für Authentifizierung
async function authenticateSafe(username, password) {
const response = await fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
});
return response.json();
}
// SICHER: Token in Cookie oder Header
function setAuthToken(token) {
// Sicheres Cookie statt URL-Parameter setzen
document.cookie = `auth_token=${token}; Secure; HttpOnly; SameSite=Strict`;
}
async function authenticatedRequest(url, data) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${getTokenFromCookie()}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
return response.json();
}
// SICHER: AJAX mit sensiblen Daten im Body
$.ajax({
url: '/api/user',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({
ssn: '123-45-6789',
credit_card: '4111111111111111'
})
});
Ausgenutzt in der Praxis
Session-Token-Diebstahl via Referrer
Session-Tokens in URLs wurden über Referrer-Header an Drittanbieter-Sites geleakt.
Log-Datei-Exposure
Webserver-Log-Dateien mit Passwörtern in URLs wurden durch Fehlkonfiguration exponiert.
Browser-Verlauf-Exposure
Geteilte Computer enthüllten Passwörter durch Browser-Verlauf und Autovervollständigung.
Tools zum Testen/Ausnutzen
- Burp Suite — Web-Sicherheitstests
- OWASP ZAP — Sicherheitstest-Proxy
- Browser-Entwicklertools — Netzwerkanfragen inspizieren
- Webserver-Log-Analysetools
CVE-Beispiele
- CVEs durch Credentials in GET-Parametern exponiert
- Session-Fixierung durch URL-Tokens
- Datenschutzverletzungen durch PII in URLs
Referenzen
- MITRE. "CWE-598: Use of GET Request Method With Sensitive Query Strings." https://cwe.mitre.org/data/definitions/598.html
- OWASP. "Testing for Sensitive Information in URL." https://owasp.org/