Security controls

Technical and organisational measures grouped by category - identity & access, network, data protection, audit, resilience, software supply chain.

This page lists every control we rely on to satisfy GDPR Art. 32, NHS DSPT, and DCB0129. Each group ends with a pointer to the evidence location in the repository.

Identity and access

  • Authentication - JWT-based with 24-hour access token TTL + 7-day refresh token TTL. Refresh rotation: using a refresh token invalidates it.
  • Session revocation - Logout writes the token ID to a Redis denylist. Subsequent requests with that token are rejected.
  • Password policy - 14+ characters, must contain uppercase, lowercase, digit, and special character. Bcrypt cost factor 12.
  • Account lockout - 5 failed logins in 15 minutes triggers a 15-minute lockout.
  • MFA - TOTP (RFC 6238) available for all users, enforced for admin and super_admin roles.
  • RBAC - Every endpoint declares the permission it requires. Permissions are assigned per-clinic.
  • Row-level security - PostgreSQL RLS enforces clinic isolation at the database level. Even if application code has a bug, the database refuses to return another clinic's data.

Evidence: backend/internal/auth/, backend/internal/middleware/auth.go, migration 000064.

Network and infrastructure

  • All resources in Azure UK South, single VNet hihuman-vnet
  • PostgreSQL and Redis have public network access disabled - reachable only via private endpoints inside the VNet
  • Container App runs inside the VNet with managed identity
  • Azure Front Door with OWASP 3.2 managed ruleset + Bot Manager + rate limiting (1000 req / 5 min per IP)
  • TLS 1.2 minimum enforced on every endpoint
  • Distroless container image: no shell, no package manager, cannot be exec'd into
  • Weekly Trivy vulnerability scans on the container image with HIGH / CRITICAL thresholds

Evidence: infra/modules/network.bicep, infra/modules/postgres.bicep, infra/modules/frontdoor.bicep.

Data protection

  • AES-256-GCM encryption for PII fields at rest (names, NHS numbers, phones, addresses)
  • Full-disk encryption on Azure-managed disks
  • TLS 1.2+ in transit everywhere
  • PII regex scrubber on all outbound LLM and webhook traffic
  • HMAC-SHA256 hashing for phone numbers in audit logs (reversible only with the HMAC key)
  • Key rotation: AES key and JWT secret rotatable at any time without downtime

Evidence: backend/pkg/crypto/, backend/pkg/pii/, backend/internal/keyrotation/.

Audit and monitoring

  • Every authenticated request creates an audit record with user ID, clinic ID, method, path, IP, timestamp
  • Every mutating clinical action (create, update, delete on patients, appointments, consultations, prescriptions) is explicitly audited with the resource ID and change summary
  • Audit logs exported daily to Azure immutable blob storage with a SHA-256 hash chain (each record includes a hash of the previous record)
  • 7-year retention on all audit data
  • Application telemetry to Azure Application Insights (trace IDs, exceptions, dependency tracking)
  • Log Analytics queries for anomaly detection (unusual access patterns, failed login spikes)

Evidence: backend/internal/auditlog/, backend/internal/middleware/audit.go.

Resilience and recovery

  • PostgreSQL: 35-day point-in-time recovery with geo-redundant backups (stored in UK West)
  • Daily automated backup verification
  • Zone-redundant high availability on production Postgres
  • Container App auto-scales between 1 and 3 replicas based on HTTP concurrency
  • Graceful shutdown with 30-second request drain window
  • Circuit breakers on every external HTTP client prevent cascading failures

Evidence: backend/internal/backupverify/, backend/pkg/circuitbreaker/.

Software supply chain

  • Dependency vulnerability scanning on every commit (govulncheck, gosec, staticcheck)
  • Container image vulnerability scanning (Trivy, HIGH / CRITICAL fail the build)
  • SBOM (Software Bill of Materials) generated in SPDX format for every release
  • Signed commits enforced on main branch
  • Branch protection requires PR review

Evidence: .github/workflows/ci.yml.