SecurityDevOps
#01
2026-07-216 min read

Scope the MySQL user first: the newsletter DDL as a security boundary on cPanel

On shared hosting, you don't control the firewall or the infrastructure. What you do control is which privileges the MySQL user holds. Scoping it to the newsletter schema alone made a compromised endpoint's blast radius structurally finite — not as a mitigation layered on top, but as a property of the design.

Read post
Build LogSecurity
#02
2026-07-176 min read

Six PHP classes, no Composer: a cPanel newsletter backend with enumeration-resistant auth

The captainrandom.co.uk newsletter backend runs on a cPanel shared host with zero framework and no Composer. Six small PHP classes, around 600 lines, and an eight-stage subscribe pipeline that tells the caller nothing about whether an email address already exists.

Read post
Build LogPHP
#03
2026-07-155 min read

I hand-wrote the newsletter backend in PHP. The threat model is the code.

The captainrandom newsletter subscription backend is 600 lines of PHP, six classes, no framework, no Composer. Every security decision — rate limits, honeypots, time-traps, token hygiene — lives in the endpoint files, readable end-to-end in one sitting. That's the trade: no ecosystem, but a threat model you can hold in your head.

Read post
SecurityBuild Log
#04
2026-07-136 min read

The confirm link token was in five places before the subscriber clicked it

Building newsletter confirm and unsubscribe handlers revealed a leak surface most developers overlook: tokens passed via GET end up in browser history, Referer headers, and server access logs before any mutation happens. The fix is an interstitial — show a page on GET, mutate only on POST.

Read post
Build LogDevOps
#05
2026-06-125 min read

No Composer, no autoloader: the newsletter backend cPanel rewarded

The deployment target was a cPanel shared host with FTP access. Composer wants SSH or a committed vendor directory. Neither fits cleanly on shared hosting. The alternative was require_once, six PHP classes, and a 190-line schema that doubles as documentation. Six sprints in, the backend is completely understood.

Read post