HIGH session fixationhmac signatures

Session Fixation with Hmac Signatures

How Session Fixation Manifests in Hmac Signatures

Session fixation occurs when an attacker forces a user to accept a known session identifier, then later uses that identifier to hijack the user’s authenticated session. When HMAC‑based signatures are used to protect session tokens (for example, signing a cookie value with a secret key), the vulnerability can appear if the server accepts a client‑provided token, verifies its HMAC, and then treats the token as valid without issuing a new identifier after login.

Consider a typical pattern in a Node.js API that signs a session ID:

const crypto = require('crypto');
function signSessionId(id, secret) {
  return crypto.createHmac('sha256', secret).update(id).digest('hex');
}
// Middleware that trusts any cookie with a valid HMAC
function sessionMiddleware(req, res, next) {
  const cookie = req.headers.cookie;
  const match = cookie && cookie.match(/session=([^;]+)/);
  if (match) {
    const [providedId, providedSig] = match[1].split('.');
    const expectedSig = signSessionId(providedId, process.env.SESSION_SECRET);
    if (providedSig === expectedSig) {
      req.sessionId = providedId; // <-- fixation point
      return next();
    }
  }
  res.status(401).send('Invalid session');
}

If an attacker can set the session cookie before the victim logs in (e.g., via a cross‑site scripting payload or a crafted link), the victim’s browser will send that known identifier. After login, the server still sees the same req.sessionId because it never replaces the identifier. The attacker, who knows the original identifier, can now reuse the valid HMAC‑signed token and gain access to the victim’s account.

This pattern maps to OWASP API Security Top 10 A2: Broken Authentication, and has been observed in real‑world flaws such as CVE‑2020-13943 (Apache Tomcat session fixation) where the server accepted a user‑supplied JSESSIONID without regeneration.

Hmac Signatures-Specific Detection

Detecting session fixation in HMAC‑protected sessions requires checking whether the server ever issues a new session identifier (and consequently a new HMAC) after a successful authentication event. middleBrick’s unauthenticated black‑box scan looks for the following indicators:

  • The API accepts a session token presented in a cookie, header, or query parameter and verifies its HMAC without requiring any additional proof of possession (e.g., a password or OAuth token).
  • After a login endpoint (/auth/login) returns a success status, the response does not contain a Set‑Cookie header with a new session value, nor does it return a freshly signed token in the body.
  • The same token value that was sent in the request continues to be accepted in subsequent authenticated calls.

For example, middleBrick will send a request to GET /profile with a cookie session=attacker123.abcdef.... If the API responds with 200 OK and the same cookie is still valid after a POST to /login with valid credentials, the scanner flags a potential session fixation.

The finding includes:

PropertyValue
CategoryBroken Authentication
SeverityHigh
EvidencePre‑login token accepted post‑login without regeneration
Remediation GuidanceGenerate a new session identifier and HMAC after successful authentication.

Because middleBrick performs the check without needing any credentials or agents, it can be run from the CLI, a GitHub Action, or directly inside an IDE via the MCP Server.

Hmac Signatures-Specific Remediation

The fix is to ensure that, after a user proves their identity, the server discards any client‑provided session identifier and creates a fresh one. The new identifier must be signed with the HMAC secret and transmitted to the client (usually via a Set‑Cookie header or response body).

Below is a corrected Node.js example that regenerates the session identifier after login:

const crypto = require('crypto');
function signSessionId(id, secret) {
  return crypto.createHmac('sha256', secret).update(id).digest('hex');
}
function generateSessionId() {
  return crypto.randomBytes(16).toString('hex');
}
// Login handler
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  // … verify credentials …
  if (valid) {
    const newId = generateSessionId();
    const sig = signSessionId(newId, process.env.SESSION_SECRET);
    const token = `${newId}.${sig}`;
    res.setHeader('Set‑Cookie', `session=${token}; HttpOnly; SameSite=Strict`);
    return res.json({ msg: 'logged in' });
  }
  res.status(401).send('Invalid credentials');
});
// Session verification middleware (unchanged)
function sessionMiddleware(req, res, next) {
  const cookie = req.headers.cookie;
  const match = cookie && cookie.match(/session=([^;]+)/);
  if (match) {
    const [id, sig] = match[1].split('.');
    if (sig === signSessionId(id, process.env.SESSION_SECRET)) {
      req.sessionId = id;
      return next();
    }
  }
  res.status(401).send('Invalid session');
}

Key points:

  • generateSessionId() creates a cryptographically random identifier that the attacker cannot predict.
  • The login endpoint always issues a Set‑Cookie header with the new HMAC‑signed token, overriding any old value.
  • The verification middleware remains the same; it will reject the old token because its signature no longer matches the newly issued secret‑derived value.

Similar patterns apply in other languages:

import hmac, hashlib, os, secrets
SECRET = os.environ['SESSION_SECRET'].encode()

def sign_session_id(session_id):
    return hmac.new(SECRET, session_id.encode(), hashlib.sha256).hexdigest()

def generate_session_id():
    return secrets.token_urlsafe(16)

@app.route('/login', methods=['POST'])
def login():
    # validate username/password …
    new_id = generate_session_id()
    token = f'{new_id}.{sign_session_id(new_id)}'
    resp = make_response(jsonify({'msg': 'logged in'}))
    resp.set_cookie('session', token, httponly=True, samesite='Strict')
    return resp

@app.before_request
def verify_session():
    token = request.cookies.get('session')
    if token:
        try:
            uid, sig = token.split('.')
            if hmac.compare_digest(sig, sign_session_id(uid)):
                request.session_id = uid
                return
        except ValueError:
            pass
    return jsonify({'error': 'Invalid session'}), 401

Deploying the fixed code and rescanning with middleBrick (via middlebrick scan https://api.example.com, the GitHub Action, or the MCP Server) will show the session fixation finding resolved and the security score improved.

Frequently Asked Questions

Does middleBrick need any credentials or agents to detect session fixation in HMAC‑signed sessions?
No. middleBrick performs an unauthenticated black‑box scan by simply sending HTTP requests to the target URL. It looks for whether a pre‑login HMAC‑signed token continues to be accepted after a successful login, without requiring any installation, API keys, or agents on the host.
Can the session fixation issue be fixed by only strengthening the HMAC secret?
No. A strong secret prevents an attacker from forging a valid signature, but it does not stop them from using a known, valid token they forced the victim to accept. The defense must involve issuing a new, unpredictable session identifier (and therefore a new HMAC) after authentication.