Most SaaS breaches are not clever. They are a leaked credential, a missing tenant check on one endpoint, or an object storage bucket somebody made public during a debugging session and never changed back.
The security industry sells posture management, and posture management is genuinely useful once you have dozens of applications to inventory. But if you are the person building the SaaS rather than the person buying forty of them, the controls that move your risk are smaller, cheaper and more boring than the product category suggests.
This is the builder’s list: the things that go wrong in applications like yours, in roughly the order they actually go wrong.
Table of contents
- Tenant isolation is the one that ends companies
- Secrets: the leak is almost always in a place you control
- Sessions and tokens
- Authorisation beyond the login page
- Dependencies and the supply chain
- Logging enough to answer the question later
- How this fits the rest of the stack
- FAQ
Tenant isolation is the one that ends companies
Every multi-tenant application has the same catastrophic bug available to it: one endpoint that reads an identifier from the request and forgets to check who owns it.
-- The bug. Looks fine in review, returns anyone's invoice.
SELECT * FROM invoice WHERE id = $1;
-- The fix. Ownership is part of the query, not a separate check.
SELECT * FROM invoice WHERE id = $1 AND tenant_id = $2;
The reason this keeps happening is that the correct version depends on a human remembering, on every query, forever, including the one added at 6pm on a Friday. Memory is not a control.
Make it structural instead. The approaches that survive, in increasing order of strength:
- A data access layer where the tenant is a required argument. If the function cannot be called without it, it cannot be forgotten.
- Row-level security in the database, so the tenant filter is enforced below your application code and applies even to an ad-hoc query.
- A database or schema per tenant, so a cross-tenant read requires connecting somewhere else entirely.
Then test it. A single automated test that authenticates as tenant A and requests a known object belonging to tenant B, run against every resource endpoint, catches this class of bug permanently. It is the highest-value test in a multi-tenant codebase and most teams do not have it.
Secrets: the leak is almost always in a place you control
Credentials do not usually escape through a sophisticated attack. They escape through a commit, a log line, a CI output, or an environment variable printed by a debug handler.
The controls that matter here are unglamorous:
- Nothing sensitive in the repository. Secret scanning in CI, and a pre-commit hook so the mistake is caught before it is published rather than after.
- Injected at runtime, not baked into images. A secret in a container layer is in your registry forever, including in every cached layer of every derived image.
- Redacted in logs. Assume every error handler will eventually print a request object. Redact at the logger, not at each call site.
- Rotatable without a code change. If rotating a key requires a deploy, it will not happen on the day it matters.
- Scoped narrowly. The credential your background worker uses should not be able to read your billing tables.
The rotation point deserves emphasis. The question is not whether a credential will leak; it is how long it stays valid afterwards. A key you can rotate in two minutes turns a serious incident into an annoying afternoon. A key hard-coded across four services turns the same event into a week.
On RunxBuild, environment variables are set per service in the dashboard and applied on the next deploy, which keeps values out of the repository and makes rotation a form field rather than a code change.
Sessions and tokens
Authentication gets the attention; session handling causes the incidents. A few decisions carry most of the weight.
Cookies over local storage for session tokens. A cookie marked HttpOnly is unreadable from JavaScript, which means a cross-site scripting bug does not immediately become account takeover. A token in local storage is readable by any script on the page, including one that arrived through a compromised dependency.
Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=86400
Rotate the session identifier on privilege change. Issue a new one at login and at any step-up. Keeping the pre-login identifier is session fixation, which is old but still ships.
Server-side revocation. A stateless token that cannot be revoked means a logout is a suggestion. Either keep sessions server-side, or keep access tokens short and revoke the refresh token.
Rate limit authentication separately. Credential stuffing looks like normal traffic in aggregate and obvious when you count failures per account. Limit by account as well as by address, because an attacker distributing across many addresses defeats address-based limits entirely.
Authorisation beyond the login page
Once a user is authenticated, the remaining question is what they may do, and that is where the subtle bugs live.
The pattern that fails is authorisation checked in the interface: the button is hidden, so the endpoint is assumed safe. The endpoint is a URL and the interface is a suggestion.
Two specific cases worth auditing deliberately:
- Bulk and export endpoints. These are frequently added later, often by a different person, and frequently miss the tenant or role check that the single-object endpoint has.
- Anything taking an identifier from the client. Report generators, file downloads, webhook replay, admin impersonation. Each one is a place where an identifier crosses a trust boundary.
Role design also matters more than it looks. Most SaaS applications ship with an admin role that can do everything, and then every internal user gets it because the alternative is fiddly. The result is that a single compromised staff account is a full compromise. Separate read from write, and separate customer-data access from configuration access, before you have thirty employees rather than after.
Dependencies and the supply chain
Your application is mostly other people’s code, and that code changes without asking you.
A workable baseline that does not consume a whole engineer:
- A lockfile committed, so builds are reproducible and a dependency cannot change silently between your test and your deploy.
- Automated dependency alerts on the repository, triaged weekly rather than continuously.
- Patch promptly for anything reachable from a request path; be more relaxed about build-time-only packages.
- Pin base images by digest rather than tag, so the same build twice produces the same thing.
- Fewest dependencies that get the job done. Every package is a trust relationship, and a small utility package is a bad trade for the maintenance surface it adds.
The realistic goal is not zero known vulnerabilities. It is knowing what you run, being able to update quickly, and having tests good enough that updating is not frightening.
Logging enough to answer the question later
During an incident the questions are always the same: who did this, when, from where, and what else did they touch. If your logs cannot answer those, the incident lasts days instead of hours.
Log the security-relevant events specifically, separately from application debug output: authentication successes and failures, password and multi-factor changes, permission changes, data exports, API key creation and use, and administrative actions including impersonation.
Each entry wants an actor, a timestamp, a source address, the affected object, and the outcome. Retain long enough to be useful, which is longer than most default retention windows, because breaches are typically discovered weeks after they begin.
And keep the security log somewhere an application compromise cannot rewrite. A log an attacker can edit is not evidence.
How this fits the rest of the stack
None of this is expensive in money, and all of it is expensive in attention, which is why the pragmatic move is to reduce the number of places attention is required. Managed databases with private networking, environment variables handled by the platform, and certificates that renew themselves each remove a class of mistake rather than mitigating it. The RunxBuild hosting calculator shows what that shape costs, which is the useful comparison against the hours a self-managed equivalent takes every month.
Useful related references:
- Cloud Computing Applications Worth Building in 2026
- RunxBuild vs Railway: App Platform Comparison
- SaaS Meaning: The Definition and What It Implies Technically
- Services on RunxBuild
FAQ
What is the most common cause of SaaS security incidents?
Credential compromise and access control mistakes, not novel exploits. A leaked API key, a reused password without multi-factor authentication, or an endpoint that reads an identifier from the request without checking ownership account for the large majority of real incidents in applications of this shape.
How do I test that tenant isolation actually works?
Write an automated test that authenticates as one tenant and requests a known object belonging to another, then run it against every resource endpoint. It should always receive a 404 or 403. This single test class catches the highest-severity bug a multi-tenant application can ship.
Should session tokens go in cookies or local storage?
Cookies, marked HttpOnly, Secure and SameSite. A token in local storage is readable by any JavaScript on the page, so a single cross-site scripting bug or one compromised dependency becomes account takeover. An HttpOnly cookie is not reachable from script at all.
How often should I rotate secrets?
Scheduled rotation matters less than the ability to rotate quickly. Build the capability first so that rotating a credential takes minutes and does not require a code change, then rotate on a schedule that suits the sensitivity. A key you can rotate immediately turns a leak into an afternoon rather than a week.
Do I need SOC 2 to sell to businesses?
Increasingly yes above a certain deal size, and it is a compliance framework rather than a security one. It asks whether you have documented controls and follow them consistently. The controls in this article are most of the technical substance; the certification adds the documentation and the audit.