Security — EIAAW Workforce
EIAAW Workforce
Security

Tenant data walled off at the database.

Most SaaS multi-tenancy is enforced in the application — one forgotten `where tenant_id = ?` and a bug becomes a breach. EIAAW Workforce enforces isolation in Postgres Row-Level Security, at the driver level. Controllers can't forget what the database enforces.

Postgres RLS enforced PDPA-aligned HMAC audit chain MFA & session rotation
Architecture · Layered enforcement
L1 · Edge Cloudflare + Railway TLS HTTPS enforced, HSTS preloaded, WAF rules on signup & webhooks. Active
L2 · App Laravel middleware stack SecurityHeaders + CSP + EnforceSingleSession + SecurityAuditMiddleware. Active
L3 · Tenant ResolveTenant middleware Binds current tenant from subdomain → SET LOCAL app.tenant_id on the Postgres connection. Active
L4 · Database Postgres ROW-LEVEL SECURITY (FORCE) Every tenant-tagged table rejects queries that don't match the session's tenant_id. Fails CLOSED on unset. Enforced
L5 · CI tenancy:check-rls + tenancy:test-leakage Boot check: the DB role must NOT have BYPASSRLS. Integration test: 22 cross-tenant leakage assertions on every deploy. Passing
Audit log · HMAC-chained
Sample · tenant #1 · last 4 events
# tamper-evident: each row's HMAC chains the previous row's HMAC.
# flipping any historical row invalidates every row after it.

{
  "id": 0x4F9A,
  "at": "2026-04-23T14:22:08+08:00",
  "actor": "user:142 (Hanna Tan, Finance Manager)",
  "action": "claim.approve",
  "target": "claim:CLM-0412 (RM 342)",
  "ip": "203.82.xx.xx",
  "ua_hash": "a1e8...c402",
  "prev_hmac": "e7f2...91ab",
  "hmac": "b4c1...7d28"
}
{
  "id": 0x4F9B,
  "at": "2026-04-23T14:22:11+08:00",
  "actor": "system",
  "action": "ledger.post (auto)",
  "target": "journal_entry:JE-8810",
  "source": "claim:CLM-0412",
  "prev_hmac": "b4c1...7d28",
  "hmac": "c302...4fde"
}
{
  "id": 0x4F9C,
  "at": "2026-04-23T14:25:52+08:00",
  "actor": "user:88 (Aisha Rahman)",
  "action": "ai.query",
  "prompt_summary": "Who is OOO next week?",
  "tokens": 482,
  "cost_myr": 0.0031,
  "prev_hmac": "c302...4fde",
  "hmac": "d5ea...0b11"
}
Principle 01

Fail closed

If the tenant isn't set, the database rejects the query. There is no fallback that "helpfully" returns all rows.

Principle 02

Least privilege

The DB role used by the app is NOT a superuser and does NOT have BYPASSRLS. Boot check refuses to start if this changes.

Principle 03

Tamper-evident

Every security-relevant action (auth, approval, AI query, export) is HMAC-chained so historical forgery is detectable.

Compliance roadmap.

We list what's live, what's in flight, and what's planned — no "bank-grade security" marketing language.

PDPA alignment Live

Data-subject access, correction, and deletion request flows. Data residency opt-in for MY. DPO contact published.

Encryption at rest & in transit Live

TLS 1.3 enforced. Postgres AES-256 disk encryption. File uploads encrypted on private disk. Secrets in Railway's encrypted env vars.

HMAC audit log integrity Live

Daily `log:verify-integrity` command walks the HMAC chain; any tamper invalidates the run and notifies the ops channel.

SOC 2 Type I readiness Q3 2026

Controls documented against AICPA Trust Service Criteria (Security, Availability, Confidentiality). Third-party audit scheduled.

SAML 2.0 + OIDC SSO Q3 2026

Enterprise tier. IdP-initiated and SP-initiated flows. SCIM 2.0 provisioning ships alongside.

Penetration test (external) Q4 2026

External pentest by an accredited firm. Report summary published; findings treated per severity with SLAs.

Security questions? Talk to a human.

DPAs, data-residency addenda, pentest summaries, and security-questionnaire responses — we answer before the contract.

Email security@