Sin Firma No Hay Problema
JWT alg:none
// OBJETIVO
Falsificar un token que el server acepte como admin, sin conocer el secreto de firma.
// ZONA DE ATAQUE
Corre en un iframe de origen opaco: no puede leer este sitio ni tu progreso. Cuando logres el exploit, la flag aparece dentro de la app — copiala y verificala.
// CONSULTÁ
Pistas
Código vulnerable
La verificación decide el algoritmo a partir del token recibido:
function verify(token) {
const [h, p, sig] = token.split('.');
const header = JSON.parse(b64urlDecode(h));
if (header.alg === 'none') return JSON.parse(b64urlDecode(p)); // ← sin firma
if (header.alg === 'HS256') {
if (sig === hmac(`${h}.${p}`, SECRET)) return JSON.parse(b64urlDecode(p));
}
return null;
}
El header lo controla el atacante, así que puede declarar alg: "none" y el
server acepta el payload sin verificar ninguna firma. Con {"user":"admin"}
dentro, se autentica como administrador sin conocer SECRET.
Remediación
Nunca dejes que el token elija su propio algoritmo: fijalo del lado del server y
rechazá none.
- function verify(token) {
- const [h, p, sig] = token.split('.');
- const header = JSON.parse(b64urlDecode(h));
- if (header.alg === 'none') return JSON.parse(b64urlDecode(p));
- if (header.alg === 'HS256') {
- if (sig === hmac(`${h}.${p}`, SECRET)) return JSON.parse(b64urlDecode(p));
- }
- return null;
- }
+ // Algoritmo fijo y obligatorio; la firma se valida siempre.
+ function verify(token) {
+ return jwt.verify(token, SECRET, { algorithms: ['HS256'] });
+ }
Por qué funciona: al pasar algorithms: ['HS256'], la librería descarta
cualquier token cuyo header declare otra cosa —incluido none— antes de confiar
en él, y exige una firma válida con el secreto. El algoritmo deja de ser una
decisión del atacante y vuelve a ser del servidor.