Hash, Encode, Escape: Security Basics for the Web

Hashing, encoding and escaping are the three verbs of web security basics, and mixing them up is how passwords end up in base64 and user comments end up executing JavaScript. They solve three different problems, they are not interchangeable, and one test question separates them instantly: can you get the original back? This guide draws the lines and pairs each verb with the right tool, starting with our free bcrypt generator for the one verb that must never be reversible.

Three verbs, three different problems

VerbReversible?Problem it solvesExample
HashingNo, by designverify without storing the secretpasswords, file integrity
EncodingYes, by designrepresent data for transportbase64 in email attachments
EscapingYes, triviallystop data being read as code&lt; instead of < in HTML

The confusion has consequences in one direction more than the others: treating encoding as protection. Base64 looks scrambled to a human, which is precisely why “encrypted” passwords stored in base64 keep appearing in breach writeups. Encoding hides nothing from anyone with a decoder, which is everyone. If the data must stay secret, hash it (when you only need to verify it) or encrypt it (when you need it back); encoding belongs to neither job.

Hashing: the one-way fingerprint

A hash function turns any input into a fixed-size fingerprint, the same input always producing the same output and no computation producing the input from the output. For passwords specifically, ordinary hash functions are dangerously fast, because fast for you is fast for an attacker guessing billions of candidates. Bcrypt’s answer is deliberate slowness, tuned by a cost factor that doubles the work with every increment: cost 10 means 1,024 internal rounds, cost 12 means 4,096, so going from 10 to 12 makes every guess four times more expensive.

A bcrypt hash is exactly 60 characters with its anatomy on display: $2b$12$ declares the algorithm and cost, the next 22 characters are a random salt, the final 31 are the hash itself. The built-in salt is why hashing the same password twice in the bcrypt generator gives two different results, both valid: the salt is stored inside the hash, so verification re-runs the computation with it. One honest limitation worth knowing: bcrypt only reads the first 72 bytes of input, which no human password approaches but automated systems feeding it long tokens should.

Encoding: a costume, not a lock

Encoding rewrites data in an alphabet some channel can carry, with perfect reversibility as the entire point. Base64 is the workhorse: it maps every 3 bytes to 4 characters from a safe 64-character alphabet, which is why encoded data grows by a third, 300 bytes becoming exactly 400 characters. The growth is the tell that nothing cryptographic happened: information was rearranged, not protected. The full family, base64 against base32 against base58 and where each one earns its keep, is covered in the encoding guide; the practical rule for this article is shorter: encode for transport, never for secrecy.

Escaping: making text safe for its context

Escaping solves the oldest injection problem on the web: text that the displaying context mistakes for instructions. In HTML, five characters carry that risk, &, <, >, " and ', because they open tags, attributes and entities. A user comment containing <script> pasted raw into a page stops being a comment and starts being code running in every visitor’s browser; the same comment with its angle brackets escaped to &lt; and &gt; displays harmlessly as the text the user typed. Our HTML entity escaper performs exactly this transformation.

Two rules make escaping work in practice. Escape at output, not at storage: store the raw text, escape at the moment of display, because escaped text in the database gets escaped again on the way out and renders as visible &amp; debris. Escape for the context you are entering: HTML escaping for HTML, URL percent-encoding for links (its own grammar, covered in the URL encoding guide), backslash escaping for strings inside JSON via the JSON escaper. The layers are different languages, and applying the wrong one is its own category of bug.

The other direction: unescaping what arrives escaped

Plenty of legitimate text arrives pre-escaped: RSS feeds, CMS exports, scraped pages, API responses where titles read Tom &amp; Jerry. The HTML entity decoder reverses the transformation and returns readable text. It doubles as a diagnostic: paste suspect text and decode it twice, and if the first pass produces more entities and the second produces clean text, the source double-escaped it, the storage-versus-output rule above broken somewhere upstream. The rest of the client-side toolbox for this kind of work lives in the developer tools pillar.

Frequently asked questions

Can a bcrypt hash be decrypted if someone steals the database?

No: there is nothing to decrypt, since the hash does not contain the password. An attacker can only guess candidates and hash each one, which is exactly the attack the cost factor exists to slow down. Weak passwords still fall to guessing; the hash buys time and makes strong passwords effectively unrecoverable.

What cost factor should I pick?

The common range is 10 to 12. The principle: as high as your login latency budget tolerates, since every increment doubles both the attacker’s cost and yours. Hardware improves, so systems that store the cost inside the hash, as bcrypt does, can raise it over time and rehash users at their next login.

Is base64 at least better than storing plaintext?

Functionally no: it decodes in milliseconds with universal tools, so treat base64 data as plaintext in any security analysis. It can be worse than plaintext in one way, the false sense of protection it gives whoever stored it.

Should I escape user input before saving it to the database?

Store it raw and escape at display time, for the context it is displayed in. Escaping before storage bakes one context’s escaping into data that may be shown in many, and produces the classic double-escaped &amp; artifacts. The separate concern of SQL injection is solved by parameterized queries, not by escaping.

ATV

Written by Nick (ATV Team)

We build and maintain the 600+ free, client-side tools on this site, and every guide is written against the tools themselves: each figure is computed and checked before it is published, and every linked tool is tested in the browser. More about how we work on the about page, and the full library of guides lives on the blog.