Back to Blog

Engineering

The Security Checklist We Apply to Every SaaS Product We Build

From authentication to data encryption, the 40-point security framework that's protected our clients' applications and their users' data across 100+ production deployments.

5 min read
VS

Viktor Sorokin

AI Engineering Lead · Dec 18, 2025

Share
The Security Checklist We Apply to Every SaaS Product We Build

Key Takeaways

  • Security isn't a feature you add at the end - it's a constraint that shapes every architectural decision from day one
  • The top 3 vulnerabilities we find in client codebases: insecure direct object references (IDOR), SQL injection in search/filter endpoints, and missing rate limiting on authentication
  • HTTPS everywhere, parameterized queries always, input validation on every endpoint - these three practices alone prevent 60% of common web vulnerabilities
  • Dependency scanning catches vulnerabilities you didn't write - run it in CI on every build, not quarterly as an afterthought
  • Security incident response plans should exist before you need them - the worst time to write a playbook is during an active breach

In 100+ production deployments, we've never had a client suffer a data breach. That's not luck - it's process. Every application we build goes through a 40-point security checklist that covers authentication, authorization, data protection, infrastructure, and incident response. This article covers the most critical items and why they matter.

Authentication: the front door. Authentication is the most attacked surface of any web application. Our minimum standards: passwords hashed with bcrypt (cost factor 12+) or Argon2id, multi-factor authentication available for all users and required for admin accounts, session tokens with appropriate expiration (24 hours for web, 30 days for mobile with refresh), account lockout after 5 failed attempts with exponential backoff, and password reset tokens that expire in 1 hour and are single-use. We've audited applications where passwords were stored in plaintext, sessions never expired, and there was no brute-force protection. These aren't edge cases - they're common mistakes that create catastrophic risk.

Authorization: the most commonly broken security control. Authorization vulnerabilities - specifically Insecure Direct Object References (IDOR) - are the most common security issue we find in client code reviews. The pattern: a user can access /api/orders/123 and see their order. But can they access /api/orders/124 - someone else's order? If the API doesn't verify that the requesting user owns the resource, the answer is yes. Every API endpoint that returns user-specific data must verify ownership. Every admin endpoint must verify admin role. Every team endpoint must verify team membership. No exceptions.

Input validation: trust nothing from the client. Every piece of data that crosses the network boundary - form submissions, query parameters, path parameters, headers, cookies - is potentially malicious. Our validation approach: whitelist validation (define what's allowed, reject everything else), parameterized queries for all database operations (never concatenate user input into SQL), input length limits on every field (a name field that accepts 10MB of text is a DoS vector), content-type validation on file uploads (check the actual file content, not just the extension), and output encoding to prevent XSS (escape all user-generated content before rendering in HTML).

Data protection at rest and in transit. HTTPS everywhere - no exceptions, no mixed content, no HTTP fallback. HSTS headers with a 1-year max-age. TLS 1.2 minimum (1.3 preferred). For data at rest: encrypt sensitive fields at the application level (not just database-level encryption), use envelope encryption with key rotation, and never store encryption keys alongside encrypted data. PII (names, emails, addresses) should be encrypted. Financial data should be encrypted. Health data should be encrypted. When in doubt, encrypt.

Dependency security: the vulnerabilities you didn't write. Modern applications have hundreds of dependencies, each with their own security history. Our approach: automated dependency scanning (npm audit, Snyk, or Dependabot) runs on every CI build, critical vulnerabilities fail the build immediately, high vulnerabilities must be patched within 1 week, and we audit new dependencies before adding them (checking maintenance status, download count, known vulnerabilities, and license compatibility). We've caught critical vulnerabilities in popular packages before they were widely reported - because automated scanning runs on every single commit.

Rate limiting and abuse prevention. Every public-facing endpoint needs rate limiting. Authentication endpoints get aggressive limits (5 attempts per minute per IP). API endpoints get reasonable limits (100-1000 requests per minute depending on the endpoint's cost). Search endpoints get moderate limits (30 per minute - search is computationally expensive). File upload endpoints get strict limits (10 per minute with size caps). Rate limit responses include Retry-After headers so legitimate clients can back off gracefully. Without rate limiting, your application is an open invitation for brute-force attacks, credential stuffing, and resource exhaustion.

Logging and monitoring for security events. You can't respond to attacks you can't see. Our minimum logging requirements: all authentication events (login success, login failure, password reset, MFA triggers), all authorization failures (someone tried to access something they shouldn't), all admin actions (with before/after state for audit), rate limit violations, and input validation failures (which often indicate probing). These logs go to a centralized monitoring system with alerting rules: 10+ failed logins from the same IP in 5 minutes triggers an alert. 5+ authorization failures from the same user triggers an alert. Any admin action on a production database triggers a notification.

Security headers that cost nothing to implement. Every application we ship includes these HTTP security headers: Content-Security-Policy (prevents XSS by controlling which scripts can execute), X-Content-Type-Options: nosniff (prevents MIME-type sniffing), X-Frame-Options: DENY (prevents clickjacking), Referrer-Policy: strict-origin-when-cross-origin (limits referrer information leakage), and Permissions-Policy (disables browser features your app doesn't use - camera, microphone, geolocation). These headers take 10 minutes to configure and prevent entire categories of attacks.

Incident response: plan before you need it. Every project we deliver includes a security incident response plan: who gets notified first (engineering lead, then CTO, then legal), how to assess severity (data breach vs. vulnerability vs. attempted attack), how to contain the incident (kill switch for affected features, not the entire application), how to communicate to affected users (template emails, timeline commitments), and how to conduct a post-mortem (root cause, timeline, remediation, prevention). The worst time to figure out your incident response process is during an active incident.

Security as a continuous practice, not a one-time audit. Security isn't something you achieve and forget. Our ongoing security practices: quarterly dependency audits, semi-annual penetration testing (automated + manual), annual security architecture review, continuous monitoring of security advisories for our technology stack, and regular security training for the development team. The threat landscape evolves constantly. Your security practices need to evolve with it.

Enjoyed this article?
Share
VS

Written by

Viktor Sorokin

AI Engineering Lead at Commit4Solutions Private

Newsletter

Engineering insights, delivered monthly

Join 2,400+ engineers and technical leaders who get our best articles on architecture decisions, team scaling, and production lessons - before they hit the blog.

No spam. Unsubscribe anytime. We respect your inbox.

Ready to build something great?