← BlogHardening My Personal Site: Sanitization, Cookies and JWT

Hardening My Personal Site: Sanitization, Cookies and JWT

June 10, 2026

It all started when I noticed an old version of my site had a known vulnerability. While closing it, instead of just patching, I designed security from the ground up. Here are the four layers I used.

1. HTML sanitization

The blog editor produces rich HTML. Saving it as-is means someone could inject <script> into the content. On the server I use Rust-based nh3 to let through only the tags I allow:

nh3.clean(dirty,
  tags={"p","h2","ul","li","a","img","pre","code", ...},
  url_schemes={"http","https","mailto"},
  link_rel="noopener noreferrer nofollow")

SVG is intentionally off the list — since scripts can be embedded inside it, uploads reject SVG too.

2. Passwords: never plaintext

The admin password is stored in the database with bcrypt. At login the plain password is compared against the hash; the password itself is never stored anywhere.

3. httpOnly cookies and JWT

The session token lives in an encrypted cookie JavaScript can't read. I covered this in detail in the BFF post. In short: the token never touches browser code.

4. Security headers

As a final layer I added defensive headers to every response:

  • X-Frame-Options: DENY — against clickjacking.
  • X-Content-Type-Options: nosniff — blocks MIME sniffing.
  • Referrer-Policy and Permissions-Policy — shrink the leak surface.
Security isn't a single feature; it's many small decisions stacked on top of each other. None is enough alone; together they form a solid wall.

Result

I also updated the dependencies and finished with zero vulnerabilities after a scan. Even for a personal site, building security in from the start is far more comfortable than patching it later.