Remove adsMember
Theme: SystemContrast: Standard
On this page
  1. What it’s for
  2. Where people use it as a lock
  3. Spotting it
  4. What to use instead
  5. If you have ten minutes today

Base64 is encoding, not encryption

By 4 min readBeginner
Copy article
Share this article

Somewhere in a repo near you there’s a config file with password: aHVudGVyMg== in it. Maybe there’s a comment above it too: “encrypted, don’t worry”.

Decode it. You get hunter2.

Nobody cracked anything. The password was written in a different alphabet, one that’s printed in a public standard and that every language ships a decoder for. Base64 is an encoding. It was never meant to keep anything secret, and it doesn’t.

What it’s for

Base64 exists because plenty of text channels can’t carry raw bytes. Email bodies, JSON strings, URLs, HTTP headers: put a null byte or a stray newline in any of them and something downstream breaks, or quietly mangles your data. RFC 4648 [1] gets around that by rewriting any byte sequence with 64 characters that survive everywhere (A-Z, a-z, 0-9, + and /), plus = for padding [2].

Take three bytes, which is 24 bits, cut them into four 6-bit chunks and look each chunk up in the alphabet. Four characters out for every three bytes in. That’s why the output is about a third bigger than the input, and why padded output always has a length divisible by four.

Three bytes become four Base64 characters, with no key involved

There’s no key in that diagram because there isn’t one. Encryption only works when the attacker is missing some secret. Here the lookup table is the whole algorithm, and decoding is the same table read backwards.

Where people use it as a lock

It looks like line noise, and line noise feels safe. I understand why people fall for it. But a value your eyes can’t read isn’t protected from anything; a machine reads it instantly.

Where it turns up:

  • Credentials in config and .env files, Base64’d so they’re “not in plain text”.
  • HTTP Basic auth. The header is Authorization: Basic followed by user:password, encoded. RFC 7617 [3] says plainly that this sends the credentials as cleartext, so without TLS you might as well not have bothered encoding them.
  • Kubernetes Secrets and similar manifests. The values are Base64 so binary data fits in YAML, and that’s the only reason. Whoever can read the manifest can read the secret.
  • “Obfuscated” API payloads, where the client is told to Base64 the body so nobody can sniff it.
  • JWTs. The header and payload are Base64url, so anyone holding the token can read the claims. The signature stops people changing them, not reading them.

If a secret is only Base64’d, it’s plain text with one extra step. Leak the encoded value and you’ve leaked the secret.

Spotting it

Usually you can tell by eye.

Clue What it suggests
Only letters, digits, +, / Standard Base64
- and _ instead of + and / Base64url (JWTs, URLs)
Ends with = or == Padding, very likely Base64
Length is a multiple of 4 Consistent with padded Base64
Starts with eyJ Base64 of {", the start of a JSON object, often a JWT
Starts with LS0tLS1CRUdJTi Base64 of -----BEGIN, a PEM key or certificate

eyJ is the one I use most. {" always encodes to the same first three characters, so a string that starts with eyJ is almost always JSON, and quite often a JWT.

Then decode it to be sure. Python, standard library only:

python
import base64

value = "aHVudGVyMg=="
print(base64.b64decode(value).decode())  # hunter2

For Base64url, use base64.urlsafe_b64decode instead, and add back any = padding that was stripped or it’ll raise an error. Or skip the code and paste it into the Base64 encoder and decoder.

What to use instead

That depends on what you wanted in the first place.

You want Use Base64’s role
Keep data secret Real encryption (for example AES-GCM) with a managed key Encode the ciphertext for text channels
Store a password A slow password hash (argon2, bcrypt, scrypt) Encode the hash string if needed
Prove data was not changed HMAC or a signature Encode the tag for transport
Hold secrets in config A secrets manager or encrypted-at-rest store None

Look at the last column. Most of the right answers still end in Base64, because ciphertext and hashes are bytes too, and those bytes still have to get through JSON. That’s fine. Encrypt, then encode. The mistake is encoding and stopping there.

While we’re at it: a hash isn’t an encoding either, and a signature doesn’t hide anything. Encoding, hashing, signing and encrypting are four different jobs, and nearly every mistake above is one of them being asked to do another’s.

If you have ten minutes today

Grep your repos and config for values that end in = or start with eyJ, and decode a few. If a password or key falls out, rotate it. Everyone who has ever opened that file has already seen it.

After that the fixes are dull but simple. Real secrets go in a secrets manager, not in git. Basic auth runs over TLS or not at all, and if you get to choose, pick a token scheme instead. When you actually need secrecy, use a vetted encryption library and Base64 the ciphertext afterwards [4].

Try it yourself

Base64 encoder and decoder

Encode text to Base64 or decode Base64 back to text, with standard and URL-safe alphabets.

Open the tool

Share this article

Share this article

Sources

  1. RFC 4648: The Base16, Base32, and Base64 Data Encodings1
  2. MDN: Base641
  3. RFC 7617: The 'Basic' HTTP Authentication Scheme1
  4. OWASP Cheat Sheet: Cryptographic Storage1

Keyboard shortcuts

Close

Everywhere

Search tools and actions
Ctrl K
Show this help
?
Switch theme
Ctrl Shift L
Pin the selected tool (in search)
Ctrl P

On a tool

Send the output to the first suggestion
Ctrl ↵
Open a file into the input
Ctrl O
Copy the output
Ctrl Shift C
Close searchesc
toolsBase64 encoder and decoderEncode text to Base64 or decode Base64 back to text, with standard and URL-safe alphabets.EncodingJSON formatter and validatorFormat, minify and validate JSON, with the exact line and column of any error.FormattingRegex testerTest JavaScript regular expressions with live highlighting, capture groups and a plain-language explanation.TextUUID and ULID generatorGenerate UUID v4, UUID v7 and ULID in bulk, or paste an ID to see its version, variant and timestamp.GeneratorsUnix timestamp converterConvert Unix timestamps to dates and back, in any time zone, with ISO 8601, RFC 2822 and relative time.Date and timeHash and HMAC generatorCalculate MD5, SHA-1, SHA-2, SHA-3, BLAKE2, BLAKE3 and CRC-32 hashes of text or a file, with optional HMAC.SecurityPassword generator and strength checkerGenerate random passwords, passphrases and PINs, or check how strong a password is.SecurityJWT decoder and verifierDecode a JSON Web Token, check its expiry and claims, and verify its signature with a secret or public key.SecurityCIDR and IP subnet calculatorCalculate the range, mask and size of IPv4 and IPv6 networks, split them into subnets, and check what contains or overlaps what.NetworkCron expression explainerRead a cron expression in plain language, see its next runs in any time zone, and tidy it into its standard form.Date and timeSort linesSort lines of text A to Z or Z to A, in natural order (file2 before file10), by code point, by number or by length.TextRemove duplicate linesRemove duplicate lines from a list, keep the first or last copy, ignore case or spaces, or drop every line that repeats.TextCompare lists and textCompare two lists line by line: find what is in both, only in one, or in either, or see a marked line diff side by side.TextText case converter and slugifyConvert text to camelCase, snake_case, kebab-case, Title Case and more, or turn a title into a URL slug with accents handled.TextURL encode, decode and parsePercent-encode and decode text for URLs, or take a URL apart into scheme, host, path and query and edit the query as a table.EncodingValidators: IBAN, BSN, VAT, card and ISBNCheck an IBAN, Dutch BSN, credit card number, EU VAT number, email address, URL or ISBN, with the reason when it fails. One value per line.ValidationHTML entity encoder and decoderEscape text for HTML with named, decimal or hex entities, or turn entities such as & and é back into characters.EncodingEscape and unescape stringsEscape text for a JSON or JavaScript string, a SQL string, a CSV field, a regular expression or a shell word, or undo the escaping.EncodingEAN, UPC and GTIN check digitsCheck the check digit of an EAN-8, EAN-13, UPC-A or GTIN-14, add a missing one, read the country from the GS1 prefix and convert between ISBN-10 and EAN-13. One code per line.ValidationTest data generatorGenerate valid-looking test IBANs, BSNs, EAN barcodes and EU VAT numbers in bulk. They pass the checks, but belong to no one.GeneratorsSQL formatter and minifierFormat SQL so each clause sits on its own line, or minify it onto one line, for MySQL, PostgreSQL, SQLite, SQL Server and BigQuery.FormattingNumber base converterConvert numbers between binary, octal, decimal, hex and any base from 2 to 36, with big numbers, two's complement and a bit view.EncodingYAML, TOML and JSON converterConvert between JSON, YAML and TOML, or validate and pretty-print a document, with the line and column of every error.FormattingCSV and JSON converterConvert CSV to a JSON array of objects or arrays, or JSON back to CSV. Detects the delimiter and reads Dutch Excel files with semicolons and decimal commas.DataGlob and .gitignore testerTest glob patterns and .gitignore rules against a list of paths and see which paths match and which rule matched them.TextColour converterConvert a colour between HEX, RGB, HSL, HWB and OKLCH, check its WCAG contrast against a background, or build a Material 3 tonal palette from it.Formattingchmod calculatorConvert Unix file permissions between octal (755), letters (rwxr-xr-x), ls -l and chmod clauses, with a checkbox grid and the setuid, setgid and sticky bits.SecurityUser-agent parserRead a user-agent string: browser, engine, OS, device type and whether it is a bot. Leave it empty to read your own browser.NetworkConvert cURL to codePaste a cURL command and get the same request as fetch, Axios, Python requests, Go or PHP. Nothing is run and nothing is sent.NetworkMarkdown previewWrite Markdown and see it rendered as you type, or copy the HTML. Raw HTML is shown as text and links are checked, so nothing in the preview can run.FormattingBarcode and QR code generatorMake a QR code, EAN-13, UPC-A, Code 128, ITF-14 or Data Matrix barcode, download it as SVG or PNG, or read a code from an image.GeneratorspagesChangelogWhat changed in each releasepagesAboutWhat codeknife.dev is and who makes itpagesIdeasSuggest improvements and vote on other visitors’ ideas.pagesPrivacyWhat leaves your browser and what is storedpagesAccessibilityConformance, known limitations and how to report a problempagesMembershipRemove ads and raise the limitspagesactions