Use of GET Request Method With Sensitive Query Strings
Description
Use of GET Request Method With Sensitive Query Strings occurs when an application transmits sensitive information (passwords, tokens, session IDs, personal data) as URL parameters in HTTP GET requests. GET parameters appear in URLs, which are logged in server logs, browser history, referrer headers, proxy logs, and may be cached or bookmarked. This exposes sensitive data to unintended parties.
Risk
Sensitive data in URLs is logged in web server access logs, potentially accessible to system administrators, log aggregation services, or attackers who compromise log storage. Browser history retains URLs with sensitive parameters. Referrer headers leak sensitive URLs to third-party sites. Proxies and CDNs may cache or log URLs. Shared or public computers expose URLs in browser history. URLs may be bookmarked or shared accidentally.
Solution
Use POST requests with data in the request body for sensitive information. Implement proper authentication tokens in headers (Authorization header) instead of URLs. Use session cookies instead of URL parameters for session management. If URL parameters are unavoidable, use short-lived, single-use tokens. Configure servers to exclude sensitive parameters from logs. Use HTTPS to prevent network interception.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Information Disclosure Sensitive data exposed in logs, history, and referrers. |
| Authentication | Scope: Credential Exposure Passwords and tokens visible in URLs. |
| Privacy | Scope: Personal Data Leak PII may be exposed through URL parameters. |
Example Code + Solution Code
Vulnerable Code
<!-- VULNERABLE: Login form using GET -->
<form action="/login" method="GET">
<input type="text" name="username" />
<input type="password" name="password" />
<!-- URL becomes: /login?username=alice&password=secret123 -->
<button type="submit">Login</button>
</form>
<!-- VULNERABLE: Password reset with token in URL -->
<a href="/reset?token=abc123xyz&[email protected]">
Reset Password
</a>
<!-- Token and email visible in URL -->
<!-- VULNERABLE: API key in query string -->
<script>
fetch('/api/data?api_key=sk_live_12345&user_id=789')
.then(response => response.json());
</script>
// VULNERABLE: Java servlet with sensitive GET parameters
@WebServlet("/transfer")
public class VulnerableTransferServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
// Sensitive data from GET parameters
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
// This URL is logged, cached, and visible
processTransfer(accountNumber, amount, pin);
}
}
// VULNERABLE: Session token in URL
public class VulnerableSessionManager {
public String createLoginUrl(String sessionToken) {
// Session token exposed in URL
return "/dashboard?session=" + sessionToken;
}
}
// VULNERABLE: Building URLs with sensitive data
public class VulnerableApiClient {
public String buildApiUrl(String apiKey, String userId) {
// API key visible in URL and logs
return String.format(
"https://api.example.com/users/%s?api_key=%s",
userId, apiKey
);
}
}
# VULNERABLE: Flask route accepting sensitive GET params
from flask import Flask, request
app = Flask(__name__)
@app.route('/authenticate')
def authenticate_vulnerable():
# Sensitive data from GET parameters
username = request.args.get('username')
password = request.args.get('password') # Password in URL!
# Access log: GET /authenticate?username=admin&password=secret
return validate_credentials(username, password)
# VULNERABLE: API with key in query string
@app.route('/api/data')
def get_data_vulnerable():
api_key = request.args.get('api_key') # API key logged!
return fetch_data(api_key)
# VULNERABLE: Password reset token in URL
@app.route('/reset-password')
def reset_password_vulnerable():
token = request.args.get('token')
new_password = request.args.get('new_password') # Password in URL!
return process_reset(token, new_password)
// VULNERABLE: Client-side API calls with sensitive params
async function authenticateVulnerable(username, password) {
// Password visible in URL
const url = `/api/login?username=${username}&password=${password}`;
const response = await fetch(url);
return response.json();
}
// VULNERABLE: Token in URL
function redirectWithTokenVulnerable(token) {
// Token in URL, visible in history and referrer
window.location.href = `/dashboard?auth_token=${token}`;
}
// VULNERABLE: AJAX with sensitive data in URL
$.ajax({
url: '/api/user',
method: 'GET',
data: {
ssn: '123-45-6789', // SSN in URL!
credit_card: '4111111111111111' // Credit card in URL!
}
});
Fixed Code
<!-- SAFE: Login form using POST -->
<form action="/login" method="POST">
<input type="text" name="username" />
<input type="password" name="password" />
<!-- Data sent in request body, not URL -->
<button type="submit">Login</button>
</form>
<!-- SAFE: Password reset without sensitive params -->
<a href="/reset?token=abc123xyz">
Reset Password
</a>
<!-- Only short-lived token in URL, no email/password -->
<!-- SAFE: API key in header -->
<script>
fetch('/api/data', {
headers: {
'Authorization': 'Bearer sk_live_12345',
'X-User-ID': '789'
}
}).then(response => response.json());
</script>
// SAFE: Use POST for sensitive operations
@WebServlet("/transfer")
public class SafeTransferServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
// Sensitive data from POST body
String accountNumber = request.getParameter("account");
String amount = request.getParameter("amount");
String pin = request.getParameter("pin");
// Data not in URL, not logged in access logs
processTransfer(accountNumber, amount, pin);
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
// Redirect GET requests to error or form
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
}
// SAFE: Session management with 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() {
// No session token in URL
return "/dashboard";
}
}
// SAFE: API key in headers
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 key in header, not URL
conn.setRequestProperty("Authorization", "Bearer " + apiKey);
conn.setRequestProperty("Content-Type", "application/json");
// Read response...
return readResponse(conn);
}
}
// SAFE: Request body for sensitive data
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");
// Sensitive data in encrypted body
String json = String.format(
"{\"card\":\"%s\",\"cvv\":\"%s\"}",
creditCard, cvv
);
try (OutputStream os = conn.getOutputStream()) {
os.write(json.getBytes(StandardCharsets.UTF_8));
}
}
}
# SAFE: Flask route using POST for sensitive data
from flask import Flask, request, session
app = Flask(__name__)
app.secret_key = 'secure-secret-key'
@app.route('/authenticate', methods=['POST'])
def authenticate_safe():
# Sensitive data from POST body
data = request.get_json()
username = data.get('username')
password = data.get('password')
# Not logged in access logs
if validate_credentials(username, password):
session['user'] = username
return {'status': 'success'}
return {'status': 'failed'}, 401
# SAFE: API key in header
@app.route('/api/data')
def get_data_safe():
# API key from header, not 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)
# SAFE: Password reset with 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')
# Sensitive data in POST body
return process_reset(token, new_password)
# SAFE: Configure logging to exclude sensitive params
import logging
from flask import g
@app.before_request
def log_request_safe():
# Log only safe information
logging.info(f"Request: {request.method} {request.path}")
# Don't log: request.args, request.form, request.data
// SAFE: POST request for authentication
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();
}
// SAFE: Token in cookie or header
function setAuthToken(token) {
// Set secure cookie instead of URL parameter
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();
}
// SAFE: AJAX with sensitive data in body
$.ajax({
url: '/api/user',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({
ssn: '123-45-6789',
credit_card: '4111111111111111'
})
});
Exploited in the Wild
Session Token Theft via Referrer
Session tokens in URLs were leaked to third-party sites through Referrer headers.
Log File Exposure
Web server log files containing passwords in URLs were exposed through misconfiguration.
Browser History Exposure
Shared computers revealed passwords through browser history and autocomplete.
Tools to test/exploit
-
Burp Suite — web security testing.
-
OWASP ZAP — security testing proxy.
-
Browser developer tools — inspect network requests.
-
Web server log analysis tools.
CVE Examples
-
CVEs from credentials exposed in GET parameters.
-
Session fixation through URL tokens.
-
Privacy violations from PII in URLs.
References
-
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/