The minimum viable security posture for a 10-person SaaS
Six controls, one afternoon, most of them free
Most security writing for startups opens with threats that will not kill you: ransomware from nation-state actors, supply-chain attacks on critical infrastructure, zero-day exploits sold on darknet forums. These exist. They are not what hits a 10-person SaaS.
What actually happens is more boring and more fixable. Here is a representative incident log from a year in small SaaS security:
- A developer commits a `.env` file containing a production database connection string to a public GitHub repository. An automated scanner finds it within four minutes. The attacker connects to the database and reads customer records before the team notices the repository visibility setting.
- A contractor whose access was never revoked still has direct database credentials from when they worked on the product eight months ago. Their email address has since been reassigned to a new hire at their old company.
- A multi-tenant application filters records by tenant_id in the query layer. One endpoint is refactored in a sprint. The WHERE clause is moved, then not reinstated. Customer A can now read Customer B's data by changing a single integer in the request URL.
- A dependency added 14 months ago has had a known remote code execution CVE for six weeks. Nobody ran a scan because automated security tooling was always "the next thing to add."
- An employee's Google account is compromised via phishing. They reused a password. The production cloud console had no MFA requirement. The attacker spins up GPU instances.
None of these are sophisticated. The remediation cost for any one of them typically runs between $40,000 and $200,000, between incident response fees, legal counsel, regulatory notifications, and customer churn following disclosure. Six controls, most of them free, prevent most of them.
The threat model for a 10-person SaaS
Before picking controls, it helps to be precise about what you are protecting against. At this scale, the relevant threat actors are:
- Automated scanners that crawl public repositories and cloud storage for credentials. These are not humans. They find secrets within minutes of exposure and sell access in bulk.
- Financially-motivated attackers who exploit known CVEs against publicly-listed software. They are not targeting you specifically. They run the CVE against every reachable instance of the vulnerable library.
- Former employees or contractors with access that was never revoked. Most are not malicious. They also have not changed their passwords since they left.
- Opportunistic attackers who find IDOR (insecure direct object reference) bugs via automated scanning. A broken tenant filter in a multi-tenant app is routinely found by scanners, not humans.
You are not, at this stage, defending against nation-state actors or targeted zero-day attacks. The controls that protect you against the threats above are different: simpler and cheaper than the ones you would build for a financial institution or a defence contractor.
Control 1: Secrets out of code
The highest-impact change for the least effort. Database connection strings, API keys, webhook signing secrets, internal service tokens: none of these should appear in a git repository, even a private one. Private repositories get made public by mistake. Internal repositories get accessed by contractors. Package registries cache dependencies.
The subtlety: deleting a committed secret and pushing again does not fix the problem. Git history is permanent, and public repository commits are cached by third-party scanners within minutes of being pushed. Assume any secret that touched a public repository is already compromised. Rotate first, then fix the process.
Three things to set up, in order:
- Move all secrets to a dedicated store. AWS Secrets Manager, HashiCorp Vault (free tier), or the native environment variable store in your deployment platform: Railway, Fly.io, Render, and Heroku all have this. Cost: $0–5/month.
- Add detect-secrets as a pre-commit hook. It blocks new secrets from entering the repository. Twenty minutes to wire up; free.
- Enable GitHub's built-in secret scanning on every repository. It runs automatically on every push and alerts on ~200 common credential formats. Free on public repos; included in GitHub Advanced Security for private ones.
Control 2: MFA on every production access point
Multi-factor authentication stops credential-based attacks cold. If an employee's password is phished or appears in a credential breach dump, MFA is the difference between a contained credential theft and a full production breach.
"Production access" has a specific meaning here: your cloud provider console, your database access layer, your deployment platform, your CI/CD pipeline configuration, and your identity provider. MFA on all of them. Not just the cloud console.
The specific MFA form matters less than the coverage. TOTP authenticator apps are adequate for most access points. The one case where a hardware token earns its $25 cost is your cloud root account. Root access is what lets an attacker spin up GPU instances for cryptocurrency mining on your bill, which is the most common financially-motivated attack against small cloud accounts. Enable hardware MFA on root, store those credentials in a password manager, and do not use them for day-to-day operations.
Control 3: Automated dependency scanning
Open-source dependencies are the largest unmanaged attack surface for most SaaS teams at this stage. The average JavaScript application has over 1,000 transitive dependencies. A CVE in a library you imported 18 months ago and have not thought about since is a documented, exploited attack vector.
The fix is automated scanning that runs on every pull request, not a quarterly manual audit you will skip:
- GitHub Dependabot: enabled by default on GitHub repositories. Opens pull requests automatically when a dependency has a known CVE. Ten minutes to configure, zero ongoing cost.
- Snyk free tier: scans dependencies and Docker images. The free tier covers most of what a 10-person team needs and integrates into CI pipelines directly.
- npm audit / pip-audit / bundler-audit: built into the respective package managers. Adding them to your CI pipeline takes five minutes and fails the build on critical-severity findings.
Set a written policy and stick to it: critical CVEs get a patch within 72 hours; high-severity within two weeks. The point is not to patch everything immediately. It is to never have an actively exploited critical vulnerability sitting unacknowledged for 90 days.
Control 4: Tenant isolation as a design decision
This is the control that cannot be retrofitted without a multi-week engineering project and significant risk of data exposure during the migration. Make this decision at schema design time. The other five controls on this list can be added whenever you get around to them. This one cannot.
The failure mode is well-documented: a multi-tenant application stores all customers' data in shared tables and filters by tenant_id in the query layer. Application code does the filtering. When one query path is wrong — because an engineer forgot the clause, or a refactor moved it, or a new feature was added without it — Customer A can read Customer B's data. This is not a theoretical edge case. IDOR bugs in multi-tenant SaaS are found by automated scanners routinely.
Two implementations that hold up in production:
- Row-level security in Postgres: the database enforces the tenant filter, not the application. A missing WHERE tenant_id clause in application code does not become a data leak — Postgres rejects the query before it returns cross-tenant rows. One day to implement at schema design time. A multi-week project to retrofit onto a table with tens of millions of rows.
- Schema-per-tenant: each customer gets a separate Postgres schema. Hard isolation guarantees but higher operational overhead. Worth it when customers are contractually entitled to schema-level isolation — common in regulated industries and enterprise contracts.
Control 5: Production access logs
You cannot run an incident response without knowing what happened. Most post-breach investigations fail not because logs never existed, but because nobody turned them on before the incident.
Three things to enable, none of them expensive:
- Cloud access logs: AWS CloudTrail, GCP Cloud Audit Logs, and Azure Monitor all record every API call made against your cloud account — who authenticated, from which IP, which resource they touched, and when. Turn these on. Set an alert for access from unexpected geographies or from accounts that should not have console access. Cost: under $5/month for a small environment.
- Application-level audit trail for destructive actions: deletes, bulk updates, permission changes, and billing events should write to an append-only log — something that cannot be updated or deleted after the fact. This is what you point to when a customer asks "who deleted these records?" and when a regulator asks "who had access to this data?"
- Log retention: 90 days minimum, stored somewhere separate from your primary application database. S3, CloudWatch Logs, or a managed log service. If your primary database is compromised, your attacker should not also have access to the logs that document what they did. Cost: $5–20/month.
Control 6: One identity provider
A 10-person company typically has 15–30 SaaS tools across development, communication, infrastructure, and operations. By default, each has its own set of credentials. When someone leaves the team, you need to revoke access in every tool individually.
You will forget at least one. This is not a process failure specific to your company — it is what happens when access management is distributed across thirty separate systems, each with its own admin panel and its own login page.
The fix is a single identity provider that becomes the canonical source of who has access to what. When someone leaves, you deactivate their account in the IdP and every connected tool's access is cut simultaneously.
Free options that work at this scale:
- Google Workspace: if your team already uses Gmail, this is your IdP. Most SaaS tools support "Sign in with Google." Use it everywhere it is available. When a team member leaves, deactivating their Google account cuts access to every Google-connected tool in one step.
- Okta free tier (up to 15 monthly active users): full SAML/OIDC identity provider with MFA management. Gives you SSO into tools that support SAML but not Google OAuth.
- Cloudflare Zero Trust free tier: an access proxy that adds authentication in front of internal tools — your admin panel, your database GUI, your monitoring dashboard — without requiring SAML integrations in each tool.
The minimum viable version at zero cost: use Google SSO for every tool that accepts it, and run a quarterly access audit for tools that do not. A shared spreadsheet is sufficient at this stage. The discipline matters more than the automation.
| Control | Primary threat | Effort | Monthly cost |
|---|---|---|---|
| Secrets out of code | Automated credential scraping | 4 hours | $0–5 |
| MFA everywhere | Phishing, credential stuffing | 2 hours | $0–20 |
| Dependency scanning | Known CVE exploitation | 1 hour | $0 |
| Tenant isolation | Cross-tenant data exposure | Design-time decision | $0 |
| Access logs | Undetected breach, slow IR | 4 hours | $5–20 |
| Single identity provider | Orphaned access on offboarding | 4 hours | $0–12 |
What to leave until later
SOC 2 is the most common question after a first enterprise sales call. If a prospect is asking for it, they want assurance, not the certificate specifically. Most procurement teams at sub-$500k ACV deals will accept a documented control environment and a security questionnaire answered with specifics. SOC 2 is a $30,000–$80,000 annual programme. It makes sense when you are closing deals where the buyer's procurement team requires it as a condition of purchase, typically past $2M ARR.
A web application firewall sounds like a first purchase. It is not. Parameterised queries and input validation in application code prevent SQL injection more reliably than a WAF sitting in front of your API. A WAF is a last line of defence, not a substitute for secure application code. Add it later, after the six controls above are in place and working.
Full SIEM tooling — Splunk, Sumo Logic, and their peers — is genuinely useful at scale. At 10 people running one application, CloudWatch or Datadog's basic log aggregation handles the same function at a fraction of the cost and without a dedicated engineer to manage it. The complexity overhead of a full SIEM at this stage usually makes security worse, not better, because nobody has time to respond to alerts.
The first enterprise security questionnaire
When a prospective enterprise customer sends a security questionnaire, the questions that filter vendors at the first pass are almost always the same five:
- Do you support SSO via SAML or OIDC?
- Do you encrypt data at rest and in transit?
- Where is data stored, and with which cloud provider?
- What is your data retention policy after account termination?
- Do you have penetration test results available?
The six controls above address questions 1 and 2 directly. For questions 3 and 4, write short, honest answers — two paragraphs each — and keep them in a shared document you can attach to questionnaires. Do not write policy documents longer than one page at this stage. The procurement reviewer will not read them.
For the penetration test question: the honest answer at 10 people is often "no, not yet." A vague answer to this question — "we take security seriously" — is worse than a specific honest one. What satisfies most mid-market procurement teams is: "We have not completed a formal penetration test. Our control environment includes [specific list]. We are targeting a lightweight penetration test by Q3 and will share the report with you on completion." Specific and honest gets more deals across the line than aspirational and vague.
These six controls are not a security programme. They are the foundation one. A programme involves documented policies, a risk register, regular third-party audits, and eventually formal certification. That comes later, when the business is large enough to justify the overhead. What these controls give you now is a posture that closes the attack vectors that actually hit companies at your stage, at a cost that rounds to zero.
Frequently asked questions
Related reading
Annual billing in B2B SaaS: when to push it, when to wait, and the migration problem nobody prepares for
Most SaaS founders push annual billing too early or too late. Here's a stage-specific framework — and the migration mechanics nobody writes about.
The case against the freemium tier in B2B SaaS
The freemium tier converts at 2–5% in B2B SaaS. A trial converts at 15–20%. That gap is structural, not tactical — and most founders conflate freemium with PLG and end up subsidising non-converting users indefinitely.
AI wrapper companies are failing. Founders keep building them. Here's why both things are true.
Three years into the AI product era, wrapper company failure rates are well-documented. Less examined is why intelligent founders keep building them anyway — and the one condition that makes some of them right.