Blog | G5 Cyber Security

Secure Query String Authentication

TL;DR

Passing authentication details in a query string is insecure. This guide shows how to move authentication to cookies or headers and protect against common attacks.

Why Query String Auth is Bad

Putting usernames, passwords, or tokens directly into the URL (the query string) has several problems:

How to Fix It

The best approach is to avoid using query string authentication altogether. Here’s how to move authentication data securely:

1. Use Cookies (Recommended)

  1. Authentication Process: When a user logs in, verify their credentials on the server-side.
  2. Create a Session ID: Generate a unique session identifier (e.g., using a random string). Store this ID securely on the server and associate it with the user’s account.
  3. Set a Secure Cookie: Send a cookie to the browser containing the session ID. Crucially, set the following attributes:
    • HttpOnly: Prevents JavaScript from accessing the cookie (protects against XSS attacks).
    • Secure: Only sends the cookie over HTTPS connections.
    • SameSite=Strict or SameSite=Lax: Helps prevent CSRF attacks. `Strict` is more secure but may break some legitimate cross-site functionality; `Lax` offers a good balance.
  4. Subsequent Requests: On subsequent requests, the browser automatically sends the cookie to your server. Your server uses the session ID in the cookie to identify the user.

Example (Python/Flask):

from flask import Flask, request, make_response, redirect, url_for
import secrets

session_data = {}

app = Flask(__name__)
app.secret_key = secrets.token_hex(16) # Important: Use a strong secret key!

@app.route('/login', methods=['POST'])
def login():
    # ... (verify username and password here)
    session_id = secrets.token_hex(16)
    session_data[session_id] = 'user123' # Store user info associated with the session ID
    resp = make_response(redirect(url_for('home')))
    resp.set_cookie('session_id', session_id, httponly=True, secure=True, samesite='Strict')
    return resp

@app.route('/home')
def home():
    session_id = request.cookies.get('session_id')
    if session_id in session_data:
        user_info = session_data[session_id]
        return f'Welcome, {user_info}!'
    else:
        return redirect(url_for('login'))

2. Use Headers

  1. Authentication Process: Similar to cookies – verify credentials on the server-side and create a token (e.g., JWT).
  2. Send Token in Header: Instead of setting a cookie, send the token in an HTTP header (e.g., Authorization: Bearer <token>).
  3. Subsequent Requests: The client application includes the header with each request.

Example (JavaScript/Fetch):

async function fetchData(token) {
  const response = await fetch('/api/data', {
    headers: {
      'Authorization': 'Bearer ' + token
    }
  });
  // ... handle the response
}

3. Redirect After Authentication

If you absolutely *must* use a query string temporarily (e.g., for Single Sign-On redirects), redirect the user to another page immediately after authentication, removing the sensitive information from the URL.

Important Security Considerations

Exit mobile version