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

AuswirkungDetails
VertraulichkeitBereich: Informationsoffenlegung

Sensible Daten in Logs, Verlauf und Referrern exponiert.
AuthentifizierungBereich: Credential-Exposure

Passwörter und Tokens in URLs sichtbar.
DatenschutzBereich: 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

  1. MITRE. "CWE-598: Use of GET Request Method With Sensitive Query Strings." https://cwe.mitre.org/data/definitions/598.html
  2. OWASP. "Testing for Sensitive Information in URL." https://owasp.org/