Cadena Base64 no válida: por qué falla la decodificación y cómo solucionarlo
Un decodificador Base64 rechaza una cadena por uno de tres motivos: un carácter fuera del alfabeto, una longitud que no puede ser correcta o un relleno en el lugar equivocado.
Base64 usa 64 caracteres, de A a Z, de a a z, de 0 a 9, + y /, más = como relleno al final (RFC 4648, sección 4). Cualquier otra cosa hace fallar a un decodificador estricto. Lo confuso es que los decodificadores no se ponen de acuerdo sobre qué incluye «cualquier otra cosa»: una cadena Base64URL como PDw_Pz8-Pg== se decodifica bien con Buffer de Node.js, falla en .NET y en Python se convierte en silencio en bytes equivocados. Saber qué tolera su decodificador es la mayor parte de la solución.
El error en cada lenguaje
| Dónde | Qué obtiene |
|---|---|
Chrome y Edge, atob() | InvalidCharacterError: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded. |
Node.js, atob() | InvalidCharacterError: Invalid character, o The string to be decoded is not correctly encoded. si la longitud es incorrecta |
Python, base64.b64decode() | binascii.Error: Incorrect padding, Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4 o, con validate=True, Only base64 data is allowed |
.NET, Convert.FromBase64String() | FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. |
Java, Base64.getDecoder().decode() | IllegalArgumentException: Illegal base64 character seguido del código del carácter en hexadecimal, como 2d para - |
PHP, base64_decode($s, true) | Ningún mensaje: la función devuelve false |
| AWS CLI versión 2 | Invalid base64: seguido del valor que pasó |
La excepción es Buffer.from(s, 'base64') de Node.js: nunca lanza errores. Se salta los caracteres que no reconoce, de modo que una cadena dañada produce bytes dañados sin ningún aviso.
Qué tolera cada decodificador
| Entrada | atob | Python b64decode | .NET | PHP estricto |
|---|---|---|---|---|
Falta el relleno: SGVsbG8 | Decodifica | Incorrect padding | Falla | Decodifica |
Relleno de más: SGVsbG8== | Falla | Decodifica | Falla | Devuelve false |
| Espacios o saltos de línea | Se ignoran | Se ignoran (falla con validate=True) | Se ignoran | Se ignoran |
- y _ de Base64URL | Falla | Se descartan en silencio: bytes erróneos o un error | Falla | Devuelve false |
Otros caracteres: comillas, \, !, : | Falla | Se descartan en silencio | Falla | Devuelve false |
El modo por defecto de Python merece una advertencia. Descarta todos los caracteres ajenos al alfabeto antes de decodificar, así que las letras de un prefijo como data:image/jpeg;base64, se decodifican como si fueran datos. Según la longitud del prefijo, obtendrá un error confuso sobre el número de caracteres, o ningún error y varios bytes de basura delante del contenido real. Pase validate=True cuando quiera que una entrada incorrecta falle de forma clara.
Caracteres que no deberían estar ahí
Cuando un decodificador estricto informa de un carácter no válido, casi siempre la cadena recogió algo por el camino hasta usted:
- Comillas o texto alrededor. Copiar
"SGVsbG8="con sus comillas, o una línea entera"key": "value", añade caracteres que no son Base64. - Un prefijo de data URI. En
data:image/png;base64,iVBOR...solo es Base64 lo que va después de la coma. Elimine todo hasta la coma, incluida. - Escapes de JSON.
json_encodede PHP escribe/como\/, y un contenido partido en líneas, como un certificado PEM, se guarda con\nentre línea y línea. Si copia el texto JSON en bruto en lugar del valor ya analizado, las barras invertidas vienen con él. Analice primero el JSON y decodifique la cadena resultante. - Caracteres de Base64URL.
-y_indican que la cadena usa el alfabeto seguro para URL, como hacen los JWT. Consulte la sección correspondiente más abajo. - Caracteres invisibles. Un espacio de no separación o un espacio de ancho cero pegados desde una aplicación de chat o un documento no son espacios en blanco ASCII, así que incluso
atob, que ignora los espacios normales, los rechaza.
Hay un caso que no produce ningún error. Si un valor Base64 estándar se coloca en una query string sin codificación porcentual, cada + se convierte en un espacio cuando se analiza la query. Después, atob y Python se saltan esos espacios: ++++YWJj llega como YWJj y se decodifica como abc, y los tres primeros bytes desaparecen sin dejar rastro. Codifique el valor con encodeURIComponent, o use Base64URL en las URL.
Longitud y relleno incorrectos
Base64 convierte cada 3 bytes en 4 caracteres, así que una cadena con relleno siempre tiene una longitud divisible entre 4. Sin relleno, el último grupo tiene 2 o 3 caracteres. Un resto de 1 es imposible para cualquier entrada: si length % 4 === 1, se perdió un carácter o se añadió uno de más, y ninguna cantidad de relleno lo arreglará.
El relleno solo puede aparecer al final, y nunca hay más de dos signos =. El RFC 4648 exige relleno salvo que el formato que usa Base64 diga otra cosa, y varios formatos lo hacen: los JWT y los code challenges de PKCE lo omiten. Para restaurar el relleno que falta antes de pasar la cadena a un decodificador estricto:
// JavaScript
const padded = s + '='.repeat((4 - s.length % 4) % 4);
# Python
padded = s + '=' * (-len(s) % 4)
Base64URL en un decodificador estándar
Base64URL (RFC 4648, sección 5) sustituye + por - y / por _, y normalmente elimina el relleno. Un decodificador estándar rechaza los dos caracteres, pero solo fallan las cadenas que por casualidad contienen alguno de ellos, así que el error parece aleatorio. Un valor aleatorio de 16 bytes se convierte en 22 caracteres Base64URL, 21 de los cuales pueden ser - o _. Cada uno evita ambos con una probabilidad de 62/64, de modo que (62/64) elevado a 21, alrededor del 51%, de esos valores se decodifican bien y el 49% restante falla. Convierta el alfabeto y restaure el relleno, o use un decodificador pensado para esta variante:
// Browser
const b64 = s.replace(/-/g, '+').replace(/_/g, '/');
const bytes = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
// Node.js
Buffer.from(s, 'base64url');
# Python
base64.urlsafe_b64decode(s + '=' * (-len(s) % 4))
// Java
Base64.getUrlDecoder().decode(s);
Aun así, urlsafe_b64decode de Python necesita el relleno, y por eso el ejemplo lo añade. Las diferencias entre los dos alfabetos se explican en Base64 vs Base64URL.
Saltos de línea de correos y archivos PEM
Los cuerpos de correo MIME parten el Base64 en líneas de 76 caracteres, y los archivos PEM, como los certificados, en líneas de 64. atob, .NET y PHP se saltan los saltos de línea. El decodificador básico de Java no lo hace: use Base64.getMimeDecoder() para la entrada partida en líneas. En los PEM, elimine también las líneas -----BEGIN ...----- y -----END ...-----, que no son Base64.
Se decodificó, pero el resultado es incorrecto
- La salida es más Base64. Los datos se codificaron dos veces.
YUdWc2JHOD0=se decodifica comoaGVsbG8=, que a su vez se decodifica comohello. Decodifique otra vez. - Las letras acentuadas aparecen como
é.atobdevuelve un carácter por byte, así que el texto UTF-8 necesita un segundo paso:new TextDecoder().decode(Uint8Array.from(atob(s), c => c.charCodeAt(0))). - Python lanza
UnicodeDecodeErrordespués de decodificar. Los bytes son Base64 válido pero no texto UTF-8, por ejemplo'utf-8' codec can't decode byte 0xe9con un texto guardado en Latin-1. Decodifique con la codificación correcta, como.decode('cp1252'), o trate el resultado como datos binarios. - La salida son símbolos ilegibles. Los datos son binarios: una imagen, un PDF o datos comprimidos. Guarde los bytes en un archivo en lugar de mostrarlos como texto.
AWS CLI: «Invalid base64»
La versión 2 de AWS CLI espera por defecto que los parámetros binarios vayan en Base64. Pasar JSON plano a un comando como aws lambda invoke --payload '{"id": 1}' falla con Invalid base64. Añada --cli-binary-format raw-in-base64-out para pasar el texto tal cual, o codifique usted mismo el payload.
Comprobar una cadena rápidamente
Pegue el valor en Decodificar Base64. La herramienta elimina los espacios en blanco y el prefijo de data URI, convierte los caracteres de Base64URL y añade el relleno que falte antes de decodificar; si aun así indica que la entrada no es Base64 válido, la cadena contiene de verdad un carácter ajeno o tiene una longitud imposible. Cuando la decodificación funciona, muestra directamente el texto UTF-8 y ofrece todo lo demás, como una imagen, en forma de archivo para descargar.