Security Architecture Document

Security Architecture Document

Project: Drop — Fintech Payment App (Remittance + QR Payments) Version: 1.0 Date: 2026-02-23 Author: ALAI Security Team Status: Draft Reviewers: CISO, CTO, DPO Classification: Confidential

Document History

Version Date Author Changes
0.1 2026-02-12 Security Agent (ALAI) Initial security audit
1.0 2026-02-23 Security Architect (ALAI) Architecture documentation

1. Security Architecture Overview

Security Owner: CISO (Alem Bašić / ALAI Holding AS) Last Security Review: 2026-02-12 (full audit); 2026-02-13 (hardening verification) Next Scheduled Review: 2027-02-12 (annual) or after Phase 2 integration (BankID, Open Banking) Compliance Targets: GDPR | PSD2 (Betalingstjenesteloven) | AML/Hvitvaskingsloven | DORA/IKT-forskriften | Finanstilsynet license

Architecture Model: Drop operates a PSD2 pass-through model. Drop never holds customer funds. AISP reads bank balances via Open Banking; PISP initiates payments directly from the user's bank account. Cards are a future feature, gated behind feature flags (all default to false).

Security Posture Summary (post-hardening 2026-02-13):

Defense-in-Depth Overview

Internet
  → WAF (planned — Phase 2 infra hardening)
  → CDN / Edge TLS termination (planned)
  → Load Balancer (TLS 1.3)
  → Application Layer (Next.js — Next.js 16.1.6)
      ├── Rate Limiting (SQLite-backed, persistent)
      ├── Origin/CSRF validation
      ├── JWT auth + session revocation
      ├── RBAC (user / merchant roles)
      ├── Input validation + sanitization
      └── Parameterized SQL queries
  → Database Layer (SQLite — MVP; PostgreSQL planned Phase 2)
      └── Stored: bcrypt(password), session token hashes, masked card tokens

Monitoring: Sentry (error tracking) — SIEM planned Phase 3

2. Authentication Flows

2.1 Current MVP Authentication (Email + Password)

User → POST /api/auth/login {email, password}
     → Rate limit check (10 req/60s per IP — SQLite-backed)
     → SELECT user WHERE email = ?
     → bcrypt.verify(password, hash) [cost factor 12]
     → If valid: generate JWT (HS256, jose ^6.1.3, 24h expiry)
     → INSERT into sessions table (token_hash = SHA-256(token))
     → Set httpOnly cookie (secure:true, sameSite:strict, maxAge:24h)
     → Return 200

Source: src/drop-app/src/lib/auth.ts, src/drop-app/src/lib/middleware.ts

2.2 Session Lifecycle

Step Action Source
Login Session created, token_hash stored auth.ts:56-65
Each request Session checked for revocation middleware.ts:66-74
Logout All user sessions revoked server-side auth/logout/route.ts:5-14
Password change All sessions revoked Planned (Phase 2)

Session table schema:

Column Type Purpose
id TEXT PK ses_<hex16> format
user_id TEXT FK References users.id
token_hash TEXT SHA-256 of JWT token
expires_at TEXT Expiration timestamp
revoked INTEGER 0 = active, 1 = revoked

2.3 BankID OIDC Authentication (Phase 2 — Planned)

BankID is not yet integrated in the MVP codebase. Required for PSD2 SCA compliance before any live transactions. Planned integration:

User → Drop App → BankID OIDC (nivå høyt — eIDAS Level High)
     → BankID returns: name, fødselsnummer (national ID), verified identity
     → Drop validates: age >= 18 (from fødselsnummer), Norwegian residency
     → Dynamic linking for payment authorization (amount + payee bound to auth)

Regulatory requirement: PSD2 (Betalingstjenesteloven §§ 4-28, 4-29) requires SCA with two of three factors. BankID covers possession + knowledge. No live transactions without this.

Integration partner: TBD — BankID Norge AS (DPA required)

2.4 KYC Flow (Phase 2 — Planned via Sumsub)

Current state: Mock KYC with auto-approve (kyc_status field in users table). Production will use Sumsub:

User → Sumsub SDK → Document scan + liveness check
     → Sumsub webhook → Drop backend
     → Update users.kyc_status = 'approved'/'rejected'
     → Required for: remittance transactions

Source: legal/dpa-sumsub.md, legal/dpia-vurdering.md


3. Authorization Model

Model: RBAC (Role-Based Access Control) with resource-level user scoping

3.1 Roles

Role Description Access
user Standard registered user Own data only (transactions, recipients, notifications, settings)
merchant Merchant with QR payment dashboard Own data + merchant dashboard (/api/merchant/*)

KYC Status (enforced gate for financial operations):

Status Meaning Effect
pending Default on registration Cannot initiate remittance
approved KYC completed Full access to financial features
rejected KYC failed or blocked Blocked from all financial operations

3.2 Resource-Level Access Control (IDOR Prevention)

All data access queries include AND user_id = ? to scope data to the authenticated user. Applied to:

Merchant endpoints verify both merchant role and ownership.

Source: src/drop-app/src/app/api/ — all route handlers

3.3 Permission Summary

Resource user merchant Admin (TBD)
Own profile CRUD CRUD
Own transactions Read Read
Own recipients CRUD CRUD
Merchant dashboard Read
All users Admin only
AML reports Compliance only

4. Data Encryption

4.1 Encryption at Rest

Data Method Status
Database (SQLite) OS-level (filesystem) MVP — migrate to PostgreSQL Phase 2
Passwords bcrypt, cost factor 12 Implemented (bcryptjs ^3.0.3)
Session tokens SHA-256 hash stored (not plaintext) Implemented
JWT secret JWT_SECRET env var (fatal if missing in prod) Implemented
Fødselsnummer (national ID) AES-256-GCM application layer + HSM key Planned Phase 2
Card data Only last_four + token_ref stored (PAN/CVV never stored) Implemented (fix C1)
Bank account numbers Only last 4 digits in API responses Implemented

Note: Field-level encryption for PII (fødselsnummer) requires HSM-backed key management (planned Phase 2 with AWS KMS).

4.2 Encryption in Transit

Connection Protocol Status
User → Drop API HTTPS / TLS 1.3 Production requirement
Drop → BankID HTTPS / TLS 1.3 Phase 2
Drop → Sumsub HTTPS / TLS 1.3 Phase 2
Drop → Neonomics (PSD2) HTTPS / TLS 1.3 Phase 2
Drop → Swan HTTPS / TLS 1.3 Phase 2
Internal (service-to-service) mTLS Phase 3 (when microservices introduced)

Source: src/drop-app/src/lib/auth.ts:48-54

Property Value Purpose
httpOnly true Prevents JavaScript access (XSS mitigation)
secure true (production) HTTPS-only transport
sameSite "strict" CSRF prevention
maxAge 86400 (24h) Session lifetime
path "/" Full site scope

5. Network Security

5.1 Current MVP Network Architecture

Internet → Next.js App (port 3000) → SQLite DB (local file)

Phase 2 target:

Internet
  → Cloudflare (DDoS + WAF)
  → AWS Load Balancer (TLS termination)
  → Private Subnet: Next.js App (ECS/Fargate)
  → Private Data Subnet: PostgreSQL (RDS)
  → External APIs: BankID, Sumsub, Neonomics, Swan (all HTTPS)

5.2 Security Groups (Phase 2 planned)

Source Destination Port Action
Internet Load Balancer 443 ALLOW
Internet Any 80 REDIRECT → 443
Load Balancer App servers 3000 ALLOW
App servers PostgreSQL 5432 ALLOW
App servers External APIs 443 ALLOW (allowlist)
Any Data Subnet Any DENY (default)

5.3 Rate Limiting

Source: src/drop-app/src/lib/middleware.ts:6-31

Endpoint Type Limit Window Implementation
Auth routes (login, register) 10 req 60 seconds SQLite-backed (persistent across restarts)
Transaction routes (remittance, qr-payment) 10 req 60 seconds SQLite-backed
Rate routes (/api/rates) 120 req 60 seconds SQLite-backed

Rate limit table: rate_limits — per-IP tracking via X-Forwarded-For header.

Known gap: X-Forwarded-For can be spoofed. Fix requires trusted proxy validation (planned Phase 2 with load balancer).


6. API Security

6.1 Input Validation

Source: src/drop-app/src/lib/middleware/validation.ts:149-203

All API inputs validated at controller level:

Amount limits:

Endpoint Min Max
Remittance 100 NOK 50,000 NOK
QR Payment 1 NOK 100,000 NOK

6.2 SQL Injection Prevention

All 24 API endpoints use parameterized queries exclusively (? placeholders). No string concatenation in SQL.

Source: src/drop-app/src/app/api/ — all route handlers; verified in security audit 2026-02-12.

6.3 CSRF Protection

Origin header validation on all authenticated requests:

Source: src/drop-app/src/lib/middleware.ts:44-55

6.4 Content Security Policy

Source: src/drop-app/next.config.ts:6-46

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'unsafe-inline' 'unsafe-eval';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self';
  frame-ancestors 'none';

Known limitation (Medium severity): unsafe-inline and unsafe-eval required for Next.js dev mode. Production should use nonce-based CSP. Planned Phase 2.


7. OWASP Top 10 Mitigation Matrix

OWASP Risk Mitigation Implementation Status
A01: Broken Access Control RBAC + AND user_id = ? on all queries All 24 API endpoints Implemented
A02: Cryptographic Failures bcrypt cost 12, JWT HS256, HTTPS TLS 1.3, httpOnly cookies auth.ts, utils-server.ts, next.config.ts Implemented
A03: Injection Parameterized queries exclusively (no string SQL concat) All API routes Implemented
A04: Insecure Design Pass-through model (no fund custody), feature flags for cards Architecture, feature-flags.ts Implemented
A05: Security Misconfiguration Security headers, demo credentials gated (NODE_ENV !== 'production') next.config.ts, db.ts Implemented
A06: Vulnerable Components All deps recent, no known CVEs (audit 2026-02-12) package.json Implemented
A07: Auth Failures bcrypt hashing, session revocation, rate limiting, no SHA-256 legacy auth.ts, middleware.ts, utils-server.ts Implemented
A08: Software Integrity TBD — signed commits planned Phase 3 Planned
A09: Logging Failures Sentry (error tracking) — audit log table planned Phase 3 MVP: Sentry Partial
A10: SSRF Pass-through model limits outbound surface; allowlist planned Phase 2 Architecture Partial

8. Security Headers Checklist

Source: src/drop-app/next.config.ts

Header Value Status
Strict-Transport-Security max-age=63072000; includeSubDomains; preload Implemented (fix M2)
Content-Security-Policy See §6.4 Partial — unsafe-inline remaining
X-Content-Type-Options nosniff Implemented
X-Frame-Options DENY Implemented
Referrer-Policy strict-origin-when-cross-origin Implemented
Permissions-Policy camera=(self), microphone=(), geolocation=(self) Implemented
Cache-Control (auth responses) no-store TBD — Phase 2

9. Dependency Vulnerability Management

Last dependency review: 2026-02-12 (security audit)

Package Version Risk Assessment
jose ^6.1.3 Low — Well-maintained JWT library
bcryptjs ^3.0.3 Low — Pure JS bcrypt
better-sqlite3 ^12.6.2 Low — Parameterized queries
next 16.1.6 Low — Recent version
react 19.2.3 Low — Latest major
radix-ui ^1.4.3 Low — UI components only

Remediation SLAs:

Severity SLA
Critical (CVSS ≥ 9.0) 24 hours
High (CVSS 7.0-8.9) 7 days
Medium (CVSS 4.0-6.9) 30 days
Low (CVSS < 4.0) 90 days

Planned: Dependabot + Snyk integration in CI/CD pipeline (Phase 3).


10. Security Logging & Audit Trail

Current (MVP): Sentry for error tracking. No structured audit log table.

Planned (Phase 3): Audit log table + SIEM integration.

Event Planned Logging Alert?
Login success user_id, ip, user_agent, timestamp No
Login failure ip, email_hash, attempt_count, timestamp Yes (> 5 failures)
Session revocation user_id, timestamp, reason Yes
Transaction initiated user_id, amount, currency, corridor, timestamp No
KYC status change user_id, old_status, new_status, timestamp Yes
AML flag triggered user_id, rule, transaction_id, timestamp Yes
Password change user_id, ip, timestamp Yes

AML transaction monitoring thresholds (from legal/hvitvaskingsrutiner.md):


11. Integration Security

Third-Party Services (Phase 2)

Service Purpose Auth Method Data Shared DPA
BankID Norge AS SCA + Identity verification OIDC Name, fødselsnummer Required
Sumsub KYC/AML document verification API key + webhook HMAC ID documents, liveness data Signed (legal/dpa-sumsub.md)
Swan Banking / payment rails OAuth 2.0 Transaction data Signed (legal/dpa-swan.md)
Neonomics PSD2 AISP/PISP (Open Banking) OAuth 2.0 (PSD2) Bank account data, payment initiation Required
Sentry Error monitoring DSN Stack traces, user IDs Signed (legal/dpa-sentry.md)
AWS Infrastructure IAM roles + KMS Infrastructure only AWS DPA

Approval

Role Name Date Signature
Author ALAI Security Team 2026-02-23
CISO / Security Lead TBD — requires appointment
DPO TBD — requires appointment
CTO Alem Bašić

Revision #9
Created 2026-02-23 12:05:09 UTC by John
Updated 2026-05-31 20:03:11 UTC by John