security

JWT access + refresh token rotation (conceptual)

A single long-lived JWT is a liability: if it leaks, it’s valid until it expires and revocation is hard. I use short-lived access tokens and longer-lived refresh tokens, and I rotate refresh tokens on every use. Rotation means that if an attacker stea

Turbo Streams + authorization: signed per-user stream name

Never subscribe clients to guessable user-specific streams. Use signed_stream_name so a user can only subscribe to their own broadcasts. This is essential when streaming private notifications.

Safe Raw SQL with exec_query + Binds

Sometimes raw SQL is the cleanest approach—just keep it safe. Use exec_query with bind params instead of interpolating values. You get both safety and correctness with types.

ActiveRecord Encryption for PII Fields

Rails’ built-in encryption makes it easier to protect PII at rest. Use it for fields like phone numbers or external tokens. Combine with deterministic encryption when you need lookup-by-value.

Safe markdown rendering (remark + rehype)

Markdown is the sweet spot for user-generated content: expressive enough, but not a full HTML editor. The danger is letting raw HTML slip through. I use remark to parse markdown, then rehype to render HTML, and I disable raw HTML unless I have a sanit

GraphQL persisted queries (hash allowlist)

GraphQL endpoints can be abused with huge queries that are expensive to parse and execute. Persisted queries let clients send a hash (e.g. sha256:...) instead of the full query, and the server only executes queries it recognizes. This reduces payload

CSRF protection with double-submit cookie

Session-based apps still need CSRF protection even when the API is ‘JSON’. I like the double-submit cookie approach: set a CSRF token cookie, require the client to echo it in x-csrf-token, and verify they match. The reason I prefer this is that it doe

OAuth PKCE flow (high level helper)

OAuth flows are a minefield, and PKCE is the safe default for public clients. I generate a verifier, derive a challenge, store the verifier in a short-lived session, and then exchange the authorization code for tokens. The key detail is treating the v

Security headers with helmet (baseline hardening)

Most security issues aren’t exotic—they’re missing headers and unsafe defaults. helmet gives a sensible baseline: headers that reduce clickjacking risk, tighten content-type sniffing, and improve general browser hardening. I still configure CSP explic

Content Security Policy (CSP) Starter

CSP is a strong defense-in-depth measure for XSS. Start with report-only to learn what breaks, then enforce. Keep it explicit and include nonces for inline scripts when needed.

Webhook signature verification (timing-safe compare)

For webhooks, I assume the internet is hostile by default. I don’t trust that a request ‘looks like’ it came from Stripe/GitHub/etc; I verify the signature over the raw request body and use crypto.timingSafeEqual to avoid leaking information via timin

Sanitize user HTML safely (DOMPurify + JSDOM)

Letting users paste rich content is a product requirement that can become a security nightmare. I never try to write my own sanitizer. Instead, I run HTML through DOMPurify using JSDOM on the server and keep the allowlist small (minimal tags + attribu