Top security mistakes Saas founders make
You have developed some Saas service, but not sure is it safety to deploy it to live? Here is must have checklist, which will help you to avoid most common mistakes. Especially usefull for solopreneurs and vibe-coders.

You built your SaaS, it works, and you are ready to ship. Before you point the domain at production, take 30 minutes to run through this list. These are the 15 mistakes that get early-stage founders hacked — not because the attacks are sophisticated, but because the fixes are easy to skip when you are moving fast.
Mistake 1 — Secrets committed to git
You should be extra careful about where you store secrets for your application. Once committed to GitHub, there is no option to erase them from history — secrets stay forever, and the only fix is to rotate them immediately.
For an early-stage SaaS, there are a few secure and inexpensive ways to store them:
The fix: 1. Keep secrets in a local
.envfile on your production server — never committed to git. 2. Inject them at deploy time using GitHub Actions secrets, AWS Secrets Manager, or Doppler. If you already committed a secret — rotate it first, then clean up the history.
Mistake 2 — Missing authorization: BOLA / IDOR
Authentication (proving who you are) and authorization (proving you can access this resource) are different checks. Many early apps verify the first and skip the second.
Example: User A is authenticated and requests GET /api/invoices/1042. That invoice belongs to User B. If your code only checks "is the user logged in?" — User A gets User B's data.
The fix: Every time your code looks something up by ID, also check that the record belongs to the requesting user. Write a test where one user tries to access another user's resource — if it succeeds, you have the bug.
Mistake 3 — JWT accepted without signature verification
JWTs have three parts: header, payload, and signature. The signature is what makes them trustworthy. Some libraries, when misconfigured, accept alg: none — meaning no signature at all. An attacker can forge any token with any claims and your server will trust it.
The fix: Use a well-maintained JWT library and always verify the signature against a secret or public key. Explicitly reject the
nonealgorithm. Never parse the payload before the signature is verified.
Mistake 4 — JWT stored in localStorage
localStorage is accessible to any JavaScript running on your page — including scripts injected via XSS. Storing your auth token there means a single XSS vulnerability exposes every active session.
The fix: Store tokens in
HttpOnlycookies. The browser sends them automatically and JavaScript cannot read them, so XSS cannot steal them. SetSecureandSameSite=StrictalongsideHttpOnly.
Mistake 5 — No rate limiting on auth endpoints
Without rate limiting, an attacker can try thousands of password combinations against your login endpoint in minutes, or flood your password reset to enumerate valid emails. Auth endpoints are the highest-value targets and the most common place to skip this protection.
The fix: Add rate limiting to
/login,/register,/reset-password, and any OTP or email verification endpoint. A limit of 5–10 requests per minute per IP is a reasonable starting point. Useexpress-rate-limit, Nginxlimit_req, or a gateway-level rule.
Mistake 6 — Overly permissive CORS
Access-Control-Allow-Origin: * with credentials is rejected by browsers — but a wildcard origin with no credentials still lets any site read your API responses. A sloppy allowlist that reflects the Origin header back unconditionally is just as dangerous.
The fix: Explicitly whitelist only the origins that need access (your frontend domain). In production, verify the allowlist is environment-specific — dev origins must never appear in the prod config.
Mistake 7 — SQL injection / DB corruption
String-concatenating user input into SQL queries lets attackers read your entire database, modify records, or drop tables. It is the oldest vulnerability in web development and still one of the most common.
Example: "SELECT * FROM users WHERE email = '" + email + "'" — if email is ' OR '1'='1, every user record is returned.
The fix: Always use parameterized queries or a well-maintained ORM. Never build query strings by concatenating user input. Validate and sanitize input at the system boundary even when using an ORM.
Mistake 8 — Secrets leaked in the client bundle
In Next.js, any environment variable prefixed with NEXT_PUBLIC_ is bundled into client-side JavaScript and visible to anyone who opens DevTools. Server-only variables can still leak if accidentally passed to a component or API response.
The fix: Never put secret keys, internal service URLs, or sensitive config in
NEXT_PUBLIC_variables. Server-only secrets belong in server components, API routes, or backend services — never in anything that reaches the browser.
Mistake 9 — Weak password hashing (MD5 / SHA-1)
MD5 and SHA-1 are fast — designed for checksums, not passwords. A modern GPU can compute billions of MD5 hashes per second, making brute-force attacks trivial. If your database leaks, weak hashes give attackers plaintext passwords within hours.
The fix: Use
bcrypt,argon2, orscryptwith an appropriate cost factor (bcrypt work factor ≥ 12). These are intentionally slow and defeat brute-force at scale. Never use fast hashes for passwords.
Mistake 10 — Missing security headers
Security headers are free, take minutes to add, and prevent entire classes of attacks. Without them your app is vulnerable to clickjacking, MIME-sniffing exploits, and content injection.
The most important ones:
Content-Security-Policy— controls what scripts and resources the browser can loadX-Frame-Options: DENY— prevents clickjacking via iframesX-Content-Type-Options: nosniff— stops MIME-type sniffing attacksStrict-Transport-Security— forces HTTPS on return visitsReferrer-Policy: strict-origin-when-cross-origin— limits referrer data leakage
The fix: Add these in your web server config, Next.js
next.config.jsheaders block, or a middleware layer. Check what is missing at securityheaders.com.
Mistake 11 — Management endpoints exposed publicly
Frameworks ship with debug and management endpoints: Spring Boot's /actuator, Node's /__health, admin panels at /admin, metrics at /metrics. Left public, they expose internals — environment variables, thread dumps, heap info — or allow dangerous operations with no auth required.
The fix: Block management endpoints at the network level (firewall, reverse proxy) or require authentication. In Spring Boot, restrict
/actuatorto an internal interface. Never expose these on the public port.
Mistake 12 — Verbose error messages in production
A stack trace in an API response tells an attacker your framework version, file structure, ORM, and sometimes the exact failing query. That is free reconnaissance.
The fix: Catch exceptions globally and return generic messages to clients (
"Something went wrong"). Log the full stack trace server-side where only you can see it. Never forward raw exception details to API responses in production.
Mistake 13 — No CSRF protection on API routes
If your app uses cookie-based sessions, a malicious website can trick a logged-in user's browser into making state-changing requests to your API. The browser automatically attaches the session cookie — your server sees an authenticated request it never originated.
The fix: Use
SameSite=StrictorSameSite=Laxon your session cookies — this is the simplest mitigation and supported in all modern browsers. For older compatibility, add CSRF token validation on all state-changing endpoints (POST, PUT, PATCH, DELETE).
Mistake 14 — Outdated or vulnerable dependencies
Old packages with known CVEs are documented, searchable, and exploitable by automated scanners. Lock files freeze specific versions, so you do not benefit from security patches unless you actively update.
The fix: Run
npm auditand address at least the high/critical findings before launch. Set up Dependabot or Renovate for automated security update PRs. For Java, add the OWASP Dependency Check plugin to your CI pipeline.
Mistake 15 — No audit logging for sensitive operations
Without logs you cannot answer "who changed this?", "when was this deleted?", or "did that payment actually process?". When something goes wrong — and it will — you are debugging blind.
The fix: Log who did what and when for every sensitive operation: user creation/deletion, role changes, payment processing, data exports, and admin actions. Include actor ID, timestamp, affected resource, and action taken. Store logs where your application cannot overwrite them.
AI Prompt for a full security audit
Here is a ready-made prompt our team uses to run a thorough AI-assisted security review. Paste your codebase files into any capable AI tool alongside it:
localStorage, sessionStorage, insecure cookies (missing HttpOnly, Secure, SameSite), or exposed in URLs.
5. No rate limiting on authentication and sensitive endpoints
Login, registration, password reset, OTP, and payment endpoints with no rate limiting at any layer.
---
### HIGH
6. Injection vulnerabilities
SQL, NoSQL, LDAP, OS command, or template injection. Any place where user-supplied input is concatenated into a query or command string.
7. Missing or insufficient input validation
Endpoints that accept request bodies or parameters without schema validation, type checking, or constraint enforcement.
8. Overly permissive cross-origin policy
CORS configured with wildcard origins combined with credentials, or overly broad origin allowlists that include non-production domains.
9. Sensitive data exposed to the client
Server-side secrets, environment variables, internal configuration, or backend infrastructure details passed to or rendered in the client.
10. Weak or broken password / credential storage
Passwords hashed with MD5, SHA-1, or any fast hash. Plaintext passwords. Missing work factor on bcrypt/argon2/scrypt.
---
### MEDIUM
11. Missing security headers
HTTP responses lacking CSP, X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security, or Referrer-Policy.
12. Verbose error messages and stack traces
Error responses that include stack traces, framework internals, database schema details, file paths, or library versions visible to the client.
13. Missing CSRF protection
State-changing endpoints using cookie-based sessions without CSRF token validation or SameSite cookies.
14. Overly privileged database or service accounts
Application connecting to databases using admin, root, or superuser credentials.
15. Debug mode or development configuration in production
Debug flags, verbose logging, development middleware, test routes, or seed data scripts present in production artifacts.
---
### LOW
16. Outdated or vulnerable dependencies
Lock files containing packages with known CVEs or significantly outdated versions of security-critical libraries.
17. Missing audit logging on sensitive operations
Functions that create, modify, or delete sensitive entities with no audit trail.
18. Missing field-level protection for PII
Model fields for SSN, tax ID, bank account numbers, or health data without encryption at rest.
19. Unrestricted file upload
File upload handlers that do not validate MIME type, enforce size limits, or store files in web-accessible directories.
20. No request size limits
API endpoints without configured limits on request body size.
---
### EXTRA — Advanced checks
21. Account enumeration — Different responses based on whether an account exists on login/reset flows.
22. Insecure direct rendering of user content — User-supplied content rendered as raw HTML without sanitization (dangerouslySetInnerHTML, innerHTML, etc.).
23. Refresh token / session reuse vulnerability — Token refresh logic that does not invalidate the previous token on use.
24. Insecure deserialization — User-controlled data passed into deserialization functions without field restrictions.
25. Server-side request forgery (SSRF) — Endpoints that accept a URL from the user and make a server-side HTTP request to it without an allowlist.
26. Missing method-level authorization — Business logic functions that modify privileged data reachable without role/permission checks at the function level.
27. Sensitive data in logs — Logging statements that output passwords, tokens, full card numbers, SSNs, or PII.
---
How to work through the audit
Step 1 — Discovery. Before asking for any code, ask me about the application's purpose, tech stack, authentication mechanism, and existing security tooling.
Step 2 — Config and dependency files first. Dependency manifests, lock files, environment/config files, web server config, auth config, CI/CD pipeline definitions.
Step 3 — Application code by layer. Auth and authorization logic → API route handlers → service/business logic → data access layer → frontend → background jobs → utilities.
Step 4 — Report as you go. After each batch of files, report findings immediately:
FINDING #[N]
Severity : [CRITICAL / HIGH / MEDIUM / LOW]
File : [path/to/file.ext : line N]
Issue : [one-line description]
Why : [why this is dangerous]
Vulnerable code: [exact snippet]
Fixed code: [corrected code in the same language]
Step 5 — Running tally. After every batch, update the count of CRITICAL / HIGH / MEDIUM / LOW findings.
Step 6 — Final report. Executive summary, full findings sorted by severity, prioritized remediation plan with estimated effort, and positive observations on controls already in place.
Ground rules: No false positives — flag uncertainty as "Needs manual review". No generic advice — every fix must match the exact language and framework. No assumptions — if you cannot determine whether a control exists without more files, ask for them first.
Ready. Please start with Step 1 and ask me the discovery questions.
Conclusion
It is not possible to be 100% secure at the early stage, but implementing these 15 fixes will make your system significantly more resilient. When your SaaS starts getting real customers, consider bringing in a professional for a penetration test — the cost is far lower than recovering from a breach.
All systems can be attacked. Your goal is to make the attack not worth the attacker's time.
Ready to Start Your Affiliate Journey?
Join AFFY today and start building your successful affiliate program.