Frontend Labs
⋯

Part 2 — Encoding: the thing that is not security

This Part is short and it prevents a whole category of career-damaging mistakes.

Chapter 5 — How text becomes bytes

In one sentence

Computers store numbers, so every character must be mapped to a number, and the mapping is called a character encoding.

The problem

A file, a network packet, and memory all hold bytes — numbers from 0 to 255. The letter A is not a number. Something must decide that A = 65.

ASCII: the original 128

   char  decimal  hex   binary
   ----  -------  ---   --------
   'A'      65    0x41  01000001
   'B'      66    0x42  01000010
   'a'      97    0x61  01100001
   '0'      48    0x30  00110000
   ' '      32    0x20  00100000
   '\n'     10    0x0A  00001010

Notice '0' is 48, not 0. The character zero and the number zero are different things — a distinction behind many parsing bugs.

ASCII uses 7 bits, so 128 characters. Enough for American English. Not enough for the world.

UTF-8: how the world actually encodes text

UTF-8 represents over a million characters using 1 to 4 bytes each, and — the crucial design win — ASCII text is byte-identical in UTF-8.

   'A'    -> 41                     1 byte   (identical to ASCII)
   'e'    -> 65                     1 byte
   'é'    -> C3 A9                  2 bytes
   '€'    -> E2 82 AC               3 bytes
   emoji  -> F0 9F 98 80            4 bytes

Two consequences that cause security bugs:

  1. Character count ≠ byte count. A "10-character" username can be 40 bytes. Length limits that count one and buffers that hold the other is a classic overflow.
  2. Invalid byte sequences exist. Not every byte string is valid UTF-8. Different parsers handle invalid sequences differently — one may reject, one may substitute, one may silently drop bytes. Two parsers disagreeing about the same input is the root of an entire attack class (Chapter 7).

Practice (10 min)

# see the bytes behind text
printf 'Hello' | xxd
printf 'Héllo' | xxd          # note the two bytes for é
printf '€' | xxd

# character count vs byte count
python3 -c "s='Héllo€'; print('chars:', len(s), ' bytes:', len(s.encode()))"

# what happens with invalid UTF-8?
printf '\xc3\x28' | python3 -c "import sys; print(sys.stdin.buffer.read().decode('utf-8', errors='replace'))"

Check yourself

  1. Why is the character '0' stored as 48?
  2. Give one security-relevant consequence of characters taking a variable number of bytes.
  3. Why does it matter that not every byte sequence is valid UTF-8?

Further reading

  • Article (a classic): "The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets" — Joel Spolsky. Twenty years old, still correct, still the best introduction.
  • Reference: utf8everywhere.org — a strongly-argued case for UTF-8, with good technical detail.

Generated from real-life-guide.md · 8 Oct 2026 16:36