Security & Trust

Bank-Grade Security, By Default

BankerPipeline was built with financial institution security requirements from day one — TOTP 2FA (RFC 6238), SSO/SAML 2.0, OIDC, AES-256-GCM field-level encryption for all PII and HMDA demographic data, role-based access control, GLBA Safeguards alignment, a unified audit log with examiner quick-views, and concentration limit monitoring. Every control is documented for your IT security and compliance review.

Authentication & Access

Strong Authentication & Session Controls

Multi-layered protection from login through session expiry — including SSO/SAML 2.0 and OIDC for institutional identity providers, TOTP-based 2FA, brute-force lockouts, JWT algorithm hardening, and strict role-enforcement middleware.

🏢

SSO / SAML 2.0 & OIDC

Organizations can authenticate through their existing identity provider using SAML 2.0 or OpenID Connect (OIDC). Banker credentials are managed centrally — provisioning, deprovisioning, and MFA policy stay within your IdP.

  • SAML 2.0 SP-initiated and IdP-initiated flows
  • OIDC authorization code flow
  • SAML assertion attributes mapped to internal roles
  • SAML error handler HTML-escaped — XSS in error messages prevented
  • SSO tokens validated via JSON.stringify() — no injection vector in assertion body
  • Compatible with Okta, Azure AD, Google Workspace, OneLogin
🔐

Two-Factor Authentication (2FA)

TOTP-based 2FA via Google Authenticator, Authy, or 1Password. Admins can enforce 2FA org-wide; individual user resets are handled through the admin console. MFA enrollment itself is rate-limited to prevent brute-force bypass during setup.

  • TOTP standard: RFC 6238 — Google Authenticator, Authy, 1Password
  • 8 backup/recovery codes per account
  • "Remember this device" for 30 days
  • Admin can enforce 2FA org-wide
  • Admin can reset individual user 2FA
  • MFA verify endpoint: 5 attempts / 15 min (brute-force on verification)
  • MFA setup endpoint independently rate-limited (brute-force on enrollment)
  • Audit log: MFA enabled, disabled, login, reset
🛡️

Brute Force Protection

Account lockout after 5 failed login attempts with a 15-minute cooldown. Progressive delays on repeated failures. Error messages don't reveal whether the account exists.

  • 5 failed attempts triggers 15-minute lockout
  • Progressive delay on repeated failures
  • Generic error messages (no account enumeration)
  • Constant-time response regardless of account validity
  • DB-tracked lockout state, per-email enforcement
🔑

Password Policy

Minimum 8 characters with mandatory uppercase, lowercase, number, and special character. Enforced at registration and on every password change. Password strength indicator shown in real time.

  • Min 8 chars, upper, lower, digit, special
  • Enforced on registration and password change
  • Real-time strength indicator (4-bar display)
  • Password history (12 previous passwords blocked)
  • Configurable expiry policy (default: 90 days)
📋

JWT Hardening & Session Management

Tokens use HMAC-SHA256 and are issued with a strict algorithm allowlist. The algorithm confusion attack vector (alg: none / RS256-to-HS256 downgrade) is closed — only HS256 is accepted. Tokens rotate automatically on sensitive actions and expire per policy.

  • Algorithm confusion vector closed — only HS256 accepted
  • "alg: none" tokens explicitly rejected
  • HMAC-SHA256 signed; Node.js built-in crypto only
  • Token rotation on sensitive actions (password change, role update)
  • View and revoke active sessions individually
  • Force logout across all devices
  • 8-hour token expiry, 30-day inactivity timeout
  • Bulk session invalidation via tokens_invalidated_before
👥

Role Middleware Hardening

Admin and manager middleware use explicit role allowlists — not a "not-unauthorized" check. A decoded token missing the role field cannot bypass access controls. Default role at registration is the least-privilege assignment.

  • requireAdmin() — explicit allowlist: ['admin']
  • requireAdminOrManager() — explicit allowlist: ['admin','manager']
  • Role-absent tokens fail closed (no bypass on missing claim)
  • Default role on registration: viewer (least privilege)
  • Default role on JWT decode: viewer (fail-safe fallback)
  • Role escalation restricted to Admin only
🏦

Banker Authentication

Bank staff authenticate via email and password (or SSO). Passwords are hashed with scrypt — industry-standard with unique per-password salts. JWT tokens carry explicit type claims to prevent cross-auth attacks.

  • Password hashing: scrypt with unique salts
  • HMAC-SHA256 signed JWT tokens
  • Explicit token type claim (type: "banker")
  • Borrower tokens rejected on banker endpoints
🤝

Borrower Portal Authentication

Borrowers access the portal through a separate, fully isolated authentication system. A borrower token cannot authenticate as a banker — the API validates token type on every request.

  • Separate token namespace from banker auth
  • Explicit type: "borrower" claim enforced server-side
  • Borrowers see only their own loan data
  • Borrower tokens rejected on banker endpoints

Access Control

Granular Role-Based Access Control

Four roles with distinct permissions. Data visibility is restricted by branch — bankers only see the records assigned to them. Every query is scoped server-side, not just filtered in the UI. Branch INSERT operations enforce org_id to prevent cross-tenant write gaps.

👥

Four User Roles

Access is enforced at the feature level. Roles cannot be self-assigned — only admins can grant or change roles within the organization.

  • Admin — full access, user management, org settings
  • Manager — branch-wide view and edit, no user management
  • Banker — assigned records only
  • Viewer — read-only, branch-scoped (default registration role)
🏢

Branch & Team Data Scoping

Branch ID is embedded in the JWT at login. All database queries are filtered server-side — a banker cannot access records outside their branch, even if they tried. Branch creation enforces org_id at the INSERT layer.

  • Branch ID embedded in JWT at login
  • Server-side query filtering on every read
  • Branch INSERT enforces org_id — multi-tenant gap closed
  • 26+ endpoints protected by org/branch guards
  • No client-side-only filtering
🏛️

Multi-Organization Isolation

Each institution is a completely isolated organization. Queries are scoped to org_id at the database layer — not delegated to the application layer to get right. Document sensitivity scoping is enforced at the record level.

  • org_id enforced on every database query
  • No shared tables between organizations
  • Organization context embedded in JWT
  • Document sensitivity scoping enforced at the record level
  • IDOR guards on activity creation — org ownership verified before write
💬

Comprehensive Audit Logging

Every authentication event, data change, permission change, and stage transition is logged with timestamp, user, IP address, and before/after values. The audit trail is append-only, searchable, and branch-filterable for regulatory exams.

  • Login, logout, failed login, MFA events
  • All loan, deposit, customer data changes
  • Role changes, permission changes, admin actions
  • Before/after snapshots on all data changes
  • 6-year retention, append-only (no overwrites)
  • Branch-scoped filter for targeted regulatory exams
  • CSV export up to 50,000 records with chain-of-custody logging

Data Protection

Encryption Throughout the Stack

Sensitive data is encrypted at the field level before it touches the database — including guarantor PII, participation partner contacts, referral source PII, and all HMDA demographic fields. Key rotation is supported without downtime. HTML escaping is applied wherever user-supplied content can reach output.

🔒

PII Field-Level Encryption

All personally identifiable information is encrypted with AES-256-GCM before it is written to the database. A direct database read returns ciphertext, not plaintext. Encryption coverage extends across all entity types.

  • AES-256-GCM encryption per field, Node.js built-in crypto
  • Customer: SSN, Tax ID, DOB, income, phone, email, address
  • Guarantor: SSN/EIN, address, phone, email, net worth
  • Participation partner: contact info (name, phone, email)
  • Referral contacts: phone, email — last 4 shown in list views only
  • HMDA demographic fields: race, ethnicity, sex
  • Insurance policies, credit scores, closing details — AES-256-GCM
  • Encryption key managed via environment variable — never hardcoded
  • Key versioning for rotation without downtime
  • Masked display (e.g. ***-**-1234)
  • HMAC-searchable indexes (no plaintext exposure on search)
  • PII never appears in server logs or error responses
🛡️

XSS Prevention

User-supplied content that can reach rendered output is HTML-escaped at the point of storage and output. This covers letter template merge fields, demo email content, and SAML error responses — closing both stored XSS and reflected XSS vectors.

  • Letter template merge fields HTML-escaped on render (stored XSS prevention)
  • Demo email content HTML-escaped before send
  • SAML error handler output HTML-escaped (reflected XSS prevention)
  • CSP hardened — unsafe-inline removed from script-src
  • Nonce-based CSP for inline scripts where required
  • X-XSS-Protection header enforced
📂

Document Storage (Cloudflare R2)

Borrower-uploaded documents (tax returns, pay stubs, financial statements) are stored in Cloudflare R2 — server-side encrypted object storage. Files never touch the application server or banker workstations.

  • Server-side encryption at rest
  • One-time HMAC-signed upload tokens
  • 20 MB per-file limit enforced server-side
  • MIME type validated server-side (magic byte check, not just extension)
  • Signed download URLs with expiration
  • No public URLs — all access authenticated
💾

Password Hashing

User passwords are hashed using scrypt with a unique random salt generated for every password. This is not a single shared salt — even an attacker with database access cannot precompute scrypt hashes.

  • scrypt algorithm with per-password unique salt
  • Node.js built-in crypto — no third-party password library
  • No plaintext password storage, ever
  • Key stretching: high CPU/memory cost (intentional)
🌐

Data in Transit

All traffic between users and BankerPipeline is encrypted in transit. HTTP is redirected to HTTPS. HSTS header set to 1 year including subdomains.

  • HTTPS enforced (301 redirect from HTTP)
  • HSTS 1-year header (includes subdomains)
  • TLS 1.2+ only (Render + Cloudflare managed)
  • Strict-Transport-Security preload
📦

Error Response Sanitization

Production error responses are sanitized across all API routes. Database schema details, stack traces, and internal file paths are never returned to the client — they are logged server-side only.

  • 82+ catch blocks sanitized — no stack trace leakage
  • Generic error messages returned to clients
  • Internal errors logged server-side only
  • SPA catch-all route does not serve .env, server.js, or migrations

Network & API

API & Infrastructure Hardening

Every API endpoint is protected against common attack vectors — rate limiting, MIME type validation, a hardened CSP with no unsafe-inline, CORS restricted to explicit origins, and a blocked exploit path list.

🛡️

HTTP Security Headers

Every response includes a comprehensive set of security headers. The Content Security Policy is hardened — unsafe-inline is removed from script-src. Inline scripts that remain are nonce-protected.

  • Content-Security-Policy — no unsafe-inline in script-src
  • Nonce-based CSP for any remaining inline scripts
  • HSTS (1-year, includes subdomains)
  • X-Frame-Options: DENY (clickjacking prevention)
  • X-Content-Type-Options: nosniff
  • X-XSS-Protection enabled
  • Referrer-Policy: strict-origin
  • Permissions-Policy (camera, mic, geolocation disabled)

Rate Limiting

Authentication endpoints are rate-limited to block credential stuffing attacks. MFA setup and verification endpoints have independent limits. Requests beyond the limit receive a 429 response with a clear retry window.

  • 5 attempts / 15 min per IP on all auth endpoints
  • Separate limits for banker and borrower auth endpoints
  • MFA verify endpoint independently rate-limited
  • MFA setup endpoint independently rate-limited
  • Progressive delay on repeated failures
  • No debug or diagnostic endpoints exposed in production
🤐

Input Sanitization & Payload Limits

All API endpoints validate and sanitize input to prevent SQL injection and XSS. Request payloads are size-limited. File uploads validate MIME type server-side via magic byte inspection — not just file extension.

  • Parameterized SQL queries throughout
  • Input validation on all API endpoints
  • 1 MB max JSON payload size
  • MIME type validation via magic byte check (not extension-only)
  • File type allowlist: PDF, JPG, PNG, DOCX, XLSX, GIF, TIFF, CSV
  • Common exploit paths blocked at middleware level
🌌

CORS Lockdown

Cross-origin requests are blocked by default. The allowed origin list was migrated from a wildcard to an explicit allowlist — only BankerPipeline domains are permitted.

  • CORS changed from wildcard (*) to explicit origin list
  • Allowed: bankerpipeline.com and bankerpipeline.polsia.app
  • Borrower portal CORS preflight handled on pre-token endpoints
  • All other origins rejected at the CORS middleware level

Borrower Portal

Borrower Portal: Isolated by Design

The borrower-facing portal is a completely separate security domain from the CRM. Borrowers cannot access banker views, banker data, or administrative functions — enforced at the API level, not just the UI. Construction draw submissions and closing worksheet views are scoped to the borrower's own loan only.

🔒

Horizontal & Vertical Isolation

Two layers of isolation are enforced on every borrower request. Horizontal: borrowers can only access their own loan records. Vertical: borrower tokens are rejected by all banker and admin API endpoints.

  • Horizontal isolation — borrowers see only their own data
  • Vertical isolation — borrower tokens rejected on all CRM/admin endpoints
  • Token type claim (type: "borrower") validated server-side on every request
  • No shared session state between borrower and banker systems
⏱️

Shorter Session Expiry

Borrower sessions expire after 24 hours — significantly shorter than the 30-day CRM session window. This reflects that borrowers are occasional users, not daily staff, and that shorter windows limit exposure from shared or public devices.

  • 24-hour session expiry (vs. 30-day for CRM users)
  • Token expiry enforced server-side, not client-side
  • Expired tokens rejected with 401 — no silent extension
📄

Document Upload Validation

Borrower document uploads are validated server-side before storage. File type allowlist, MIME magic byte inspection, path traversal blocking, and file size caps are all enforced at the server layer.

  • File type allowlist enforced server-side (PDF, JPG, PNG, DOCX, XLSX)
  • MIME type validated via magic byte check (not extension-only)
  • Path traversal protection — filenames sanitized before storage (verified)
  • 20 MB per-file size limit enforced at the server
  • One-time signed upload tokens — no public write access to storage
  • Files stored in Cloudflare R2 — never on application server
📊

Portal Feature Isolation

Borrower-accessible features — construction draw submissions, closing worksheet read-only views, and messaging — are scoped to the authenticated borrower's loan. No cross-loan access is possible regardless of URL manipulation.

  • Construction draw submissions: loan ownership verified before write
  • Closing worksheet: read-only view, borrower's loan only
  • Messages: org-isolated, borrower read-only
  • Checklist items: borrower-visible toggle controlled by banker
  • Separate middleware guards on borrower vs. banker routes
  • Admin and manager middleware use explicit role allowlists

Infrastructure

Enterprise-Grade Hosting

BankerPipeline runs on Render, a SOC 2 Type II compliant cloud platform. Data is stored in Neon PostgreSQL — enterprise-grade serverless database with automatic backups and point-in-time recovery.

☁️

Render (SOC 2 Type II)

Render is SOC 2 Type II certified — an independent auditor verifies their security, availability, and confidentiality controls annually. Your data runs in isolated build and runtime environments.

  • SOC 2 Type II certified hosting platform
  • Automatic HTTPS (Let's Encrypt)
  • DDoS protection via Cloudflare
  • Isolated build and runtime environments
🗄️

Neon PostgreSQL

Fully managed serverless PostgreSQL with automatic backups, point-in-time recovery, and no manual patching. Tenant data is fully isolated per organization at the query layer.

  • AES-256 encryption at rest
  • TLS 1.2+ in transit
  • Automated daily backups
  • Point-in-time recovery
Compliance

Aligned with Banking Regulations

BankerPipeline was designed with GLBA Safeguards Rule compliance requirements in mind — not retrofitted after deployment. The Exam Prep Dashboard provides direct links to all examiner views, and the Unified Audit Log surfaces pre-filtered examiner quick-views for Admin Actions, Permission Changes, Failed Logins, Deletions, and Data Exports.

GLBA Safeguards

Gramm-Leach-Bliley Act

AES-256-GCM field-level encryption for PII, role-based access controls, audit logging, and HTTPS enforcement are all aligned with the FTC's Safeguards Rule requirements for financial institutions.

SOC 2 Hosting

Hosting Infrastructure

Render is SOC 2 Type II certified. Neon PostgreSQL and Cloudflare R2 are enterprise-grade platforms with independent security programs — not shared consumer infrastructure.

Least Privilege

Access Controls

Role-based access control, branch scoping, and org-level isolation ensure users access only what they need. Default registration role is viewer — least-privilege assignment. Aligns with NIST and CC6.3 principles.

Encryption

Encryption at Rest & In Transit

AES-256-GCM field-level encryption for all sensitive PII across customers, guarantors, participation partners, and referral contacts. TLS 1.2+ in transit. scrypt password hashing. Key versioning supports rotation without service interruption.

Audit Trail

Unified Audit Log & Examiner Views

Append-only audit log with 6-year retention and before/after snapshots. Examiner quick-views pre-filter for: Admin Actions (90d), Permission Changes, Failed Logins, Deletions, and Data Exports. Branch-scoped filtering for targeted regulatory exams. CSV export is chain-of-custody logged.

Exam Prep

Exam Prep Dashboard

The Exam Prep Dashboard provides direct links to all examiner views from a single page. Compliance checklist with unified audit log card, 6-year retention confirmation, and pre-filtered examiner quick-view links — reducing exam preparation time.

Concentration

Concentration Limit Monitoring

Regulatory concentration limits — CRE/C&D concentration ratios and single-borrower exposure limits — are monitored in real time. Breach alerts are surfaced in the dashboard. Limits are configurable by administrators against bank capital.

HMDA

HMDA Data Access Controls

HMDA LAR data — including race, ethnicity, sex, and age demographic fields — is restricted to users with appropriate roles. Demographic fields are encrypted at rest with AES-256-GCM. Access is logged in the audit trail.

CRA

CRA Data Controls

Community Reinvestment Act data is access-controlled at the role level. CRA reporting data is restricted to authorized roles and all access is included in the complete audit trail.

Exports

Compliance Export Controls

Compliance exports — including HMDA LAR, CRA, and full audit trail exports — are restricted to Admin role only. No Manager, Banker, or Viewer can initiate a compliance data export. All exports are themselves logged in the audit trail.

Bot Protection

Exploit Path Blocking

Common exploit paths (/.env, /.git, WordPress probes) are blocked at the middleware level. The SPA catch-all route does not serve sensitive files. Rate limiting on all auth endpoints limits automated credential stuffing.

Underwriting

Underwriting Checklist Security

Underwriting checklist template management endpoints — create, update, delete — are secured with requireAdmin() middleware. Bankers and managers can use templates but cannot modify the template library.

Security Verification

Continuous Security Review

BankerPipeline undergoes structured security sweeps rather than point-in-time audits. All findings from each sweep are tracked to resolution before the next sweep begins. The findings log below reflects the current verified state.

4
Independent security sweeps completed — all findings resolved
20+
QA sweep bugs identified and patched in full verification pass
10
P2 security issues identified and independently patched
71/71
Security test cases passing — zero open findings

Prior Audit Findings — Verified Resolved

✓ C-01 — Resolved
✓ C-01b — Resolved
✓ C-02 — Resolved
✓ H-08 — Resolved
✓ M-01 — Resolved
✓ M-02 — Resolved
✓ M-03 — Resolved
✓ M-04 — Resolved
✓ M-05 — Resolved
✓ M-06 — Resolved

All critical, high, and medium findings from prior sweeps have been patched and independently verified. No open security findings as of the last review cycle.

At a Glance

Security Summary

Key security controls for your compliance team's evaluation.

Control Area Technology / Approach Status
SSO / SAML 2.0 & OIDC SAML 2.0 SP/IdP-initiated, OIDC authorization code flow — Okta, Azure AD, Google Workspace, OneLogin ✓ Active
Two-Factor Authentication TOTP RFC 6238 (Google Authenticator, Authy, 1Password), 8 backup codes, 30-day device remember, MFA setup + verify independently rate-limited ✓ Active
JWT Algorithm Hardening HS256-only allowlist — algorithm confusion (alg: none, RS256-to-HS256) vector closed ✓ Active
Role Middleware (Admin/Manager) requireAdmin() / requireAdminOrManager() — explicit allowlists, role-absent tokens fail closed, default role: viewer ✓ Active
Brute Force Protection Account lockout after 5 failed attempts, 15-min cooldown, progressive delay, no account enumeration ✓ Active
Password Policy Min 8 chars, upper/lower/digit/special, real-time strength indicator, password history, configurable expiry ✓ Active
Session Management HMAC-SHA256 JWT, token rotation on sensitive actions, bulk session invalidation, 8h/30d expiry ✓ Active
PII Field-Level Encryption AES-256-GCM — customers, guarantors (SSN/EIN, address, net worth), participation partners, referral contacts, HMDA demographic fields ✓ Active
Password Storage scrypt with per-password unique salts, Node.js built-in crypto — no plaintext, no shared salt ✓ Active
XSS Prevention HTML escaping on letter templates (stored), demo emails, SAML error handler (reflected); CSP with no unsafe-inline; nonce-based CSP ✓ Active
Data Encryption at Rest Neon PostgreSQL (AES-256), Cloudflare R2 (server-side encryption) ✓ Active
Data Encryption in Transit TLS 1.2+, HSTS (1-year), HTTPS-only (301 redirect) ✓ Active
Role-Based Access Control Admin / Manager / Banker / Viewer — feature-level permissions, server-side enforcement, explicit allowlists in middleware ✓ Active
Multi-Tenant Isolation org_id enforced on every read and write; branch INSERT org_id enforced; document sensitivity scoped; IDOR guards on activity creation ✓ Active
CORS Lockdown Wildcard removed — explicit allowlist: bankerpipeline.com, bankerpipeline.polsia.app ✓ Active
Content Security Policy CSP hardened — unsafe-inline removed from script-src; nonce-based CSP for remaining inline scripts ✓ Active
File Upload Validation MIME magic byte validation (not extension-only), file type allowlist, path traversal protection, 20 MB limit, signed upload tokens ✓ Active
Error Response Sanitization 82+ catch blocks sanitized — no stack traces, schema details, or file paths returned to clients ✓ Active
Rate Limiting 5 attempts / 15 min per IP on auth endpoints; MFA setup and verify independently rate-limited ✓ Active
Security Headers HSTS, CSP (no unsafe-inline), X-Frame-Options (DENY), X-Content-Type-Options, X-XSS-Protection, Referrer-Policy, Permissions-Policy ✓ Active
Unified Audit Log Append-only, 6-year retention, branch-scoped filter, 5 examiner quick-views, CSV export with chain-of-custody logging ✓ Active
Exam Prep Dashboard Direct links to all examiner views — Admin Actions (90d), Permission Changes, Failed Logins, Deletions, Data Exports ✓ Active
Concentration Limit Monitoring CRE/C&D ratios, single-borrower exposure — real-time gauges, breach alerts, configurable against bank capital ✓ Active
Hosting Compliance Render (SOC 2 Type II), Neon PostgreSQL, Cloudflare R2 ✓ Active
GLBA Safeguards Alignment Encryption, access controls, audit trail, HTTPS, PII protection, password policy, least-privilege defaults ✓ Aligned
HMDA Demographic Data Encryption AES-256-GCM encryption for race, ethnicity, sex fields — never plaintext at rest ✓ Active
Borrower Portal Isolation Horizontal + vertical isolation — borrower tokens rejected on all CRM endpoints; construction draws and closing worksheet scoped to own loan ✓ Active
Borrower Portal Session Expiry 24-hour expiry for borrower sessions (vs. 30-day for CRM staff) ✓ Active
PII-Free Logs PII excluded from server logs and error responses — only opaque identifiers logged ✓ Active
Compliance Export Controls HMDA LAR, CRA, and audit trail exports restricted to Admin role only; exports are chain-of-custody logged ✓ Active
HMDA / CRA Access Controls HMDA LAR and CRA data access restricted to authorized roles, all access logged ✓ Active
Security Verification 4 independent sweeps completed; 71/71 test cases passing; all prior audit findings (C-01, C-01b, C-02, H-08, M-01–M-06) resolved ✓ Verified