The Password in the Debug Log — JavaScript Bug Hunt

Modelled on Twitter's 2018 disclosure: a bug wrote passwords to an internal log before the hashing step, leaving them readable in plain text.

  • Language: JavaScript
  • Layer: Backend
  • Difficulty: Easy
  • Concepts: Security, Logging
  • Modelled on: Twitter · 2018
  • Visible tests: a correct password authenticates; the password never reaches the log
  • Reward: 50 XP for a complete fix

Briefing

Modelled on Twitter's 2018 disclosure: a bug wrote passwords to an internal log before the hashing step, leaving them readable in plain text. Twitter asked all 330 million users to change their password. GitHub disclosed a near-identical bug the same year.

auth.js logs the whole credential object on every login attempt.

Fix login so no secret ever reaches the log.

Bug report

BUG-TW2018 · Priority: Critical · Reported by: security review

login(credentials, store, logger) must:

  • log exactly one entry of the form { event: "login", username: <username> } — never the password, and never any other secret field
  • return true when store[username] equals hash(password), false otherwise

Observed: the log line contains the raw password. Anyone with log access has every user's credential.

Logs

[auth] login {"username":"kai","password":"hunter2"}
[auth] 330M log lines contain readable credentials

The code as shipped

src/auth/auth.js (editable)

function hash(text) {
  var h = 5381;
  for (var i = 0; i < text.length; i++) {
    h = ((h * 33) ^ text.charCodeAt(i)) >>> 0;
  }
  return "h" + h.toString(16);
}
exports.hash = hash;

exports.login = function (credentials, store, logger) {
  logger.log({ event: "login", username: credentials.username, password: credentials.password });
  return store[credentials.username] === hash(credentials.password);
};

Read-only context: src/auth/logger.js, src/auth/LOGGING.js.

Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.