Technology · Discipline

Application security hardening and threat modelling

I review my own public endpoints, fix findings in severity order with explicit probes, and re-run the threat model when a fix breaks the world.

Public OAuth authorization serverNGINX and systemdPython security library
Where the control sits
registration: anyoneconsent: I approve by name
Registered is not authorised
  • 3findings from the hardening pass
  • 7 probeswritten beside the fixes
  • 9,000lines of Python in the security-module library

The OAuth registration lock I reverted within a day is the worked example. I treat security as a threat-model question: I close holes quickly, and I reverse my own fix when the facts show it sits at the wrong layer.

A hardening pass, triaged by severity

Auth and access plane has an OAuth authorization server that is public on purpose: commercial AI assistants self-register as clients and connect to my private tool estate through it. Its traffic analytics showed me exposure, so I ran a hardening pass.

I triaged the findings by severity, applied the smallest fix for each, and paired each fix with a probe, so every fix has a test that shows it doing its job. The high finding was anonymous client registration with attacker-chosen redirect addresses. I closed it with a fail-closed admin gate on registration, a constant-time comparison and a secret held outside the repository.

Findings by severity
HighundoneMediumkeptLowkept
Medium: interactive API documentation, turned off. Low: health endpoint detail, cut down. Bar lengths are illustrative.

The fix that locked everyone out

The next day the gate locked out every assistant whose cached client identity had expired. I went back to the threat model. A client registered by an attacker still gets no authorization code unless I log in and approve it on the consent screen, so the gate added friction and no protection. The control belongs at the step every grant has to pass: the consent screen is where a person sees exactly which client is asking for what, and registration is bookkeeping.

I bypassed the gate with a one-line, reversible change and wrote the reasoning beside it, so the decision can be revisited, and lengthened the pending-client lifetime. A richer registration design, with redirect allow-listing and client-class trust signals, is written up and was not rushed in under pressure. The whole sequence is told in The hardening fix that locked everyone out.

Pending-client lifetime
Before1 hourAfter30 days
Some assistants cache their client identity indefinitely.

A security change that locks out every legitimate client is a failed change, and I reverse it on the facts even when it looked like hardening.

Defence at the right layer

Beside the server I built a security-module library in Python, and I kept it off the request path.

request
123
library

1 NGINX front: TLS 1.2 and 1.3, HSTS, a rate-limit zone per endpoint · 2 sandboxed systemd unit · 3 the consent step every grant must pass

In the library: HMAC device fingerprints, layered rate limiting, request threat validation, a hash-chained audit log and rules with isolation-forest scoring.