This document defines the security posture required for the Golden Hour AI prototype and its deployment assets. It is not a certification or proof of production readiness. Real-world use requires a jurisdiction-specific privacy assessment, threat model, penetration test, retention/deletion policy, clinical governance, and operating procedures.
Emergency descriptions, profile facts, audio, location, contact data, tokens, tasks, timelines, and summaries are sensitive. They must remain out of source code, URLs/query strings, analytics, ordinary logs, screenshots, test snapshots, and public issue reports.
flowchart LR
Untrusted["Untrusted browser, text, audio, filenames, uploads"]
Edge["HTTPS + rate limits + size/type validation"]
Auth["Authentication + CSRF + authorization"]
App["Application commands + idempotency"]
Data["PostgreSQL / private blob"]
AI["External AI provider boundary"]
Callbacks["Signed provider webhooks"]
Ops["Azure/GitHub operator boundary"]
Untrusted --> Edge --> Auth --> App --> Data
App -->|"minimum redacted fields"| AI
Callbacks -->|"HMAC, timestamp, replay check"| Edge
Ops -->|"OIDC / managed identity / secret references"| App
Treat browser state, AI output, provider callbacks, filenames, document text, realtime messages, and session IDs as attacker-controlled. A network location, authenticated identity, or model response is not sufficient authorization.
| Class | Examples | Required handling |
|---|---|---|
| Restricted | Auth/refresh/share tokens, signing secrets, provider keys, private documents, raw audio | Never log/cache/client-persist; encrypt in transit/at rest; strict role/resource access; short retention |
| Sensitive emergency | Profile snapshot, allergies/medicines/conditions, observations, location, timeline, summaries | Resource-scoped access; minimal projections; no URL/analytics/snapshot content; audited access |
| Operational metadata | Opaque IDs, status, timestamps, correlation ID, latency/outcome class | Minimize, retain by policy, ensure combinations do not reveal a person |
| Public reviewed content | Product shell, translations, protocol demonstration files | Integrity/version control; safe to PWA-cache; still label prototype/clinical-review status |
No production retention durations are invented here. Define lawful purpose, jurisdiction, retention, deletion, export, backup expiry, legal hold, data residency, and subject-right processes before collecting real data.
- ASP.NET Core Identity owns password hashing, lockout, normalized identifiers, and role membership.
- Access and refresh credentials are delivered in
Secure,HttpOnly,SameSitecookies in shared HTTPS environments; never store JWT or refresh tokens in local/session storage, IndexedDB, Cache Storage, URLs, or JavaScript-readable cookies. - Access lifetime is short. Refresh tokens are random, stored only as a strong hash, rotated on every use, tied to a token family/device record, and revoked on logout, password/security event, or detected reuse.
- Reuse of an old refresh token revokes its family and produces a security audit event. Responses do not reveal whether a user exists.
- Login/register/refresh/recovery endpoints have per-IP and per-account limits. Lockout messages avoid account enumeration.
- Production signing keys are high entropy and supplied through a secret reference. Issuer, audience, algorithm, expiry, clock skew, and key rotation are explicitly validated.
Cookie-authenticated unsafe methods require CSRF defense: a server-issued antiforgery token bound to the session, custom header, and origin/host validation. SameSite alone is defense-in-depth, not the entire control. CORS is denied by default in the same-origin production topology and never combines wildcard origins with credentials.
Authentication never substitutes for resource authorization. Every profile, session, task, timeline, location, summary, invitation, token, document, and hub operation evaluates the authenticated principal or validated anonymous grant against that exact resource and operation.
Roles (User, Caregiver, Emergency participant, Administrator) grant only coarse capability. Session ownership/participation and field-level sharing determine actual access. Administration does not imply routine access to private emergency content.
SignalR connections use the same identity, re-evaluate membership before joining a server-generated group name, and authorize each hub method. Clients cannot provide arbitrary group names or enumerate groups/sessions. Reconnect re-authenticates and refetches state.
- Generate at least 128 bits of randomness with a cryptographic RNG.
- Return the raw token only once in the URL fragment/path designed for the feature; never write it to logs, analytics, referrers, or server storage.
- Store only a keyed or memory-hard/cryptographic hash with session, scope, expiry, revocation, and creation metadata.
- Compare hashes in constant time and return the same response shape for invalid, expired, revoked, or unknown tokens.
- Rate-limit attempts by token/IP/device signals without locking out the emergency-call action.
- Project fields through a server allowlist based on the profile snapshot and sharing preference. Insurance, full address, private documents, raw audio, auth tokens, and complete records are excluded by default.
- Audit access metadata minimally. Anonymous writes are allowlisted, validated, consent-aware, scoped, and idempotent.
Use Referrer-Policy: no-referrer and avoid third-party resources on token-bearing pages. The UI provides explicit revoke and expiry state.
- Validate every DTO at the boundary and again enforce domain invariants; return RFC 7807 errors with correlation ID and no sensitive values.
- Use parameterized EF queries. Never construct SQL, shell commands, file paths, URLs, HTML, or log templates from untrusted strings.
- React escapes text by default; avoid
dangerouslySetInnerHTML. Apply a restrictive Content Security Policy and encode any server-rendered value. - Audio: maximum 30 seconds and a small byte limit, allowlisted MIME/container signatures, filename ignored, generated storage name, malware/content checks where appropriate, bounded decoder/transcriber, and no public blob access.
- Documents (future): reject path traversal, archives/bombs, active content, unsupported extensions/signatures, and prompt instructions. Store outside web root with randomized names and explicit content disposition.
- Normalize/validate Unicode without discarding the original. Protect database/log/UI limits from oversized grapheme sequences and control characters.
- HTTPS only, HSTS outside local development, trusted forwarded-header configuration, secure response headers, and bounded request/body/form/time limits.
- Rate-limit by route risk: strict token/auth/webhook/upload limits and session-aware mutation limits. Return
429with safe retry information. - Critical commands carry an opaque idempotency key scoped to actor/resource/operation. Persist the result atomically; a duplicate returns the original result instead of applying twice.
- Use optimistic concurrency/version checks for task/session transitions and return a conflict that prompts a state refetch.
- A transaction commits durable state and outbox entry before realtime/event delivery. Consumers and provider callbacks are idempotent.
- Emergency call initiated/connected and provider delivered states require explicit user/provider evidence; the application never infers success from a button click or queued request.
Provider-specific endpoints must read a bounded raw body, locate a known provider/secret version, verify an HMAC signature in constant time, validate an absolute timestamp within the configured skew, and atomically reject a previously seen provider event ID/signature digest. Signature verification happens before parsing/trusting fields.
Return quickly after storing a minimal normalized delivery event/outbox job. Do not persist an entire raw payload by default. Invalid signature/timestamp/replay receive an appropriate non-revealing error; valid duplicates return the provider-required idempotent response without replaying state changes.
- OpenAI/provider keys are backend-only secrets and never use
VITE_names. - Send the minimum redacted fields; provider terms, region, training/retention settings, and data processing agreements require review.
- Treat model output as untrusted structured input and apply the controls in AI_SAFETY.md.
- Provider status is
queued,accepted,delivered,failed, orunknownbased only on the provider contract. A local mock is visibly non-production and does not imply external delivery. - Timeouts, bounded retries, circuit breaking, rate limits, and idempotency prevent hanging or duplicate effects.
The service worker precaches hashed shell assets and reviewed public translations/protocols only. It must not cache authenticated API responses, token-bearing bystander pages, audio, private documents, profile/session responses, or error bodies containing user input. Clear user-scoped local data on logout/revocation.
An opt-in offline card stores only explicitly approved minimal fields, is encrypted where platform capabilities permit, shows its age, and can be removed. Queued updates are limited to noncritical idempotent events; emergency calls, token/sharing operations, invites, and provider notifications are never queued/retried.
Use structured allowlisted fields: timestamp, severity, operation name, outcome class, opaque actor/resource ID, correlation/trace ID, duration, dependency name/status, HTTP status, and safe error code. Redact cookies, authorization/signature headers, URLs with tokens, connection strings, keys, emails, phone numbers, location, input/output bodies, filenames, prompt/model text, profile facts, and raw provider payloads.
Audit significant security and state events with UTC time, opaque actor/resource IDs, action, outcome, reason code, and correlation ID. Audit data itself is access-controlled, tamper-evident as operationally feasible, retained by policy, and does not duplicate sensitive content.
Health endpoints expose component status but no secrets, connection details, exception stacks, database names, or private counts. Liveness does not contact dependencies; readiness reports only safe configured/ready/degraded categories.
.envis local-only and ignored..env.examplecontains names/placeholders, not usable shared credentials.- GitHub deployment uses OIDC federation with least-privilege Azure roles; avoid long-lived publish profiles/service-principal passwords.
- GitHub environments require approval for production and contain
POSTGRES_ADMIN_PASSWORD,JWT_SIGNING_KEY,WEBHOOK_SIGNING_SECRET, and Android signing secrets where used. - Azure managed identity is preferred for ACR, Key Vault, Storage, Service Bus, and telemetry. Where an app currently requires a connection string, inject it as a Container App secret and plan migration to identity-based SDK auth.
- No keystore, base64 keystore,
.env, deployment output, certificate, token, or generated signed artifact is committed. - Rotate secrets after suspected exposure; refresh/update the Container App revision and revoke old credentials. Do not print secure parameters from scripts.
| Threat | Primary controls | Verification |
|---|---|---|
| Cross-user/profile/session access | Resource authorization and projection | Integration tests using two identities |
| Share token theft/guessing/tampering | Entropy, hash-only store, expiry/revoke, rate limit, no-referrer | Valid/expired/revoked/tampered/guessing tests |
| Refresh replay/session theft | Secure cookies, hash/rotation/family revoke, CSRF | Reuse/logout/CSRF tests |
| XSS/SQL/path traversal | Output encoding/CSP, parameterization, generated storage names | Adversarial boundary tests |
| Webhook forgery/replay | HMAC, timestamp, nonce/event uniqueness, idempotency | Invalid/replayed/duplicate callback tests |
| Prompt injection/unsafe model output | Isolation, strict schema, forbidden checks, deterministic dispatcher/protocol | Direct/document injection and unsafe-output tests |
| Duplicate/concurrent state mutation | Idempotency, optimistic concurrency, unique constraints/transaction | Duplicate and two-writer tests |
| Sensitive telemetry/cache leak | Allowlisted logging, redaction tests, cache inspection | Captured log/cache assertions |
| Dependency outage | Bounded timeout/fallback, server authority, health classifications | AI/DB/blob/bus/SignalR/provider outage tests |
| Supply-chain compromise | Lockfiles, pinned major runtime, dependency review, code scanning, minimal image | CI restore/build/SBOM/scan evidence before release |
Run the automated negative matrix in TEST_PLAN.md, dependency/secret/container scans, a manual browser storage/cache inspection, cookie/header review, and an authorization matrix before a shared demo. Do not weaken tests to pass a gate.
Report a vulnerability privately to the repository owner with safe reproduction steps and no real medical data. For suspected secret exposure: revoke/rotate first, preserve minimal audit evidence, invalidate affected sessions/share links, assess logs/artifacts/caches, and notify affected parties according to an approved incident process. A production incident contact and disclosure SLA must be defined before launch.