Part of my notebook. Terse by design. Longer-form thoughts on this topic: Machines Have Identity Problems Too.
Hash functions, and which one for passwords
- MD5: 128-bit, fast (processes 512-bit blocks into a 16x32-bit state). Fine for file integrity checksums, disqualified for security precisely because it’s fast.
- SHA-256: 256-bit, slower, secure; brute force is expensive.
- bcrypt: Blowfish-based, salted by design, with a tunable work factor so the operator can keep raising the attacker’s cost. ~300 ms per hash on purpose.
Password storage (the OWASP short version): never plain text; use a modern
slow hash; generate a random salt per password; store salt and
hash(password + salt); on login, recompute with the stored salt and
compare. The salt defeats rainbow tables; the slowness defeats brute force.
Sessions vs JWTs, and the hybrid everyone lands on
Server-side sessions (Redis/DB-backed, cookie-carried): still everywhere in traditional web apps and admin dashboards, because they give you the things stateless tokens can’t: instant logout, session tracking, device management, revocation. Pair with CSRF protection.
JWTs: self-contained, stateless, verified locally without a network hop; the default for SPAs, mobile, APIs, and microservices. Known sharp edges: revocation needs external state anyway (a Redis denylist), and claims bloat if you treat the token as a database.
The hybrid most large systems converge on: short-lived JWT access token
(~15 min) for API auth + long-lived refresh token (~1-2 weeks) stored
server-side in Redis. Stateless verification on the hot path, revocation and
rotation where you need control. My longer take on aud scoping, key
rotation, and clock skew is in
the blog post.
OAuth2, SSO, SAML, OIDC
OAuth2 lets an app access resources hosted by another app on a user’s behalf: client requests authorization → auth server authenticates and checks scopes → resource owner grants → client receives an authorization code or access token → client presents the token to the resource server. Worth reading how it goes wrong: account takeover on Booking.com via OAuth.
SSO rides on federated identity: identity information shared across trusted, independent systems, so one login opens many apps. The two protocols you’ll meet: SAML (XML-based, the workplace standard) and OpenID Connect (JWT-based, the “Log in with Google” flow). The best technical primer I’ve found: SSOReady’s SAML primer. Also useful: microservice authN/authZ solutions.
mTLS in practice
Regular TLS authenticates the server; mutual TLS has both sides present X.509 certificates, giving identity + encryption at the transport layer.
Use it for: service-to-service traffic (the zero-trust backbone of Istio, Linkerd, Consul meshes), B2B API integrations where certificates beat shared secrets, IoT fleets with provisioned certs, and as a replacement for API keys. Skip it for: browsers and mobile apps (certificate distribution to consumers is misery) and anywhere you need fast per-session revocation (CRLs/OCSP are possible but clunky).
The division of labor: mTLS answers “which workload is calling”; JWTs and sessions answer “which user is logged in and what may they do.” You usually want both.
Implementation sketch:
- A CA (internal: Vault, cfssl; or commercial) issues server and client certs.
- Server config: own cert/key, trust the client CA,
ssl_verify_client on(NGINX). - Client presents its cert and key (
curl --cert client.crt --key client.key ...). - Server verifies the chain, expiry, revocation, and optionally pins CN/SAN to an expected identity.
In production you rarely wire this by hand: service meshes inject Envoy sidecars that handle short-lived certs, rotation, and validation (SPIRE/Istio CA underneath), and API gateways (Kong, Apigee) enforce mTLS for partner traffic, layering JWT/user auth on top.
An InfoSec checklist, earned the hard way
- Audit your third-party jars. Log4j’s JNDI lookup vulnerability (a JNDI URL in a query param leading to remote script execution) had us stripping the lookup classes and publishing a patched internal build to Nexus for every team, fast.
- Passwords salted and hashed (see above), never logged.
- Vulnerability scans in CI (Jenkins/GitLab): dependency scanning on every build, not annually.
- Every database and machine behind real authentication and authorization; no “internal so it’s fine.”
- Service-to-service calls carry identity (JWT or mTLS), not network trust.
- Parameterized queries only; avoid native/string-built SQL entirely.
- Tenant/pod boundaries between client data and shared resources.
- Watch for credentials leaking into logs (STS tokens, AWS keys); scrubbing after the fact is not a strategy.