# VAPT readiness pack — Global Health CIO Club

Prepared for the web application security assessment (WASA / VAPT). Items marked **[fill in]** depend on the
hosting environment chosen for staging.

## 1. Auditor pre-requisites

| # | Pre-requisite | Response |
| --- | --- | --- |
| 1 | Application name and auditee organisation | Global Health CIO Club (name to be confirmed with domain). Auditee: Caresoft Foundation; technology partner Caresoft Systems Pvt Ltd |
| 2 | Environment and URL in scope | Staging: `https://[fill in]` (production-identical build). Production is out of scope for active testing |
| 3 | Code freeze | Staging frozen from **[fill in date]** until the final report. Releases go through `deploy/release.sh`; the release log (`shared/storage/logs/release.log`) proves no change during the window |
| 4 | Backup before testing | Taken with `deploy/backup.sh` the day testing starts; restore verified with `deploy/restore.sh <file>` into `cioclub_restore_test` |
| 5 | Payment gateway | Stripe in test mode (`sk_test_...`); test cards only. The built-in fake checkout is disabled when the Stripe driver is `live` |
| 6 | WAF / rate limits / MFA during testing | No WAF in front of the application. Application rate limits and fail2ban stay on; auditor IPs **[fill in]** are allow-listed in fail2ban (`ignoreip`). Admin accounts require TOTP 2FA: the test-account file includes each admin's TOTP secret |
| 7 | Hosting / data centre | **[fill in provider, region]** |
| 8 | Operating system and web server | Ubuntu 24.04 LTS; Nginx (TLS 1.2/1.3, HSTS); PHP-FPM 8.3 with a dedicated pool and user |
| 9 | Database | MariaDB 10.11 / MySQL 8 (utf8mb4). Application account has SELECT/INSERT/UPDATE/DELETE only; schema changes use a separate DBA account |
| 10 | Framework and libraries | Plain PHP 8.3 + PDO (no framework, no Composer). Front end: Bootstrap 5.3.3, Quill 2.0.3 (editor), qrcode-generator 1.4.4, Noto fonts — all self-hosted, no CDN |
| 11 | API / endpoint list | `docs/vapt/endpoints.csv` — 109 endpoints with methods, who may call them and how they are protected. Regenerate with `php bin/endpoint_inventory.php` |
| 12 | Roles and test credentials | Two accounts per role (9 roles): platform super, moderator, editor, ads, support; country admin; member; pending member; advertiser. Created by `php bin/create_test_accounts.php --org="<auditor>"`; strong random passwords (no role@123 patterns); removed after the audit with `--remove` |
| 13 | SPOC | Primary: **[fill in]**; alternate: **[fill in]** |
| 14 | VPN | Not required (public HTTPS application) |
| 15 | Third-party integrations | Amazon SES (email), Meta WhatsApp Cloud API, Stripe, Anthropic Claude API (AI assistant/translation), CareMeet (video, Caresoft-owned). All outbound only, except signed webhooks: `/hooks/ses`, `/hooks/stripe`, `/hooks/caremeet` |

## 2. Architecture in brief

- Single front controller (`public/index.php`); only `public/` is web-served. Config, code, SQL, logs and uploaded vault
  documents live above the web root.
- Roles: platform admin (5 sub-roles), country admin (scoped to countries), member, advertiser. Advertisers are
  blocked from the whole members' area by design (vendor firewall).
- Data at rest: passwords bcrypt; TOTP secrets AES-256-GCM; recovery codes SHA-256; reset/verify tokens stored as
  SHA-256 hashes; backups AES-256 encrypted.

## 3. OWASP Top 10 (2021) — controls and evidence

Evidence = automated checks in `tests/security.php` (79 checks) plus the functional suites (556 checks).

| Risk | Controls | Evidence |
| --- | --- | --- |
| A01 Broken access control | Server-side permission matrix (`can()`), country scoping (`scope_sql`), post visibility rules, own-record checks for advertisers/invoices/campaigns, private circles and TLP posts | IDOR checks: drafts, edit/delete others' posts, admin URLs as member, other advertiser's campaign, invoices, conversations, vault downloads |
| A02 Cryptographic failures | TLS only (HSTS), `__Host-` Secure cookies on HTTPS, bcrypt, AES-256-GCM secrets, hashed tokens, encrypted backups | Secret storage check; cookie flag check; backup tamper test |
| A03 Injection | PDO prepared statements throughout; identifiers never from input; allow-list HTML sanitiser (DOM) for articles; output escaping | SQLi probes (boolean, UNION, time-based) on search, filters and slugs; stored and reflected XSS probes; sanitiser strips iframes/handlers/styles/data: links |
| A04 Insecure design | Vendor firewall, give-to-get, k-anonymity (benchmarks shown only at n ≥ 5), no member data to advertisers, AI sees only what the member can see | Functional suites (Phase 2–3) |
| A05 Security misconfiguration | Strict CSP with per-request nonce (no inline scripts or handlers), X-Frame-Options, nosniff, Permissions-Policy, COOP, no X-Powered-By, PHP hardening ini, dedicated FPM pool with open_basedir and disabled exec functions, uploads never executed | Header and CSP checks; inline-script scan; sensitive paths return 404 |
| A06 Vulnerable components | Minimal dependencies, self-hosted; Quill upgraded to 2.0.3 (1.3.7 had CVE-2021-3163) | Library inventory in item 10 |
| A07 Identification and authentication | Blocked-password list and name/email checks, lockout after 6 failures, per-IP rate limits, TOTP 2FA (mandatory for admins) with replay protection and single-use recovery codes, session rotation on sign-in, idle/absolute timeouts (admins 2 h / 12 h), sign-out everywhere on password change, enumeration-safe sign-up/login/reset | 2FA flow, replay, recovery, lockout, session fixation, password-change invalidation, enumeration checks |
| A08 Software and data integrity | Atomic releases with syntax check and health-checked rollback; signed webhooks with timestamp tolerance and replay ledgers; Stripe amount/currency verification | Stripe/CareMeet webhook tests (bad signature, stale, replay, amount mismatch) |
| A09 Logging and monitoring | Audit log of admin and security events; security log for fail2ban (auth, 2FA, CSRF failures); `/health` endpoint (token-protected detail) | Security-log checks |
| A10 SSRF | Push endpoints restricted to known browser push services; webhooks never fetch arbitrary URLs except the verified SNS certificate host; recordings accepted only from the CareMeet domain | Push allow-list tests (internal metadata IP refused) |

## 4. Accepted residual risks (for the report)

| Item | Reason | Mitigation |
| --- | --- | --- |
| `style-src 'unsafe-inline'` in CSP | Bootstrap components and progress bars set inline styles | Scripts remain nonce-only; style injection cannot execute code |
| `allow_url_fopen = On` | Needed to fetch the Amazon SNS signing certificate | Host strictly validated (`sns.*.amazonaws.com`, `.pem`) before fetching |
| Admin 2FA uses TOTP, not hardware keys | Broadest device support | Recovery codes; account lockout; audit log; WebAuthn can be added later |
| Rate limits are application-level | No WAF in front | Nginx `limit_req` on sign-in routes plus fail2ban; a CDN/WAF (e.g. Cloudflare) is recommended for production |

## 5. Re-test checklist after fixes

1. `php tests/security.php https://<staging>` and the four functional suites pass.
2. `php bin/endpoint_inventory.php` regenerated if routes changed.
3. Remove audit accounts: `php bin/create_test_accounts.php --remove`.
4. Rotate any secret that was shared with the auditor (TOTP of audit accounts disappears with the accounts).
