No re-authentication for sensitive changes
CWE-620CWE-306OWASP A07:2021Updated September 4, 20265 min read
If an email address, phone number or password can be changed without the current password being requested again, one hijacked session is enough for a lasting account takeover. The attacker puts the recovery channel in their own name and locks the rightful owner out.
A hijacked session is unpleasant but temporary: it expires, or the user logs out. Unless the attacker can put the account’s recovery channel in their own name within that window. Then a temporary problem becomes a permanent loss. That one extra request for the password is what makes the difference.
What is re-authentication?
Re-authentication is making a user prove their identity again at the moment they want to do something sensitive, even though they are already logged in. In practice it means asking for the current password, and preferably the second factor as well, before the change is carried out.
The reason lies in the difference between two questions. A valid session proves that someone logged in at some point. It does not prove that the person now at the keyboard is that person. That difference is usually acceptable: when viewing an overview or drafting a message, the session is sufficient grounds. When changing the keys to the account, it is not.
At a bank you may walk in with your card and arrange a few things at the counter. For changing your address or requesting a new card, extra identification is asked for. Not because you are suspect, but because those actions determine who gets in from now on.
What can an attacker do without re-authentication?
Vulnerable:
// The session is the only condition
app.post('/account/email', requireLogin, async (req, res) => {
await users.changeEmail(req.user.id, req.body.newEmail);
res.send('Email address updated');
});
app.post('/account/password', requireLogin, async (req, res) => {
await users.setPassword(req.user.id, req.body.newPassword);
res.send('Password updated');
});
An attacker who obtains a session in any way, through an unattended workstation, a stolen cookie, cross-site scripting or a shared computer, now makes two requests. First they set the email address to one they control. Then they change the password.
From that moment the situation is reversed. The rightful owner can no longer log in, because the password has changed. They cannot request a recovery, because the recovery mail goes to the attacker’s address. And their own session expires by itself. What began as temporary access has become a permanent takeover, and getting it back requires the intervention of your service desk.
Safe:
async function confirmIdentity(req) {
// Recently confirmed? Then do not ask again within that window
if (req.session.confirmedAt > Date.now() - 10 * 60 * 1000) return true;
const user = await users.find(req.user.id);
const passwordOk = await argon2.verify(user.hash, req.body.currentPassword ?? '');
if (!passwordOk) return false;
if (user.mfaEnabled && !await checkMfa(user, req.body.mfaCode)) {
return false;
}
req.session.confirmedAt = Date.now();
return true;
}
app.post('/account/email', requireLogin, async (req, res) => {
if (!await confirmIdentity(req)) {
return res.status(403).send('Please confirm your identity first');
}
const old = req.user.email;
// New address only becomes active after confirmation through that address
await users.startEmailChange(req.user.id, req.body.newEmail);
// Warning to the old address, with a way back
await mail.send(old, 'A change of your email address has been requested. ' +
'Was this not you? Use this link to block the change: ...');
res.send('Check your new email address to confirm the change');
});
Three layers here each make the attack harder on their own. Identity is confirmed again, with a short validity period so a user adjusting several settings does not have to keep retyping. The new address only becomes active after confirmation through that address. And the old address receives a warning with a way to intervene, which is precisely the notification the rightful owner needs to catch it in time.
What is the impact of missing re-authentication?
The severity runs from medium to high. The distinguishing feature is that this finding amplifies the consequences of every other problem: it determines whether a temporary compromise stays temporary.
Without re-authentication, every hijacked session is a potential permanent takeover. That applies to the classic scenarios, an unattended workstation, a shared computer, a stolen cookie, but also to brief situations that would otherwise pass without consequence, such as a colleague glancing at an unlocked screen.
The consequence reaches beyond the account itself. Whoever controls the email address of an account can often also get into linked services that use the same address for recovery. And because the rightful owner is locked out, the problem moves to your service desk, which has to establish who the real owner is, a process that forms an attack surface of its own.
How do you detect missing re-authentication?
A tester walks through the account settings and tries, for each sensitive action, whether it can be completed without the current password: changing the email address, the phone number and the password, disabling two-factor authentication, creating API keys and deleting the account.
What matters here is the check on the server. Applications sometimes do ask for the current password in the form but do not actually verify it; a direct request without that field then simply succeeds. That is tested explicitly. They also check whether a change of email address only takes effect after confirmation through the new address, whether the old address receives a warning, and whether existing sessions are revoked after a password change. Whether the API imposes the same requirements as the web interface is assessed too, because in practice those two regularly diverge. AssistSec tests these points as a coherent whole, because resilience is determined by the weakest of the routes involved.
How do you prevent missing re-authentication?
- Ask for the current password before changing a password, email address or phone number.
- Ask for the second factor as well when it is enabled.
- Verify that confirmation on the server and not only in the form.
- Let a new email address take effect only after confirmation through that address.
- Send a warning to the old address, with an option to block the change.
- Revoke all other sessions and tokens after a password change.
- Ask for confirmation again when disabling two-factor authentication and when creating API keys.
- Keep the confirmation valid briefly, so a user can adjust several settings without retyping each time.
- Impose the same requirements on the API as on the web interface.
Sources
Frequently asked questions
Is this not annoying for users?
It is a small hurdle at a small number of moments. Users change their email address or password a few times a year at most, and they are used to being asked for confirmation precisely there. The inconvenience bears no relation to the difference in outcome when a session is hijacked.
Which actions call for re-authentication?
Anything touching access to the account or anything irreversible: changing the password, email address or phone number, disabling two-factor authentication, creating API keys, changing payment details and deleting the account.
Is asking for the old password enough?
For most applications, yes. If you use two-factor authentication, preferably ask for the second factor as well, because an attacker who knows the password but hijacked the session would otherwise still get through. A short window in which the confirmation stays valid keeps it workable.
Should I inform the user about the change?
Yes, always, and send that notification to the old address. That is the only way the rightful owner notices something has changed once the attacker has already set the new address. Include a way to undo the change in the notification.
Related articles
- VulnerabilitiesCWE-287A07:2021Broken authenticationBroken authentication explained: how attackers take over accounts through brute force, leaked passwords and predictable session tokens, and how to stop them.
- VulnerabilitiesCWE-352A01:2021Cross-site request forgery (CSRF)Cross-site request forgery (CSRF) explained: how an attacker abuses a logged-in user's browser to perform unwanted actions, and how you prevent it.
- VulnerabilitiesCWE-308A07:2021Multi-factor authentication is missingWithout a second factor, a leaked password is enough for full access. Learn which MFA methods offer protection and which do not.
- VulnerabilitiesCWE-613A07:2021Session stays valid after logoutA logout that only clears the cookie leaves the token intact on the server. Learn how a captured session then simply keeps working.
- VulnerabilitiesCWE-613A07:2021Sessions do not expireA session without an expiry stays usable forever. Learn why that makes stolen tokens valuable and how to set a sensible timeout.