Security hardening: add SECURITY.md + fix three MEDIUM findingsVerified

Adds a SECURITY.md tailored to Usher (pre-1.0 OpenID Provider library):
supported versions, private reporting, and an explicit in-scope /
integrator-responsibility boundary.

Fixes three MEDIUM issues surfaced by an in-session security triage:

* Login-throttle bypass (CWE-348/CWE-770): handle-authorize keyed the
  per-IP rate limit on hunchentoot:real-remote-addr, which returns the
  client-supplied X-Forwarded-For header. With in-process TLS the header
  is spoofable, so an attacker could rotate it per request to evade the
  throttle and its Argon2i CPU/memory cap. Add :trust-forwarded-for
  (default off) and a client-ip helper that uses the real socket peer
  unless a trusted proxy is configured.

* Signing keys at rest (CWE-312/CWE-522): save-signing-keys wrote the
  RSA private key as plaintext JSON with default (world-readable) perms,
  contradicting the design's key-at-rest requirement. Always create the
  file mode 0600, and when :key-encryption-key is set (passphrase/octets
  from env/KMS, never the DB) encrypt the material with AES-256-GCM,
  key derived via PBKDF2-SHA256. Plaintext files still load; enabling a
  KEK re-encrypts on next save.

* Reset/verify tokens never expired (CWE-640/CWE-613): password-reset and
  email-verification tokens had no expiry, so a leaked link stayed valid
  indefinitely. Embed an expiry in the stored credential value (no store
  schema change) via issue-cred-token/consume-cred-token; add
  :pw-reset-ttl (1h) and :email-verify-ttl (24h). Secrets remain stored
  hashed and compared in constant time; single-use semantics preserved.

Full build and the 199-check test suite pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Anthony Green2026-07-01 15:38:20 -0400 88db05c6a6bf78f24f367501b7f98c5373020dae
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>