Frontend Labs
⋯

Appendix A — Glossary (plain language)

Short, practical definitions. Where a term has a precise technical meaning that differs from loose usage, the loose usage is flagged.

AEAD          Authenticated Encryption with Associated Data. Encryption that
             also detects tampering, in one operation. AES-GCM, ChaCha20-
             Poly1305. Use this for new encryption. (Ch 15)
AES          Advanced Encryption Standard. The default symmetric block
             cipher. 128-bit blocks; 128/192/256-bit keys. (Ch 13)
ACME         Automated Certificate Management Environment. The protocol
             behind Let's Encrypt; automates issuing/renewing TLS certs. (Ch 30)
ARP          Address Resolution Protocol. Maps IP to MAC on a LAN. No
             authentication -> ARP spoofing. (Ch 37)
Argon2id     The recommended password-hashing function. Deliberately slow
             and memory-hard. (Ch 10)
Attack surface  Every point where an attacker can try to get in or get data
             out. Shrink it. (Ch 2)
Authentication (authn)  Proving WHO you are. (Ch 3)
Authorization (authz)   Deciding WHAT you may do. Checked on every request. (Ch 3, 45)
Base64       An encoding that re-expresses bytes using 64 safe characters.
             Reversible by anyone. NOT encryption. (Ch 6)
Bastion / jump host  A hardened gateway you connect through to reach
             internal systems. Use ProxyJump, not agent forwarding. (Ch 35)
BOLA / IDOR  Broken Object-Level Authorization / Insecure Direct Object
             Reference. Accessing another user's object by changing an id. (Ch 45)
CA           Certificate Authority. Signs certificates vouching that a
             public key belongs to a name. (Ch 29, 30)
CIA triad    Confidentiality, Integrity, Availability. The three things
             security protects. (Ch 1)
CSP          Content Security Policy. A response header that restricts what
             a page can load/run; mitigates XSS. (Ch 43)
CSPRNG       Cryptographically Secure Pseudo-Random Number Generator. Use
             this for keys, tokens, salts, nonces. (Ch 12, 46)
CSRF         Cross-Site Request Forgery. A malicious site causes the user's
             browser to make a state-changing request with their cookies. (Ch 44)
CT           Certificate Transparency. Public append-only logs of every
             issued certificate; makes mis-issuance detectable. (Ch 30)
DANE         DNS-based Authentication of Named Entities. TLSA records pin a
             server's cert via DNSSEC. Common for SMTP. (Ch 38)
Defense in depth  Multiple independent controls, so one failure isn't fatal. (Ch 1)
DH / DHE / ECDHE  Diffie-Hellman key exchange / ephemeral / elliptic-curve
             ephemeral. Agree a shared secret over a hostile network.
             "E" = forward secrecy. (Ch 18, 20)
DNSSEC       Signs DNS records so resolvers can verify authenticity.
             Does NOT encrypt. (Ch 38)
DoH / DoT    DNS over HTTPS / over TLS. Encrypts DNS queries in transit.
             Does NOT authenticate the data (that's DNSSEC). (Ch 38)
ECC          Elliptic Curve Cryptography. Same security as RSA with much
             smaller keys. (Ch 20)
ECDSA / EdDSA  Elliptic-curve signature schemes. ECDSA needs a safe random
             k per signature (footgun); Ed25519 is deterministic. (Ch 20, 21)
Encoding     Changing data's representation for safe transport. Fully
             reversible, no key, NO security. (Ch 6)
Fail closed / open  On error, deny (closed) or allow (open). Choose per
             context; security controls usually fail closed. (Ch 3)
Forward secrecy (PFS)  Compromising a long-term key later does not decrypt
             past sessions, because session keys were ephemeral. (Ch 18)
Grover's algorithm  Quantum search; halves the effective strength of
             symmetric crypto/hashes. Fix: double the size (AES-256). (Ch 23)
Hash         One-way fixed-size fingerprint of data. SHA-256. Not
             reversible, not encryption. (Ch 8)
HMAC         Keyed hash proving integrity + authenticity between two
             parties who share a key. (Ch 11)
HNDL         "Harvest now, decrypt later." Recording encrypted traffic
             today to decrypt with a future quantum computer. (Ch 23)
HSTS         HTTP Strict Transport Security. Header forcing HTTPS and
             blocking cert-error click-through. (Ch 31)
Hybrid (crypto)  Combining classical + post-quantum (e.g. X25519MLKEM768)
             so it's secure if either holds. (Ch 25)
IDOR         See BOLA. (Ch 45)
IV / nonce   Initialisation Vector / number-used-once. Randomiser for a
             cipher mode. Must never repeat with the same key. (Ch 14, 15)
JWT          JSON Web Token. Signed (not encrypted) claims. Payload is
             readable by anyone. Many footguns. (Ch 6, 46)
KDF          Key Derivation Function. Turns a shared secret or password
             into uniformly-random key material. HKDF, PBKDF2, Argon2. (Ch 18, 22)
KEM          Key Encapsulation Mechanism. Like DH but asymmetric-shaped:
             encapsulate -> (secret, ciphertext); decapsulate -> secret.
             ML-KEM. (Ch 24)
Kerckhoffs's principle  The system stays secure even if everything but the
             key is public. Don't roll your own / rely on secret designs. (Ch 12)
Kill chain   The stages of an intrusion: recon -> initial access -> ... ->
             actions on objectives. (Ch 2)
Least privilege  Minimum access, for minimum time. (Ch 45, 52)
MAC          (1) Message Authentication Code (HMAC etc.) -- integrity tag.
             (2) Media Access Control address -- L2 hardware address.
             (3) Mandatory Access Control (SELinux/AppArmor). Context tells
             you which. (Ch 11, 37, 54)
MITM         Man-in-the-middle. Active on-path attacker who can read and
             modify traffic. (Ch 37)
ML-KEM / ML-DSA / SLH-DSA  NIST post-quantum standards (FIPS 203/204/205):
             key exchange / signatures / hash-based signatures. (Ch 24)
mTLS         Mutual TLS. Both client and server present certificates. (Ch 31)
Nonce        See IV. In ECDSA, the per-signature random k. (Ch 15, 20)
OAEP / PSS   Padding schemes for RSA encryption / RSA signatures. Required;
             "textbook RSA" without them is broken. (Ch 19)
OAuth 2.0    Delegated authorization ("let this app use my Calendar"). NOT
             authentication. (Ch 46)
OCSP         Online Certificate Status Protocol. Real-time revocation check.
             Weak in practice (soft-fail). (Ch 30)
OIDC         OpenID Connect. Identity layer on top of OAuth 2.0 -> "log in
             with Google". Adds the ID token. (Ch 46)
One-time pad  Provably unbreakable cipher; impractical (key as long as the
             message, never reused). (Ch 12)
Origin       scheme + host + port. The unit of browser isolation (SOP). (Ch 41)
PKCE         Proof Key for Code Exchange. Protects the OAuth authorization-
             code flow from interception. Use for ALL clients. (Ch 46)
PKI          Public Key Infrastructure. CAs, certificates, chains, trust
             stores, revocation, transparency. (Ch 30)
Post-quantum (PQC)  Cryptography that resists quantum attack; runs on
             ordinary computers. (Ch 23-26)
Preimage / collision resistance  Can't find an input for a given hash /
             can't find two inputs with the same hash. (Ch 8)
Rainbow table  Precomputed hash lookup table. Defeated by a unique salt. (Ch 10)
Replay attack  Re-sending a valid captured message. Defeat with nonces/
             timestamps inside the signed/authenticated data. (Ch 21, 28)
RSA          Public-key algorithm based on integer factoring. Still used;
             new systems prefer ECC. Broken by quantum (Shor). (Ch 19)
Salt         Random per-record value added before hashing a password. Not
             secret; prevents precomputation. (Ch 10)
SBOM         Software Bill of Materials. Inventory of every component in a
             build. (Ch 47)
Same-origin policy (SOP)  Script on origin A can't read data from origin B. (Ch 41)
SameSite     Cookie attribute controlling whether a cookie is sent on
             cross-site requests. Lax / Strict / None. (Ch 41, 44)
Shor's algorithm  Quantum algorithm that factors integers and computes
             discrete logs efficiently -> breaks RSA, DH, ECC. (Ch 23)
Side channel  Leaking secrets via timing, power, cache, EM, etc. rather
             than the algorithm. (Ch 11)
Signature (digital)  Private key signs, public key verifies. Adds non-
             repudiation over a MAC. (Ch 21)
SLSA         Supply-chain Levels for Software Artifacts. A framework for
             build/artifact integrity. (Ch 47)
SNI          Server Name Indication. The hostname in the TLS ClientHello.
             In the clear unless ECH is used. (Ch 27, 28)
SSRF         Server-Side Request Forgery. Making a server fetch attacker-
             chosen URLs -> cloud metadata theft, internal pivot. (Ch 44)
STRIDE       Threat-modeling prompt: Spoofing, Tampering, Repudiation,
             Information disclosure, Denial of service, Elevation of
             privilege. (Ch 52)
Symmetric / asymmetric  One shared key / a public+private key pair. (Ch 12, 17)
TOFU         Trust On First Use. Pin an identity on first contact (SSH
             known_hosts). Weak at that first moment. (Ch 34)
Trust boundary  Where data crosses between different levels of trust or
             different owners. Threats cluster here. (Ch 2, 52)
XSS          Cross-Site Scripting. Attacker's JavaScript runs in your
             site's origin in a victim's browser. (Ch 43)
Zero trust   No implicit trust from network location; authenticate and
             authorize every request per-resource. (Ch 39)

Appendix B — The OpenSSL cookbook

Assumes OpenSSL 3.x (openssl version should say OpenSSL 3.x, not LibreSSL). macOS: brew install openssl@3 and put it first on PATH (Ch 0.4).

Random data and passwords

openssl rand -hex 32                  # 256-bit value, hex
openssl rand -base64 32               # 256-bit value, base64
openssl rand -out key.bin 32          # raw bytes to file
openssl passwd -6 'plaintext'         # crypt() SHA-512 hash (for /etc/shadow)

Hashing and HMAC

printf 'hello' | openssl dgst -sha256
openssl dgst -sha256 -r file          # coreutils-style output
openssl dgst -sha512 file
printf 'msg' | openssl dgst -sha256 -hmac 'secretkey'
openssl dgst -sha256 -mac HMAC -macopt hexkey:$(openssl rand -hex 32) file
openssl speed -evp sha256 sha512      # benchmark

Base64 / hex

printf 'data' | openssl base64                 # encode
echo 'ZGF0YQ==' | openssl base64 -d            # decode
printf 'data' | openssl base64 -A              # no line wrapping
xxd -p file            # hex encode      | xxd -r -p   # hex decode

Symmetric encryption (file, password-based)

# ENCRYPT -- ALWAYS include -pbkdf2 and a high -iter
openssl enc -aes-256-cbc -pbkdf2 -iter 600000 -salt -in f -out f.enc
# DECRYPT
openssl enc -d -aes-256-cbc -pbkdf2 -iter 600000 -in f.enc
# armoured (base64) output
openssl enc -aes-256-cbc -pbkdf2 -iter 600000 -salt -a -in f -out f.b64
# raw key + IV instead of a password
openssl enc -aes-256-cbc -K "$(openssl rand -hex 32)" -iv "$(openssl rand -hex 16)" -in f -out f.enc

openssl enc cannot do authenticated (GCM) encryption properly — no tag handling. For real work use age, libsodium, or a language AEAD API.

Key generation (OpenSSL 3.x: one command for all types)

openssl genpkey -algorithm RSA   -pkeyopt rsa_keygen_bits:2048 -out rsa.pem
openssl genpkey -algorithm RSA   -pkeyopt rsa_keygen_bits:4096 -out rsa4096.pem
openssl genpkey -algorithm EC    -pkeyopt ec_paramgen_curve:P-256 -out ec.pem
openssl genpkey -algorithm EC    -pkeyopt ec_paramgen_curve:P-384 -out ec384.pem
openssl genpkey -algorithm ed25519 -out ed.pem
openssl genpkey -algorithm x25519  -out x.pem
# post-quantum (OpenSSL 3.5+)
openssl genpkey -algorithm ML-KEM-768 -out mlkem.pem
openssl genpkey -algorithm ML-DSA-65  -out mldsa.pem
openssl genpkey -algorithm SLH-DSA-SHA2-128s -out slh.pem

openssl pkey -in rsa.pem -pubout -out rsa.pub       # derive public key
openssl pkey -in rsa.pem -aes-256-cbc -out rsa.enc.pem   # encrypt key at rest
openssl pkey -in rsa.pem -text -noout               # inspect

Sign and verify

# RSA-PSS
openssl dgst -sha256 -sign rsa.pem -sigopt rsa_padding_mode:pss -out f.sig f
openssl dgst -sha256 -verify rsa.pub -sigopt rsa_padding_mode:pss -signature f.sig f
# ECDSA
openssl dgst -sha256 -sign ec.pem -out f.sig f
openssl dgst -sha256 -verify ec.pub -signature f.sig f
# Ed25519 / ML-DSA / SLH-DSA -- sign the raw message, no -dgst
openssl pkeyutl -sign   -inkey ed.pem      -rawin -in f -out f.sig
openssl pkeyutl -verify -pubin -inkey ed.pub -rawin -in f -sigfile f.sig

RSA encrypt a small blob (key transport)

openssl pkeyutl -encrypt -pubin -inkey rsa.pub -in aeskey.bin -out aeskey.enc \
  -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256
openssl pkeyutl -decrypt -inkey rsa.pem -in aeskey.enc -out aeskey.bin \
  -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256

Key exchange (ECDH / X25519 / ML-KEM)

# X25519
openssl pkeyutl -derive -inkey mine.pem -peerkey theirs.pub -out shared.bin
# ML-KEM (OpenSSL 3.5+)
openssl pkeyutl -encap -inkey mlkem.pub -pubin -out ct.bin -secret s.bin   # sender
openssl pkeyutl -decap -inkey mlkem.pem -in ct.bin -secret s.bin           # recipient
# HKDF the raw secret into an AES key -- NEVER use it directly
openssl kdf -keylen 32 -kdfopt digest:SHA256 \
  -kdfopt hexkey:$(xxd -p -c256 shared.bin) \
  -kdfopt hexinfo:$(printf 'app v1' | xxd -p) HKDF

CSRs and certificates

# self-signed (labs) with SANs
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
  -keyout k.pem -out c.pem -days 90 -subj "/CN=shop.securesh.op" \
  -addext "subjectAltName=DNS:shop.securesh.op,DNS:www.securesh.op"

# CSR against an existing key
openssl req -new -key k.pem -subj "/CN=shop.securesh.op" \
  -addext "subjectAltName=DNS:shop.securesh.op" -out req.csr

# sign a CSR with your CA
openssl x509 -req -in req.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 90 -sha256 -copy_extensions copy -out leaf.crt

# inspect
openssl x509 -in c.pem -noout -text
openssl x509 -in c.pem -noout -subject -issuer -dates -serial -ext subjectAltName
openssl req  -in req.csr -noout -text -verify

# verify a chain
openssl verify -CAfile root.pem -untrusted intermediates.pem leaf.pem
openssl verify -show_chain -CAfile /etc/ssl/cert.pem leaf.pem

# does this key match this cert?
diff <(openssl x509 -in c.pem -noout -pubkey) <(openssl pkey -in k.pem -pubout) && echo MATCH

Format conversions

openssl x509 -in c.pem -outform der -out c.der
openssl x509 -in c.der -inform der -out c.pem
openssl pkcs12 -export -in c.pem -inkey k.pem -out bundle.p12          # PEM->PKCS12
openssl pkcs12 -in bundle.p12 -nodes -out all.pem                     # PKCS12->PEM
openssl crl -in list.crl -noout -text

Inspecting live TLS

openssl s_client -connect host:443 -servername host -brief </dev/null
openssl s_client -connect host:443 -servername host -showcerts </dev/null
openssl s_client -connect host:443 -servername host -msg -state </dev/null
openssl s_client -connect host:443 -tls1_2 </dev/null            # force version
openssl s_client -connect host:443 -groups X25519MLKEM768 </dev/null
openssl s_client -connect host:443 -status </dev/null | grep -A5 OCSP
openssl s_client -connect smtp:587 -starttls smtp </dev/null
openssl s_client -connect host:443 -cert client.crt -key client.key </dev/null   # mTLS
openssl s_time -connect host:443 -new -time 5                    # handshake rate

Local test server

openssl s_server -accept 8443 -cert c.pem -key k.pem -tls1_3 -www
openssl s_server -accept 8443 -cert c.pem -key k.pem -CAfile ca.crt -Verify 1 -www  # require client cert

Decoding errors

openssl errstr 0A000086
openssl version -a
openssl list -providers
openssl list -cipher-algorithms
openssl list -kem-algorithms          # 3.5+: look for ML-KEM
openssl list -signature-algorithms

Appendix C — Command cheat sheet

Hashing / checksums

sha256sum file             shasum -a 256 file          # verify downloads
b3sum file                                             # BLAKE3 (fast)
printf 's' | sha256sum

Passwords / hashes (cracking — your own hashes / lab only)

hashcat -m 0     hash.txt wordlist        # MD5
hashcat -m 1400  hash.txt wordlist        # SHA-256
hashcat -m 3200  hash.txt wordlist        # bcrypt
hashcat -m 16500 jwt.txt  wordlist        # JWT HS256
john --wordlist=rockyou.txt hash.txt
john --show hash.txt

TLS testing

testssl.sh https://host                    # or: docker run --rm drwetter/testssl.sh
sslyze host:443
nmap --script ssl-enum-ciphers,ssl-cert -p 443 host
curl -v --tlsv1.3 https://host
curl --cacert ca.pem --cert c.pem --key k.pem https://api

SSH

ssh-keygen -t ed25519 -C "you@host"                    # generate
ssh-keygen -lf key.pub                                 # fingerprint
ssh-keygen -lvf key.pub                                # + randomart
ssh -Q kex; ssh -Q cipher; ssh -Q key                  # what's supported
ssh -G host                                            # effective client config
ssh -vvv host                                          # debug
ssh -J bastion target                                  # jump (NOT -A)
ssh -L 5433:db.internal:5432 bastion                   # local forward
ssh -R 8080:localhost:3000 host                        # remote forward
ssh -D 1080 bastion                                    # SOCKS proxy
ssh-keygen -Y sign -f key -n file f                    # sign a file
ssh-keygen -Y verify -f allowed_signers -I id -n file -s f.sig < f
ssh-keygen -s ca -I name -n principals -V +8h key.pub  # issue an SSH cert
ssh-keygen -Lf cert.pub                                # inspect a cert
sudo sshd -t         # validate config      sudo sshd -T   # dump effective config
ssh-audit host                                         # grade config

Network capture / scan

sudo tcpdump -i any -n 'tcp port 443'
sudo tcpdump -i any -nA 'tcp port 80'                  # plaintext payloads
sudo tcpdump -i any -w cap.pcap 'host 10.0.0.5'
tshark -r cap.pcap -Y 'http.request' -T fields -e ip.src -e http.host -e http.request.uri
tshark -r cap.pcap -o tls.keylog_file:keys.log -Y http2      # decrypt own TLS
SSLKEYLOGFILE=keys.log curl -s https://host -o /dev/null
nmap -sn 10.0.0.0/24                                   # host discovery
nmap -sS -sV -sC -p- 10.0.0.5                          # full TCP + versions + scripts
nmap -sU --top-ports 50 10.0.0.5

DNS

dig +trace name                    dig +dnssec name    dig DS name
dig +short CAA domain               dig TXT _dmarc.domain
delv name                          # DNSSEC-validating lookup
kdig -d @1.1.1.1 +tls name          # DoT
curl -s -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=host&type=A'

Web app testing

# proxy: Burp Suite / OWASP ZAP  (the core tool)
ffuf -w words.txt -u https://host/FUZZ                 # content discovery
feroxbuster -u https://host                            # recursive
nuclei -u https://host                                 # templated checks
sqlmap -u 'https://host/x?id=1' --batch                # SQLi (LAB ONLY)
katana -u https://host                                 # crawl
httpx -l hosts.txt -sc -title -tech-detect
curl -sI https://host | grep -iE 'strict-transport|content-security|x-frame|x-content-type'

Supply chain

syft dir:. -o cyclonedx-json > sbom.json
grype sbom:sbom.json
osv-scanner -r .
trivy fs --scanners vuln,secret,misconfig .
npm audit --audit-level=high     pip-audit     cargo audit
cosign sign --key k image        cosign verify --key k.pub image
gitleaks detect --source .       trufflehog git file://.

Secrets / crypto helpers

age-keygen -o key.txt            age -r <recipient> -o f.age f      age -d -i key.txt f.age
vault kv put secret/app k=v      vault kv get secret/app
openssl rand -base64 24                                # a decent random password

Appendix D — What to use in 2026 (algorithm reference)

Defaults for new systems. When in doubt, use the bold option and move on — agonising over choices in this table is rarely where your risk is.

Symmetric encryption

Need Use Avoid
General encryption AES-256-GCM or ChaCha20-Poly1305 (AEAD) ECB (ever), unauthenticated CBC/CTR, openssl enc for anything serious
No AES hardware (mobile/embedded) ChaCha20-Poly1305 AES-CBC software
Nervous about nonce management XChaCha20-Poly1305 (192-bit nonce) reusing a 96-bit nonce
Disk encryption AES-XTS (via LUKS/FileVault/BitLocker) rolling your own
File encryption tool age GPG for new designs, openssl enc

Hashing

Need Use Avoid
General hashing, integrity, signatures SHA-256 (or SHA-512 / SHA-384 for margin) MD5, SHA-1 (broken)
Very high throughput BLAKE3 —
Keyed integrity between two parties HMAC-SHA-256 hash(secret‖msg) (length extension)
Password / passphrase storage Argon2id (≥19 MiB, t≥2); scrypt or bcrypt acceptable SHA-*/MD5, plain or salted; "encryption"
Post-quantum margin for hashes SHA-384 / SHA-512 truncating below 256-bit output

Key exchange

Need Use Avoid
TLS / general X25519; X25519MLKEM768 (hybrid PQ) where supported static DH, DH groups < 2048-bit, RSA key transport
SSH mlkem768x25519-sha256 or sntrup761x25519-sha512 diffie-hellman-group1, group14-sha1
Constrained interop P-256 (secp256r1) P-192, custom curves

Signatures

Need Use Avoid
General / new systems Ed25519 DSA, ECDSA without RFC 6979, RSA PKCS#1 v1.5 for new designs
Interop with existing PKI ECDSA P-256 (RFC 6979 / deterministic), or RSA-2048 PSS ssh-rsa (SHA-1), RSA-1024
Post-quantum, general ML-DSA-65 (hybrid with Ed25519 first) rolling your own
Post-quantum, long-lived verifier (firmware, roots) SLH-DSA (or stateful LMS/XMSS with airtight state mgmt) ML-DSA where a decade+ of lattice risk matters
JWT algorithm EdDSA or ES256; RS256 acceptable none; HS256 with a weak secret; unpinned alg

Public-key sizes (if you must use RSA)

RSA ≈ symmetric strength Verdict
1024 80-bit broken-ish, do not use
2048 112-bit minimum acceptable
3072 128-bit good; = 256-bit ECC
4096 ~140-bit fine, slower; common for roots

TLS

   Protocols:  TLS 1.3 + TLS 1.2 only. Disable 1.1 / 1.0 / SSLv3.
   TLS 1.2 suites: ECDHE + AES-GCM or ChaCha20-Poly1305 ONLY.
   TLS 1.3 suites: the default 5 (prefer AES-256-GCM / ChaCha20-Poly1305).
   Cert:      ECDSA P-256 leaf (RSA-2048 fallback chain only if needed).
   Key exch:  X25519 (+ P-256); add X25519MLKEM768 where supported.
   On:        HSTS (1 year, includeSubDomains), OCSP stapling.
   Off:       0-RTT (unless every early-data path is audited), compression.
   Generate config with: Mozilla SSL Config Generator ("intermediate").
   Verify with: testssl.sh / SSL Labs -> aim for A/A+.

SSH

   KexAlgorithms   mlkem768x25519-sha256,sntrup761x25519-sha512@openssh.com,curve25519-sha256
   Ciphers         chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
   MACs            hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
   Keys            ed25519 (or ecdsa-sk/ed25519-sk for humans); rsa-sha2-512 only for legacy
   Auth            publickey only; PasswordAuthentication no; PermitRootLogin no

Passwords / auth

   Storage:   Argon2id (verify params against the current OWASP Password
              Storage Cheat Sheet before shipping).
   Policy:    allow long passphrases; screen against Have I Been Pwned;
              NO forced composition rules; NO forced periodic rotation
              (NIST SP 800-63B).
   MFA:       offer TOTP as a baseline; push users toward WebAuthn/passkeys
              (the only phishing-resistant option). SMS = last resort.
   Sessions:  opaque 128-bit IDs server-side by default; rotate on login;
              Secure; HttpOnly; SameSite=Lax|Strict; __Host- prefix.
   OAuth:     Authorization Code + PKCE for ALL clients. Validate state,
              exact redirect_uri, and (OIDC) id_token sig/iss/aud/exp/nonce.

Randomness

   Keys / tokens / salts / nonces:  the OS CSPRNG.
     /dev/urandom, getrandom(2), secrets (Python), crypto.randomBytes (Node),
     crypto/rand (Go), SecureRandom (Java), openssl rand.
   NEVER: rand(), Math.random(), mt19937, time/PID seeds, "random enough".

Appendix E — Reading list, and how the facts here were checked

How this guide's technical claims were verified

   * Every worked cryptographic example (Base64 padding, hex, MD5/SHA-1/
     SHA-256 digests including the avalanche comparison, toy RSA
     p=61/q=53/e=17/d=2753, toy Diffie-Hellman p=23/g=5, the ECDSA
     k-reuse key recovery, AEAD tamper-detection, RSA-PSS / ECDSA /
     X25519 round trips) was executed and its output confirmed before
     being written down.
   * OpenSSL / ssh-keygen / dig command sequences were run on a real
     system. Where the local build was LibreSSL (macOS) and a command
     needs OpenSSL 3.x or 3.5+, that is called out inline and in Ch 0.4 --
     the commands are written for OpenSSL 3.x.
   * Algorithm parameters (ML-KEM-768: 1184-byte public key, 1088-byte
     ciphertext, 32-byte shared secret; ML-DSA-65: 1952 / 3309 bytes;
     SLH-DSA signature sizes; AES round counts; RSA<->symmetric strength
     equivalences) are from the FIPS 203/204/205 standards and NIST
     SP 800-57 / SP 800-131A.
   * Migration timelines are from NIST IR 8547 (draft), NSA CNSA 2.0, and
     UK NCSC guidance as published at the time of writing (dates in Ch 23,
     25 are absolute).
   * Historical incidents (DigiNotar 2011, Flame 2012, Heartbleed 2014,
     SHAttered 2017, Capital One / SSRF 2019, SolarWinds 2020, Log4Shell
     2021, dependency confusion 2021, xz-utils backdoor 2024) are
     described from the public post-mortems and advisories.
   * OWASP Top 10 category names/numbers follow the 2025 edition; the guide
     stresses that rankings shuffle between editions while the bug classes
     are stable.

If you find an error: treat the primary source (RFC, FIPS, vendor advisory, openssl/ssh on your own up-to-date system) as authoritative over this guide, and over any single blog post.

Books

   FOUNDATIONS / CRYPTO
     Serious Cryptography, 2nd ed. -- Jean-Philippe Aumasson   (the one to own)
     Real-World Cryptography -- David Wong                     (engineering focus)
     Cryptography Engineering -- Ferguson, Schneier, Kohno
     The Code Book -- Simon Singh                              (accessible history)

   TLS / PKI / NETWORK
     Bulletproof TLS and PKI, 2nd ed. -- Ivan Ristic
     OpenSSL Cookbook -- Ivan Ristic                           (free)
     SSH Mastery, 2nd ed. / DNSSEC Mastery, 2nd ed. -- Michael W. Lucas
     Practical Packet Analysis, 3rd ed. -- Chris Sanders
     The Practice of Network Security Monitoring -- Richard Bejtlich

   WEB / APP SECURITY
     The Web Application Hacker's Handbook, 2nd ed. -- Stuttard & Pinto
     The Tangled Web -- Michal Zalewski
     Real-World Bug Hunting -- Peter Yaworski
     Alice and Bob Learn Application Security -- Tanya Janca
     Designing Secure Software -- Loren Kohnfelder

   DESIGN / OPERATIONS / IR
     Threat Modeling: Designing for Security -- Adam Shostack
     Security Engineering, 3rd ed. -- Ross Anderson            (free PDF; huge)
     Incident Response & Computer Forensics, 3rd ed. -- Luttgens et al.
     Building Secure and Reliable Systems -- Google (SRE)      (free)

   GOING DEEPER
     Practical Malware Analysis -- Sikorski & Honig
     The Hardware Hacking Handbook -- Woudenberg & O'Flynn
     Hacking Kubernetes -- Martin & Hausenblas

Free courses and practice

   PortSwigger Web Security Academy   -- the best free web-security training
   Cryptopals (cryptopals.com)         -- learn crypto by breaking it
   CryptoHack (cryptohack.org)         -- gamified crypto challenges
   pwn.college                          -- systems / binary exploitation
   TryHackMe / HackTheBox              -- guided -> unguided, legal labs
   picoCTF                              -- beginner CTF, great archives
   OverTheWire (Bandit -> ...)         -- shell + basics
   flaws.cloud / flaws2.cloud           -- AWS security
   OWASP Juice Shop / WebGoat / DVWA   -- vulnerable apps to run locally

Primary sources worth reading directly

   RFC 8446 (TLS 1.3)            -- Section 2 is genuinely readable
   RFC 8439 (ChaCha20-Poly1305)  -- with test vectors
   RFC 4253 / 4252 / 4254 (SSH)  -- the three sub-protocols
   RFC 8555 (ACME)               -- how Let's Encrypt works
   RFC 6749 + RFC 9700 (OAuth 2.0 + Security BCP)
   FIPS 197 (AES), FIPS 203/204/205 (ML-KEM / ML-DSA / SLH-DSA)
   NIST SP 800-63B (authentication), 800-207 (zero trust),
        800-61 (incident handling), 800-57 (key management), IR 8547 (PQC)
   OWASP: Top 10, API Top 10, ASVS, WSTG, and the Cheat Sheet Series
   Saltzer & Schroeder, "The Protection of Information in Computer Systems" (1975)
   Diffie & Hellman, "New Directions in Cryptography" (1976)

Illustrated / interactive explainers

   tls13.xargs.org / tls12.xargs.org   -- every byte of a TLS handshake
   howhttps.works                       -- illustrated HTTPS
   badssl.com                           -- broken TLS configs to test clients
   jwt.io                               -- decode JWTs live
   crt.sh                               -- browse Certificate Transparency logs
   dnsviz.net                           -- visualise a domain's DNSSEC chain
   csp-evaluator.withgoogle.com         -- find the holes in a CSP
   canarytokens.org                     -- free tripwire tokens

Staying current (see Ch 58 for the full list)

   Blogs:      Project Zero, Trail of Bits, PortSwigger Research,
               Cloudflare / Google / AWS security blogs
   Newsletter: tl;dr sec
   Podcast:    Risky Business
   Advisories: GitHub Advisory DB, OSV.dev, your distro's security list
   Standards:  the IETF WG / NIST project / CA-Browser Forum for areas you
               operate in

Appendix F — Answers to "Check yourself"

Brief answers. If yours differs in wording but matches in substance, you're fine. Page back to the chapter for anything that doesn't click.

Chapter 1 — What "security" actually means

  1. CIA triad: Confidentiality (only authorised parties see data), Integrity (data isn't altered undetected), Availability (the system is usable when needed). Every control serves one or more.
  2. They trade off: encrypting everything can hurt availability/performance; aggressive lockouts hurt availability; maximum availability (no auth) destroys confidentiality. Security is choosing the right balance for the context.
  3. Defence in depth: layered independent controls so no single failure is catastrophic. The 2017 credit-bureau breach chained ~five failures (unpatched framework, flat network, no egress filtering, expired traffic-inspection cert, plaintext credentials) — any one control working would have blunted it.
  4. Because the threat, the value at risk, and the acceptable cost differ per system. "Secure" is meaningless without "against whom, protecting what, at what cost".

Chapter 2 — Thinking like an attacker

  1. Attack surface: every point an attacker can interact with (endpoints, inputs, ports, dependencies, people). You reduce it by removing features, inputs, and exposure.
  2. Trust boundary: where data moves between different trust levels or owners (internet→app, app→DB, user→server). Threats cluster there because that's where assumptions change.
  3. Recon → weaponisation/delivery → exploitation → installation/persistence → command & control → actions on objectives. Defenders try to break any link.
  4. It forces you to enumerate entry points and assets systematically rather than guessing, and it shows where one control protects many paths.

Chapter 3 — The vocabulary

  1. Encoding is reversible with no key (transport). Hashing is one-way, no key (fingerprint). Encryption is reversible with a key (confidentiality). Only encryption keeps data secret.
  2. Authentication = who you are; authorization = what you may do. Authn comes first; authz is checked on every request.
  3. A threat is a bad outcome someone wants; a vulnerability is a weakness; an exploit is the technique that turns the vulnerability into the threat; risk = likelihood × impact.
  4. Fail-closed denies on error (default for security controls); fail-open allows on error (sometimes chosen for availability, e.g. a badge reader on a fire exit). Choose deliberately per context.

Chapter 5 — How text becomes bytes

  1. ASCII assigns the character '0' the code 48; the number zero is a different value. Confusing the two breaks parsing.
  2. Character count ≠ byte count (a 10-char name can be 40 bytes), so length limits and buffer sizes can disagree → truncation or overflow.
  3. Different parsers handle invalid UTF-8 differently (reject / replace / drop), so two components can disagree about the same input — the root of many filter bypasses.

Chapter 6 — Base64, hex, URL encoding

  1. To carry arbitrary bytes through channels that only accept a limited "safe" character set (email, URLs, JSON, terminals).
  2. It maps every 3 bytes to 4 characters (6 bits each), so output is 4/3 the size.
  3. Padding: = marks that the final group had only 1 or 2 input bytes (== = 1 byte, = = 2 bytes).
  4. No — encoding has no integrity protection. Flip a character and it decodes to different (or invalid) data with nothing to detect the change.
  5. No. A JWT payload is Base64url and readable by anyone; only its signature provides integrity. Never put secrets in it.

Chapter 7 — Unicode and normalisation

  1. e.g. é as U+00E9, or as e (U+0065) + combining acute (U+0301). Same glyph, different bytes.
  2. Because some component may normalise after your check (NFKC folds full-width < to <), turning previously-safe input into an attack. Normalise first, then validate.
  3. Substituting a visually identical character from another script (Cyrillic а for Latin a) so раypal.com looks like paypal.com — the basis of IDN phishing.
  4. You can enumerate exactly what's permitted ([a-z0-9_-]); you can't enumerate every malicious variation (homoglyphs, invisibles, encodings).

Chapter 8 — What a hash function is

  1. Deterministic, one-way (preimage resistant), avalanche (one bit flips ~half the output), collision resistant.
  2. Infinitely many possible inputs map to finitely many outputs (2^256 for SHA-256), so collisions must exist mathematically; security is that finding one is infeasible.
  3. The birthday bound: finding any collision takes ~2^(n/2) work, so a 256-bit output gives 128-bit collision resistance.
  4. Low-entropy input: an attacker just hashes every possible card number / email / PIN and looks yours up. Hashing only hides high-entropy secrets.

Chapter 9 — Real hash functions

  1. Collision resistance fails: you can find two different inputs with the same hash cheaply (MD5 in seconds, SHA-1 with real effort). Preimage resistance still holds, but that's not enough.
  2. A signature signs the hash of a document; if you can craft two documents with the same hash, a signature on one is valid for the other — get a benign file signed, swap in the malicious one.
  3. Given hash(secret‖msg) and len(secret), an attacker computes a valid hash for msg‖padding‖extra without the secret (SHA-1/SHA-2 structure). Fix: use HMAC, not a different hash.
  4. SHA-256 by default (SHA-512 / SHA-3-256 for margin; BLAKE3 for speed). Never MD5 or SHA-1 for security.

Chapter 10 — Password storage

  1. SHA-256 is fast — a GPU does billions/sec, so an attacker who steals the database cracks most passwords quickly.
  2. A salt is a random, unique-per-user value stored alongside the hash. It's not secret. It stops precomputation (rainbow tables) and hides that two users share a password.
  3. Memory-hard = the function needs a lot of RAM per evaluation, which defeats GPUs/ASICs whose many small cores can't each get enough memory.
  4. Argon2id — roughly ≥19 MiB memory, ≥2 iterations, parallelism 1 (verify against the current OWASP cheat sheet; tune to ~250–500 ms on your hardware).
  5. Username enumeration: the app reveals which accounts exist via different messages/timing/status for "no such user" vs "wrong password". Prevent it with identical responses and timing for both.

Chapter 11 — HMAC

  1. Anyone can recompute a plain hash, so an attacker who changes the message just recomputes the hash. It detects accidents, not adversaries.
  2. HMAC adds a secret key: it proves the message is unchanged and came from someone holding the key. Both parties must share that key.
  3. A MAC uses one shared key (either party can make tags, so no third-party proof). A signature uses a private/public keypair — only the key holder can sign, and anyone can verify, giving non-repudiation.
  4. A byte-by-byte == returns early on the first mismatch; timing leaks how many leading bytes are correct, letting an attacker forge a tag one byte at a time.
  5. When both parties are you (session cookies, internal service auth, webhooks) — HMAC is faster and simpler and you don't need third-party verifiability.

Chapter 12 — Symmetric crypto idea

  1. Kerckhoffs's principle: the system stays secure even if everything except the key is public. "Proprietary/secret" crypto is a warning sign because it hasn't survived public scrutiny.
  2. The key must be as long as the message; it must be truly random; it must never be reused; and it provides no integrity.
  3. The key cancels: C1 XOR C2 = P1 XOR P2. The attacker gets the XOR of the two plaintexts, separable with language statistics.
  4. No — encryption gives confidentiality only. Tampering needs a MAC or AEAD.

Chapter 13 — Block ciphers and AES

  1. 128 bits (16 bytes) — always, for every key size. Only the round count changes (10/12/14 for 128/192/256).
  2. SubBytes (non-linear substitution), ShiftRows (row shifts), MixColumns (column mixing), AddRoundKey (XOR the round key).
  3. On hardware without AES acceleration (some phones/embedded), ChaCha20 is faster in software and is constant-time by design.
  4. PKCS#7 padding always adds bytes so padding can be unambiguously removed; a full 16-byte input gets a whole extra block of padding → 32 bytes.

Chapter 14 — Modes of operation

  1. ECB encrypts each block independently, so identical plaintext blocks → identical ciphertext blocks; large uniform regions stay visible (the penguin).
  2. The IV seeds the chain so identical plaintexts encrypt differently. For CBC it must be unpredictable (not just unique).
  3. The keystream repeats → C1 XOR C2 = P1 XOR P2 (one-time-pad reuse). Knowing one plaintext reveals the other; with GCM it also breaks authentication.
  4. ECB and CBC need padding (they operate on whole blocks) → padding-oracle risk. CTR and GCM are stream-like and need none.
  5. None of ECB/CBC/CTR provide integrity. Only the AEAD modes (GCM, ChaCha20-Poly1305) do.

Chapter 15 — AEAD

  1. Confidentiality (encryption) and integrity/authenticity (a tag), in one operation.
  2. AAD is data authenticated but not encrypted — e.g. a record ID, version, or header. It prevents ciphertext being swapped between contexts.
  3. Nothing — an error, and no plaintext at all. The tag is checked before any plaintext is released.
  4. In GCM, nonce reuse also leaks the authentication subkey, letting the attacker forge arbitrary messages, not just recover plaintext XOR.
  5. Without -pbkdf2 -iter, OpenSSL derives the key with a single MD5 iteration — trivially brute-forced. Those flags enable a slow, salted KDF.

Chapter 16 — Symmetric with OpenSSL

  1. It falls back to one round of MD5 for key derivation — weak and fast to crack.
  2. The IV isn't secret, but the receiver needs it to decrypt, so it's stored/sent with the ciphertext (typically prepended).
  3. Command-line arguments are visible in ps output and shell history. Pass keys via file, env var, or stdin.
  4. openssl enc has no way to output/verify the GCM authentication tag, so its "GCM" is unauthenticated. Use a real library or age.

Chapter 17 — Key distribution & the big idea

  1. Two parties who have never met, communicating over a network an attacker watches, need a shared key — but any way to send it exposes it.
  2. Encrypt to someone with their public key; sign with your private key. (Mailbox vs wax seal.)
  3. Asymmetric operations are slow and size-limited, so you use them once to establish a symmetric key, then do the bulk work with fast AEAD.
  4. It doesn't tell you whose public key it is — binding a key to an identity needs certificates / PKI (or a known SSH host key, a web of trust).

Chapter 18 — Diffie–Hellman

  1. It cannot "un-mix" the exchanged public mixtures to recover either private colour, so it can't compute the shared secret.
  2. The discrete logarithm problem (recover a from g^a mod p) in a large prime field.
  3. Ephemeral — fresh keys per session, discarded after — which gives forward secrecy: stealing a long-term key later doesn't decrypt past sessions.
  4. Man-in-the-middle: the attacker does separate exchanges with each side. Fix: authenticate the DH public values with signatures (certs / host keys).
  5. Run it through a KDF (HKDF) — raw DH output isn't uniformly random and may have structure.

Chapter 19 — RSA

  1. Factoring the product of two large primes.
  2. 65537 (0x10001) — small, so encryption/verification is fast; a Fermat prime, so it's coprime to φ; large enough to avoid small-exponent attacks.
  3. It's deterministic (dictionary/repeat detection) and malleable (c1·c2 mod n decrypts to m1·m2); small messages fall to a plain root.
  4. ~190 bytes for RSA-2048 + OAEP-SHA256 — only enough for a symmetric key. RSA is a key-transport primitive, not a data cipher.
  5. OAEP for encryption, PSS for signatures.
  6. No. Shor's algorithm factors any RSA key in polynomial time; size doesn't help.

Chapter 20 — Elliptic curve cryptography

  1. A 256-bit ECC key ≈ RSA-3072 (128-bit security level).
  2. The elliptic-curve discrete logarithm problem (ECDLP): recover the scalar k from Q = k·P, with no known sub-exponential shortcut.
  3. Reusing (or making predictable) the per-signature nonce k lets an attacker compute the private key from two signatures. Real incident: the Sony PS3 code-signing key extraction (fixed k).
  4. Ed25519 derives k deterministically from the key and message (like RFC 6979), so there's no RNG to fail and no k to repeat.
  5. X25519 for key exchange (ECDH), Ed25519 for signatures.
  6. No — Shor breaks ECDLP too, and ECC keys are smaller, so arguably fall first.

Chapter 21 — Digital signatures

  1. Non-repudiation — a third party can verify that the private-key holder (not the verifier) produced it. It also needs no pre-shared secret.
  2. Because the message is large and raw sign operations have exploitable structure; you sign a fixed-size hash instead.
  3. It breaks the scheme: a hash collision lets a signature on a benign document be valid for a malicious one. Real example: SHA-1 and the SHAttered collision → CAs dropped SHA-1.
  4. Who the key holder is; when it was signed; that the signer read/meant it; that the key isn't stolen or the signer coerced; freshness (replay).
  5. Put a nonce/timestamp/expiry inside the signed data.

Chapter 22 — Keys & signatures with OpenSSL

  1. openssl genpkey (and openssl pkey for inspection/conversion).
  2. Ed25519 hashes the message internally; you sign the raw message with pkeyutl -rawin, not dgst.
  3. Run the raw shared secret through a KDF (HKDF) to derive actual keys.
  4. Signing (the private-key op) is the slow direction; verification is fast. So TLS clients can cheaply verify a server's cert/signature on every connection while the CA signs rarely.

Chapter 23 — Why quantum breaks public-key crypto

  1. Shor's algorithm, which runs in polynomial time (period-finding) for factoring and discrete logs.
  2. Shor scales polynomially, so a bigger modulus only adds a small constant factor — RSA-16384 falls about as easily as RSA-2048.
  3. Grover halves the effective key strength: AES-256 → ~128-bit effective, still infeasible. Not a problem (use AES-256).
  4. Record encrypted traffic now, decrypt it once a quantum computer exists. Any data that must stay secret past ~2030s needs PQ key exchange today.
  5. Key-exchange migration is more urgent, because HNDL lets past sessions be broken retroactively; a signature forged in 2035 can't retro-authenticate a 2026 handshake.

Chapter 24 — Post-quantum algorithms

  1. ML-KEM (FIPS 203, key encapsulation), ML-DSA (FIPS 204, signatures), SLH-DSA (FIPS 205, hash-based signatures).
  2. Structured lattice problems (Module-LWE). Shor's period-finding doesn't apply to lattice problems; the best known attacks (classical or quantum) are exponential.
  3. A KEM: the recipient publishes a public key; the sender encapsulates → (shared secret, ciphertext); the recipient decapsulates the ciphertext to the same secret. No simultaneous two-way exchange.
  4. Public key 1184 B, ciphertext 1088 B, shared secret 32 B.
  5. SLH-DSA signatures are 8–50 KB and signing is slow (ms) — too heavy for per-connection TLS — but fine for firmware signed rarely and verified occasionally, where its hash-only security assumption is worth it.
  6. So a break of one mathematical family doesn't break everything — lattices (ML-*), hashes (SLH-DSA), and codes (HQC) are independent.

Chapter 25 — Hybrid and migration

  1. A classical exchange (X25519) and a PQ KEM (ML-KEM-768), combined through a KDF. Secure as long as either remains unbroken.
  2. Because it is both: X25519 plus ML-KEM-768 run together. Naming makes the composition explicit.
  3. Crypto-agility: you can change algorithms without a rewrite. Concretely: algorithms in config not code; algorithm+version tags on every stored blob/hash/signature; key storage behind an abstraction; ability to run two algorithms in parallel during migration; a current crypto inventory.
  4. The bootloader has a ~10-year field life and no practical way to update its trust anchor later, so it must be right now; the JWTs live minutes and can migrate on the normal library-upgrade cycle.
  5. Inventory — you can't migrate crypto you can't see, and you can't prioritise without knowing data lifetimes and exposure.

Chapter 26 — Post-quantum with OpenSSL

  1. openssl pkeyutl -encap (the sender, using the recipient's public key) and openssl pkeyutl -decap (the recipient, using their private key).
  2. ~3.3 KB for ML-DSA-65 vs 64 bytes for Ed25519.
  3. Tiny key, ~8 KB signature, slow signing, fast-ish verify — fine for firmware (signed once, verified occasionally, decade-long validity), wrong for high-volume TLS handshakes.
  4. No — the certificate can stay classical (ECDSA/RSA) for now; only the key-exchange group changes to X25519MLKEM768.
  5. The real cost is bytes on the wire, not CPU — ML-KEM's operations are as fast as or faster than X25519.

Chapter 27 — What HTTPS guarantees

  1. Confidentiality, integrity, and server authentication (plus forward secrecy in TLS 1.3).
  2. Any three of: it doesn't make the server trustworthy; doesn't hide which site you visit (DNS + SNI); doesn't protect data before/after the tunnel; doesn't authenticate the client by default; doesn't stop app bugs; doesn't hide traffic size/timing.
  3. The destination IP, the hostname (via DNS and SNI unless ECH), and packet sizes/timing.
  4. Because HTTPS is now near-universal (including on phishing sites), so "Secure" was misread as "trustworthy".
  5. At the server — often at a load balancer/CDN before the app. Everything past that point sees plaintext, so logs, backups, and a breached DB are outside TLS's protection.

Chapter 28 — The TLS 1.3 handshake

  1. One round trip for a full handshake; zero for resumption (0-RTT).
  2. CertificateVerify is a signature over the handshake transcript with the cert's private key — it proves the server holds that private key for this session, not just that it has a copy of the certificate.
  3. It's an HMAC over the whole transcript from each side — proving both parties saw the same handshake, which defeats downgrade and tampering.
  4. 0-RTT data is replayable by an attacker who captured it. Only idempotent, non-state-changing requests (GET/HEAD) may safely use it.
  5. Only the ClientHello and ServerHello (the keys don't exist yet); the SNI is visible unless ECH is used. Everything after ServerHello is encrypted.
  6. To save a round trip: the client guesses the likely group and sends its key_share immediately; if it guessed wrong the server sends a HelloRetryRequest.

Chapter 29 — Certificates and validation

  1. The Subject Alternative Name (SAN) DNS entries. If absent, modern clients reject the certificate (the CN is ignored).
  2. The server sends leaf + intermediate(s); the client already has the root in its trust store.
  3. Any five of: valid signature chain to a trusted root; hostname matches a SAN; current time within validity for every cert; correct basicConstraints (CA:TRUE on non-leaves, CA:FALSE on leaf); key usage / EKU permits serverAuth; not revoked; (browsers) sufficient CT SCTs.
  4. *.example.com matches a.example.com but not example.com and not a.b.example.com (one label only).
  5. DV proves control of the domain — nothing about who runs it. EV adds vetting of the legal entity; browsers dropped the special EV UI because users ignored it and it gave false assurance.
  6. Chrome caches intermediates it has seen before, so a server that omits the intermediate still works there; curl and other clients that don't cache fail.

Chapter 30 — PKI, CT, revocation, ACME

  1. Any of the ~50 trusted CAs can issue a certificate for any domain, so your security depends on all of them. Mitigations: CAA records (restrict which CA may issue) and CT monitoring (detect issuance you didn't request).
  2. It makes mis-issuance publicly detectable (usually within minutes). It does not prevent issuance or encrypt anything.
  3. Most clients soft-fail: if the OCSP responder is unreachable they proceed, so an attacker who can MITM just blocks OCSP. Replaced in practice by short-lived certificates and browser-pushed revocation lists (CRLSets / CRLite).
  4. Client asks CA for a cert for example.com; CA returns a token; client serves it at http://example.com/.well-known/acme-challenge/<token>; CA fetches it from multiple vantage points; on success, issues a ~90-day cert; client auto-renews at ~60 days.
  5. A CAA DNS record lists which CAs may issue for the domain; every CA is required to check it before issuing.
  6. DigiNotar (2011, hacked, issued fake *.google.com) and Symantec (2017–18, years of misissuance) — both removed from root stores.

Chapter 31 — TLS in production

  1. Edge termination (LB/CDN): plaintext from the LB to the app unless re-encrypted. App termination: encrypted to the process. Re-encrypt / mesh: every hop is TLS/mTLS — no plaintext in transit.
  2. For service-to-service auth, API clients, admin access, IoT fleets, partner integrations. Burden: you now run a mini-PKI — issuing, rotating, and revoking client certs.
  3. HSTS defeats sslstrip (an attacker downgrading your redirect to HTTP). Gap: the first visit before any HSTS header is seen — closed by the browser preload list.
  4. Any five: disabled cert verification (-k / verify=False / rejectUnauthorized:false / no-op TrustManager); mixed content; missing intermediate; wildcard shared across trust boundaries; private key in git / world-readable; key reused across renewals; TLS 1.0/1.1 enabled; cert missing www or apex; trusting X-Forwarded-* from anywhere.
  5. Powerful: it defeats a mis-issued or rogue-CA certificate outright. Dangerous: if you rotate keys/CAs without a shipped backup pin, the app bricks itself. Rules: pin a CA/intermediate SPKI (not the leaf); always ship ≥2 pins including an offline backup; set an expiry and a killswitch.
  6. Because anyone who can reach the app directly can spoof the header, forging the client IP and bypassing "HTTPS-only" / IP-allowlist checks. Only trust it from the known LB.

Chapter 32 — Inspecting TLS

  1. </dev/null (or type Q) stops it hanging; -servername <host> sets SNI.
  2. Compare the public keys: openssl x509 -in cert -noout -pubkey and openssl pkey -in key -pubout must be identical.
  3. openssl s_client -connect host:443 -tls1_1 — it should fail now.
  4. Any five: TLS 1.3 + 1.2 only; AEAD-only ciphers; forward secrecy on all suites; cert not near expiry with correct SANs and complete chain; sane key size and SHA-256 signature; no known vulns (Heartbleed/ROBOT/POODLE/…); HSTS present; OCSP stapling on.
  5. No — BREACH is HTTP-response compression plus secrets in the body. Fix it at the app (disable compression on sensitive responses, mask CSRF tokens), not in TLS config.

Chapter 33 — SSH and the connection protocol

  1. Transport (key exchange, server auth via host key, encrypted records), user authentication (proves who the client is), connection (multiplexes channels: shells, forwards, SFTP).
  2. The server is authenticated first (in the transport layer), then the user authenticates inside the encrypted channel.
  3. Rely on publickey; disable password authentication on internet-facing hosts.
  4. It has defaulted to a post-quantum hybrid key exchange (sntrup761x25519 since OpenSSH 9.0, mlkem768x25519 in OpenSSH 10), so recorded sessions resist future quantum decryption.
  5. ssh -D opens a local SOCKS proxy that routes arbitrary TCP through the remote host; ssh -L forwards one specific remote service to a local port.

Chapter 34 — Keys, agents, host verification

  1. Ed25519 — small, fast, no parameter footguns, and it avoids ECDSA's per-signature k nonce risk.
  2. The agent holds decrypted private keys in memory and signs on request (unlock once per session). Agent forwarding (-A) exposes that signing capability to the remote host — anyone with root there can authenticate as you.
  3. ProxyJump / -J — the jump host only forwards bytes and never sees your keys or agent.
  4. TOFU = trust the host key on first connection, pin it in known_hosts, and compare on every later connection. Weakness: an attacker on-path during that first connection gets their key pinned. Remove the prompt via config-management-distributed known_hosts, SSHFP+DNSSEC, or an SSH CA (@cert-authority).
  5. restrict = deny all SSH features, then add back only what's listed. command="..." forces a single command regardless of what the client requests — the basis of locked-down deploy/backup accounts.

Chapter 35 — SSH certificates, hardening, bastions

  1. It's O(users × hosts) entries to keep in sync, with stale keys lingering and painful re-TOFU on rebuilds. Replace authorized_keys with user certificates (CA-signed, short-lived) and known_hosts with host certificates (one @cert-authority line).
  2. @cert-authority *.example.com <CA public key> — any host presenting a valid cert for its name is then trusted.
  3. A leaked cert simply expires within hours (-V +8h), so there's rarely anything to revoke; a KRL covers the rare urgent case.
  4. Any five: PermitRootLogin no; PasswordAuthentication no; AuthenticationMethods publickey (or add MFA); AllowGroups allowlist; AllowTcpForwarding no / AllowAgentForwarding no on app servers; modern KexAlgorithms/Ciphers/MACs; MaxAuthTries 3; LogLevel VERBOSE.
  5. -J routes through the bastion without exposing your keys or agent to it; -A forwards your agent socket to the bastion, letting a compromised bastion act as you.

Chapter 36 — SSH hands-on

  1. ProxyJump = transparently connect via a bastion; IdentitiesOnly = offer only the named key (avoids "too many auth failures"); ControlMaster = reuse one TCP connection for multiple sessions.
  2. -L 5433:db:5432 host = reach a remote DB via localhost:5433; -R 8080:localhost:3000 host = expose your local :3000 on the remote's :8080 (demos/webhooks); -D 1080 host = SOCKS proxy to reach an internal network.
  3. ssh-keygen -Y sign -f key -n <namespace> file then ssh-keygen -Y verify -f allowed_signers -I <id> -n <namespace> -s file.sig < file. The -n namespace scopes what the signature means (e.g. file, git) so a signature can't be replayed in a different context.
  4. Wrong key / key not in authorized_keys / file permissions too open (~/.ssh must be 700, private key 600, and HOME not group-writable).
  5. sudo sshd -t.

Chapter 37 — The network attacker

  1. Passive on-path: reads only → defeat with encryption. Active on-path (MITM): reads/modifies → defeat with authentication + integrity. Off-path: must spoof/guess → defeat with randomised IDs and ingress filtering.
  2. ARP has no authentication, so a forged "the gateway is at my MAC" is believed. Dynamic ARP Inspection (with DHCP snooping) drops replies that don't match a trusted binding.
  3. Either a cert from a CA whose root is in the victim's trust store (e.g. a corporate inspection root on a managed device) or a genuine mis-issued cert (which CT would surface quickly). A self-signed/wrong-name cert is refused with no click-through.
  4. The employer installs its own root CA on managed devices; its proxy then presents certs it signs for any site. The padlock is technically real (valid chain to a trusted root), but the proxy is reading the traffic.
  5. An AS announces IP prefixes it doesn't own, detouring traffic through it. Real incident: the 2018 Amazon Route 53 / MyEtherWallet hijack (crypto theft).

Chapter 38 — DNS security

  1. Any three: stub↔recursive (on-path spoof, rogue DHCP, edited resolv.conf); recursive↔authoritative (off-path cache poisoning); the recursive's cache / a compromised resolver; the registrar/registry account (domain hijack); the authoritative server or zone.
  2. Every guess is a fresh race, and a winning spoof can poison an NS record for the whole zone. Mitigations: source-port randomisation and 0x20 case encoding — they raise the entropy but don't authenticate the answer, so they're not a true fix. DNSSEC is.
  3. Data-origin authentication and integrity for DNS records (plus authenticated denial of existence). It does not encrypt anything, and it doesn't protect the stub↔recursive hop unless that resolver validates and you trust it.
  4. DoT = port 853, a dedicated port (easy to see and block, content encrypted). DoH = port 443, blends with web traffic (hard to block, also bypasses enterprise DNS controls). Trade-off: network privacy vs. operator/enterprise visibility.
  5. Registry Lock: a manual, out-of-band step required to change NS records or transfer the domain — stops registrar-account compromise / social-engineering domain hijacks.
  6. A DNS record (often a CNAME) pointing at a decommissioned third-party resource (S3 bucket, Heroku app). An attacker who claims that resource gets subdomain takeover.

Chapter 39 — Firewalls, segmentation, zero trust, VPNs

  1. A stateful firewall tracks connection state, so it can allow return traffic for connections it permitted outbound without a separate rule (and drop unsolicited/invalid packets).
  2. Blast-radius control — a compromise in one segment can't freely reach the others.
  3. Never trust, always verify: no implicit trust from network location; every request to every resource is authenticated and authorised per-session. Castle-and-moat trusts everything "inside" the perimeter.
  4. Good for: authenticated encrypted tunnels over untrusted networks, remote access, site-to-site links. Not: anonymity; a substitute for endpoint security or app authorization; "zero trust".
  5. It removes negotiation, so there's no downgrade attack and no misconfiguration of weak suites; you revise the whole protocol version instead.
  6. AllowedIPs is both the routing rule (which peer gets which destination IPs) and the ACL (which source IPs are accepted from that peer).

Chapter 40 — Seeing the network

  1. '(udp port 53 or (tcp port 443 and host 10.0.0.5)) and not port 22'.
  2. A capture filter (BPF, e.g. tcp port 443) decides what's recorded; a display filter (Wireshark syntax, e.g. tls.handshake.type == 1) decides what's shown from an existing capture. Different languages.
  3. Run the client with SSLKEYLOGFILE set, then point Wireshark's TLS (Pre)-Master-Secret log file at it.
  4. -sV gives service versions; -sC (or --script default) runs the default script set.
  5. HSTS — it makes the browser refuse the plaintext connection sslstrip needs, so the downgrade never happens.

Chapter 41 — How the web decides what to trust

  1. Scheme + host + port. http://x and https://x are different origins (scheme differs).
  2. evil.example can send requests to bank.example (with cookies), and embed its images/scripts/frames — but it cannot read the response body, cookies, storage, or framed DOM.
  3. Secure = only sent over HTTPS; HttpOnly = JavaScript can't read it (blunts cookie theft via XSS); SameSite = not sent on cross-site requests (blunts CSRF).
  4. No — the spec forbids * with credentials, and the usual underlying mistake is reflecting the Origin header without an allowlist, which is effectively the same thing.
  5. No. CORS is a browser mechanism; a Python script (or curl) ignores it entirely. Your API still needs real authn/authz on every endpoint.

Chapter 42 — Injection

  1. Keep code and data on separate channels — parameterise, never concatenate untrusted input into a string an interpreter will parse.
  2. execute("... WHERE id = ?", [id]) — the query structure is fixed and id is bound as data.
  3. Identifiers (table/column names), ORDER BY targets, LIMIT in some drivers. Validate them against a strict allowlist of known-good values.
  4. Use the array/execFile form that passes arguments directly to the program with no shell (subprocess.run(["ping","-c","1",host])), and still validate the value.
  5. Input that was stored safely, then later concatenated into a query when it's read back — so every query must be parameterised, not just the ones handling direct input.

Chapter 43 — XSS

  1. Stored (payload persisted server-side, hits every viewer), reflected (payload echoed from the request, needs a lure), DOM-based (client JS moves attacker input into a dangerous sink, no server involvement).
  2. Any four: make authenticated requests as the user; read the DOM/page; read localStorage/sessionStorage and non-HttpOnly cookies; keylog and phish with a fake overlay; pivot to internal APIs; persist via a service worker.
  3. Encoding untrusted data for the exact context it lands in (HTML body vs attribute vs JS vs URL vs CSS) — each has different rules, so one encoding isn't enough; data in a javascript: URL or a <script> block can't be made safe by HTML-encoding at all.
  4. 'unsafe-inline' and 'unsafe-eval' (and broad host allowlists that host JSONP/old Angular). A weak CSP uses those; a strong one is nonce/hash-based or strict-dynamic.
  5. Sanitise with a vetted allowlist library (DOMPurify) permitting only specific tags/attributes — never a homemade regex.

Chapter 44 — CSRF, SSRF, clickjacking

  1. SOP lets the browser send cross-origin requests (with cookies) but not read the response. CSRF still works because the attacker only needs the side effect (the transfer happens), not the response body.
  2. Defences: SameSite cookies; anti-CSRF synchronizer tokens; requiring a custom header (forces a preflight); re-auth for sensitive actions. Gap in SameSite=Lax: it's still sent on top-level GET navigations (and there are same-site-subdomain and non-cookie-auth caveats).
  3. http://169.254.169.254/... — the cloud instance metadata service. The Capital One breach (2019) used SSRF to it to steal IAM credentials.
  4. Blocklists miss 0.0.0.0, [::1], decimal/octal IPs, DNS names that resolve to private ranges, redirects, and DNS rebinding. Right approach: allowlist destinations and schemes, then resolve the host and check the resulting IP against private/loopback/link-local/metadata ranges — before connecting and after every redirect.
  5. Content-Security-Policy: frame-ancestors 'none' (keep X-Frame-Options: DENY for old browsers).

Chapter 45 — Broken access control

  1. IDOR/BOLA: accessing another user's object by changing its identifier because the app checks authentication but not ownership. UUIDs don't fix it because they leak (URLs, Referer, logs, other endpoints) — unguessable ≠ authorised.
  2. Vertical = gaining a higher privilege level (user → admin). Horizontal = accessing a peer's data at the same level (user A → user B).
  3. Binding a whole request body to a model so the client can set fields it shouldn't (role, credits). Fix: an explicit allowlist of updatable fields (a DTO).
  4. The client is attacker-controlled, so UI restrictions and hidden routes mean nothing — the API is the boundary. Centralising it (one policy layer) prevents the inconsistent, missed ad-hoc checks that cause breaches.
  5. SELECT * FROM orders WHERE id = ? AND customer_id = :current_user. Return 404 rather than 403 so you don't confirm the object exists (enumeration).
  6. Because the client is attacker-controlled — recompute every monetary value server-side from stored prices and validate coupons server-side.

Chapter 46 — Authentication and identity

  1. Stateful sessions: trivially revocable and inspectable (drawback: needs a shared store). Stateless JWTs: scale without a lookup (drawback: hard to revoke, footgun-rich, claims readable by the client).
  2. Session fixation: an attacker fixes a victim's session ID before login and reuses it after. Fix: regenerate the session ID on login / privilege change.
  3. Any three: alg:none (pin the algorithm, reject none); RS256→HS256 confusion (pin alg, use separate verify APIs); weak HMAC secret (long random key); missing exp/aud/iss checks (validate all); token in localStorage (XSS reads it — use a cookie with flags); no revocation (keep them short-lived + denylist).
  4. OIDC authenticates the user (OAuth 2.0 only authorises resource access). Use the Authorization Code flow with PKCE for all clients; PKCE prevents authorization-code interception.
  5. WebAuthn credentials are bound to the origin and use a cryptographic challenge-response, so a phishing site on a different origin can't use them. TOTP codes can be relayed in real time to the real site.
  6. Because "reset via email" makes email the true root of trust — recovery is often weaker than primary auth. Standard: make recovery as strong as primary auth (or deliberately weaker, with monitoring).

Chapter 47 — Supply chain

  1. In the transitive dependencies — the hundreds you didn't choose and don't review, any of which can run install-time code and ship to prod.
  2. Publishing a public package with the same name as a company's private one and a higher version, so the resolver picks the public (malicious) one. Prevent with scoped names / an explicit private index with no public fallback / a pull-through proxy allowlist.
  3. A Software Bill of Materials — a complete component+version inventory per build — so when a CVE drops you can answer "are we affected?" in minutes.
  4. Tags are mutable — the same tag can be repointed to malicious code later. A digest/SHA pins the exact artifact.
  5. The build system itself was compromised, so the officially signed artifact was backdoored — signing the release doesn't help if the pipeline is owned. Protect the build, not just the release step.
  6. e.g. Python pickle.load / PyYAML yaml.load (unsafe), Java ObjectInputStream, PHP unserialize, .NET BinaryFormatter — all can lead to RCE on untrusted data. Use JSON / schema-validated formats (or yaml.safe_load).

Chapter 48 — Testing a web application

  1. A proxy (Burp/ZAP) between browser and app that lets you inspect, modify, and replay every request — the core tool because it makes the app's real interface visible and manipulable.
  2. Broken access control. Technique: create two accounts (A and B); for every object/endpoint A can use, replay the request as B and as anonymous; change IDs.
  3. ' " \ ; < > and a URL/{{77}}/../— quotes/semicolons hint at SQL or command injection (errors, timing),</>at XSS (check the reflection context),{{77}}→49at template injection,../` at path traversal, a URL param at SSRF.
  4. Any three: verbose errors / stack traces; exposed .git/, .env, backup files; debug endpoints (/actuator); directory listing; default credentials; outdated JS libraries (retire.js); missing security headers.
  5. Title; severity (CVSS + business impact); affected endpoint/params; reproducible steps with request/response evidence; root cause; specific remediation; references — and a retest result after the fix.

Chapter 49 — Secrets management

  1. Any four: CI/CD logs; container image layers (ARG/ENV, docker history); client-side bundles/binaries; application logs and APM; Slack/Jira/wiki/ screenshots; DB dumps and VM snapshots; environment variables (via /proc, crash dumps, child processes).
  2. Best → worst: Vault dynamic credential → encrypted file at rest → env var → hard-coded.
  3. A credential minted on demand with a short TTL (e.g. a DB user that self-destructs in an hour), so a leaked one is useless almost immediately — limiting blast radius.
  4. Create the new secret alongside the old; deploy consumers to accept either; switch producers to the new; confirm the old is unused; revoke the old. Keep two versions live so rotation is routine.
  5. Rotate/invalidate the secret immediately — assume it's compromised. Then investigate and clean history. Deleting the commit is not remediation.

Chapter 50 — Logging, detection, monitoring

  1. Any six of: authentication events, authorization denials, input-validation rejections, account lifecycle, sensitive data access/export, config/deploy changes, integrity failures, system events. Every event: UTC ISO-8601 timestamp, user ID, source IP, user-agent, request ID, action, outcome.
  2. Passwords, session tokens, API keys, full JWTs, MFA codes/secrets, full card numbers / CVV, raw national IDs, full sensitive request bodies. Prevent by redacting at the logging boundary (field allowlist, hash identifiers, mask PANs) and neutralising newlines/control chars (log injection).
  3. Central so an attacker on one host can't erase the evidence; append-only so it can't be altered; time-synced (NTP) so events across systems can be correlated.
  4. A good alert is actionable and means "attack in progress" (low false-positive rate). Besides the rule it needs a runbook — what to check, how to triage, when to escalate.
  5. Detection-as-code = detection rules kept in version control, peer-reviewed, and tested (e.g. Sigma). Mapping to ATT&CK shows your coverage and the gaps.

Chapter 51 — Incident response

  1. Preparation, Detection & Analysis, Containment, Eradication, Recovery, Post-incident. Preparation consumes most of the effort.
  2. Pulling the network stops the bleeding while preserving RAM (keys, live malware, processes) for forensics. Power off when you must stop something irreversible fast — e.g. active ransomware encryption.
  3. CPU registers/cache → RAM → network state → running processes → disk → logs/archives → physical config (most volatile first).
  4. A documented record of who handled which evidence, when, and why — it keeps the evidence admissible and the timeline trustworthy.
  5. At the point you become aware of a personal-data breach — not when you fully understand it (GDPR: 72 hours from awareness).
  6. It focuses on systemic causes, not individuals. Blame makes people withhold information in the next incident, which makes you slower and blinder.

Chapter 52 — Threat modeling and secure design

  1. What are we building? What can go wrong? What are we going to do about it? Did we do a good job?
  2. Spoofing → authentication/mTLS/signatures; Tampering → integrity (AEAD/HMAC/signatures), access control; Repudiation → audit logging, signatures; Information disclosure → encryption, least privilege, redaction; Denial of service → rate limits, quotas, timeouts, input-size caps; Elevation of privilege → authz on every request, sandboxing, least privilege, no injection.
  3. Where data crosses between different trust levels or owners. Threats cluster there because that's where trust assumptions change and where input becomes "trusted".
  4. Any five: least privilege (minimum rights, minimum time); fail-safe defaults (deny by default); defense in depth (no single control trusted); complete mediation (check every access every time); economy of mechanism (keep the security-critical part small); open design (security in the key, not the design); separation of privilege (require two conditions); psychological acceptability (make the secure path the easy path).
  5. Recompute the price/total server-side from stored data and ignore any client-supplied amount. It's primarily Tampering (and an insecure-design flaw).
  6. Mitigate (add a control), eliminate (remove the feature/data), transfer (shift it to someone else, e.g. a managed service), or accept (document it, with sign-off, an owner, and a review date).

End of guide. You started at "what is a byte" and finished able to threat-model a system, run a PKI, harden a host, break and fix an app, and plan a post-quantum migration. Keep the lab. Read primary sources. Break your own things first. Use it to defend.

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