Docs · Last updated 2026-05-26

Security Model

Below is what Readyline does on its side and what stays on yours. The split matters for your SSP narrative and for what gets flagged at a C3PAO assessment.

Encryption

In transit

All HTTP traffic is TLS 1.2+. HTTP requests to apex and app subdomains redirect to HTTPS at the nginx layer. TLS termination uses a Let's Encrypt certificate, auto-renewed via certbot. Ciphers are restricted to modern AEAD suites (e.g., ECDHE-RSA-AES128-GCM-SHA256, ECDHE-RSA-AES256-GCM-SHA384); CBC and RC4 are disabled.

Between app server and database, traffic flows over a private network. For on-premises deployments where this is not guaranteed, the included nginx config can be configured to also TLS-wrap the DB connection (Postgres or MySQL).

At rest

The MySQL data directory is encrypted via the InnoDB-native encryption (AES-256), with the master key stored in the OS keyring. Uploaded files live under storage/app/{tenant}/ and are protected by filesystem-level encryption (LUKS for on-prem, AWS-EBS-encryption for hosted SaaS). Database backups produced by php artisan tenants:backup (scheduled daily) are gzip-compressed dumps of the central DB plus every active tenant database, optionally AES-256-CBC encrypted via the --encrypt flag. Off-host replication is a separate operational step.

FIPS posture

Readyline runs cleanly on OpenSSL configured in FIPS-140-3 mode on supported platforms (RHEL 9, Ubuntu 22.04 with FIPS overlay). For DoD subcontractors that need to claim FIPS-validated cryptography under NIST 800-171 §3.13.11, this is the path. We do not bundle our own crypto. All crypto is provided by the underlying OS, which keeps the FIPS validation surface on the OS vendor.

Note: FIPS mode disables a handful of algorithms (notably MD5 outside of HMAC). The Laravel cache key hashing has been moved to SHA-256 to remain FIPS-clean. No application changes are required by the operator.

Authentication

Password policy

Passwords are stored as bcrypt hashes with a work factor of 12. The minimum policy is 12 characters with at least one upper, one lower, one digit, and the password is checked against a known-breach corpus; reuse of the last 5 passwords is blocked. Failed login attempts trigger a 15-minute lockout after 5 failures and write to the audit log.

MFA (TOTP)

Time-based one-time password (RFC 6238, 6-digit) is available for every user. Backup codes are issued at enrollment and one-time-use. MFA is mandatory for the c1adm founder console and for any tenant user with the admin role. Tenant admins can require it for everyone via a single toggle.

Session handling

Sessions are server-side via Laravel's database session driver. Cookies are HTTP-only, Secure, SameSite=Lax. Default session lifetime is 120 minutes of inactivity (configurable per tenant). Logout invalidates the server-side session record so a leaked cookie can be immediately revoked. We do NOT use JWTs in the cookie. The cookie carries only an opaque session identifier.

Audit logging

Every authentication event, every privileged action, and every state change to compliance artifacts (controls, POAMs, policies) is written to a central audit_log table with: who (user_id), when (timestamp + IP), what (event type + entity), and a JSON delta of the change. Logs are append-only at the application layer (no UPDATE or DELETE handlers); a tenant admin can READ but cannot MODIFY.

Retention default is 7 years to satisfy DFARS 252.204-7012 record-keeping. Rows older than 12 months are exported monthly to compressed cold archives via php artisan audit:archive; originals stay append-only within the 7-year window. Audit events can also be forwarded to an external SIEM (CEF, LEEF, syslog or JSON, with native Splunk, Microsoft Sentinel and Datadog connectors) on a scheduled push.

Role-Based Access Control

Out of the box we ship 4 roles:

  • Admin: full tenant access, user management, billing.
  • Compliance Lead: controls, POAMs, policies, SSP. Cannot manage users or billing.
  • Contributor: can update assigned controls/POAMs, upload evidence. Read-only elsewhere.
  • Viewer: read-only access to the dashboard, SSP, POAM.

Custom roles + per-resource permission grants are available on Level 2 and above. Every permission check is centralized in the PermissionControlled trait so authorization cannot be skipped by mistake.

Secret rotation

Application secrets (APP_KEY, database credentials, SMTP credentials, Stripe and Anthropic API keys) live in the .env file owned by the deploy user with 0640 permissions. Secret rotation (database passwords, APP_KEY regeneration with re-encryption of encrypted columns, and session invalidation) is a documented manual operations procedure run during a maintenance window; we recommend a 90-day cadence. Automated tooling for this is on the roadmap.

Stripe and Anthropic API keys are rotated manually via the c1adm Founder Console. The key rotation panel will be available in a future release; today, keys are edited in .env and PHP-FPM reloaded.

Hardening

  • nginx is configured with HSTS (Strict-Transport-Security: max-age=63072000; includeSubDomains; preload), X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin.
  • The founder console (c1adm) additionally sends X-Robots-Tag: noindex, nofollow, noarchive.
  • CSRF protection is on every form (Laravel's default).
  • SQL queries use parameterized statements via Eloquent / query builder; no raw concatenation.
  • File uploads are size-limited and MIME-validated server-side.
  • Login throttling is per-IP and per-username, layered.
  • PHP-FPM is process-isolated from nginx; the database connection user has the minimum grants needed (no CREATE, no DROP, no GRANT).

Reporting a vulnerability

Email security@readylinegrc.com with reproduction steps. We acknowledge within 1 business day. Coordinated disclosure preferred. Please give us 30 days to ship a fix before public disclosure. We do not currently offer a bug bounty but we will publicly credit researchers on this page.