Skip to main content
FORTFOLIO

Security

Last updated: 2026-06-01

Fortfolio is built by a small team. We do not have a SOC 2 report, an ISO 27001 certificate or a fleet of compliance auditors. What we do have is a deliberately small attack surface, sensible defaults, and a commitment to be honest about what we have and have not done. This page describes our security posture in concrete terms so you can make your own judgement.

Transport security

Every page on fortfolio.app is served over HTTPS, using a TLS certificate from Let’s Encrypt that is auto-renewed by Certbot. Plain HTTP requests are 301-redirected to HTTPS at the Nginx layer. We use modern cipher suites (TLS 1.2 and 1.3) and disable legacy protocols. HSTS is enabled with a 6-month max-age.

Authentication

We do not store passwords. Authentication is delegated entirely to Google OAuth via NextAuth. When you sign in, Google returns an identity token to our server, we verify it server-side, and we issue a session cookie that lives for 30 days. The only authentication-related secret we hold is the NextAuth signing key, which is stored in environment variables on the server and never committed to the repository.

We do not support password login, so phishing the user’s Fortfolio password is not a possible attack — there is no such password. We do support sign-out from every device via the account menu.

Data at rest

User data lives in a managed PostgreSQL instance on DigitalOcean. The disk is encrypted at rest by the provider. Database backups are encrypted and rotated. Access to the database is restricted to the application server on the same private network and to a small number of administrator IP addresses. The database is not exposed to the public internet.

Secrets handling

Third-party API keys — yfinance fallback, EODHD, Resend, Lemon Squeezy, Google OAuth client secret — live in server-side environment variables on the DigitalOcean droplet. They are never sent to the browser, never logged, and never appear in client-side bundles. The Python Flask microservice that mediates yfinance calls binds only to 127.0.0.1:5001 , so it is not reachable from outside the host even if a firewall rule were misconfigured.

Application security

  • CSRF protection is enabled on every state-changing route through the NextAuth CSRF token.
  • All form inputs are validated server-side, including the holdings array (weights are clamped, tickers are length-bounded, currencies are enum-checked).
  • All database queries are parameterised via Prisma; we do not build SQL from user input.
  • The Content Security Policy disallows inline scripts from origins other than our own and the small set of providers we whitelist.
  • We use Next.js’ built-in escaping for any user-rendered string (bug-report text, saved-portfolio names) and never call dangerouslySetInnerHTML with user content.
  • Rate limiting is enforced on the alert-evaluation cron and on the ticker-validation endpoint to prevent abuse of upstream data providers.

Dependency hygiene

We pin direct dependencies in package.json and check package-lock.json into the repository so production builds are deterministic. We run npm audit on every build and fix high-severity findings within one business day. We use Dependabot for automated dependency PRs against the staging branch.

Infrastructure

Fortfolio runs on a single DigitalOcean droplet (Ubuntu 22.04 LTS) in the United States region. PM2 supervises the Node and Python processes; Nginx terminates TLS and proxies to PM2. The droplet is administered via SSH key authentication only — password SSH is disabled, and inbound SSH is restricted to a small set of administrator IPs. The host firewall (ufw) drops everything except 80, 443 and 22.

We do not run a Kubernetes cluster, a service mesh, or a separate production environment. This is intentional: the smaller the infrastructure, the smaller the attack surface, and the lower the chance of a misconfiguration creating an opening we did not notice.

What we do not claim

To be candid about our limits: Fortfolio is not:

  • SOC 2 Type II certified;
  • ISO 27001 certified;
  • PCI-DSS certified (we do not handle card data — payments are processed by Lemon Squeezy as merchant of record);
  • HIPAA-covered (we hold no health information);
  • operating a 24/7 security operations centre.

We do not store anything that requires those certifications. The data we hold is your email, your Google profile, the portfolios and tests you choose to save, and your support emails. We protect it with the controls described above.

Responsible disclosure

If you believe you have found a security vulnerability, please email security@fortfolio.app with details and, where possible, a proof of concept. We will:

  • Acknowledge receipt within 72 hours.
  • Provide a triage assessment within 7 days.
  • Aim to remediate critical vulnerabilities within 14 days, high within 30, medium within 60.
  • Credit you publicly in our security disclosures if you would like.

We ask in return that you give us reasonable time to remediate before public disclosure, that you do not exfiltrate user data beyond the minimum needed to demonstrate the issue, and that you do not pivot to services or systems beyond Fortfolio itself.

Status and incidents

Live system status and any active incidents are posted on the Status page. Material security incidents affecting user data will be disclosed to affected users by email within 72 hours of confirmation, as required by the GDPR and consistent with state breach-notification statutes in the United States.

Related

Privacy Policy, Cookie Policy, Terms of Service.