Security Is Not a Feature, It Is a Prerequisite
In 2025, cyberattacks against SaaS applications are constantly increasing. SQL injection, XSS, CSRF, credential stuffing — the attack vectors are numerous and the consequences of a breach are devastating: data loss, GDPR fines, destroyed reputation.
The good news? The majority of these attacks can be prevented with established practices. Boilerplate-Stack integrates these protections natively, following OWASP recommendations.
Magic Links vs Passwords: The Right Choice in 2025
Passwords have been the weak link in web security for decades:
- Users reuse the same passwords everywhere
- Password databases are regularly compromised
- Secure storage (bcrypt, argon2) does not protect against phishing
- Password reset flow is an attack vector in itself
Magic links eliminate all these problems:
- No password to store — nothing to compromise in your database
- Email-based authentication — if the attacker has email access, they already have access to everything via "forgot password"
- Superior user experience — one click, no memorization
- OAuth compatible — add Google, GitHub alongside
In Boilerplate-Stack, magic links are sent via Brevo with customized templates in French and English.
Server-Side Authentication: getUser() vs getSession()
This is the most dangerous and most common mistake in Supabase development:
// DANGEROUS — the session can be forged client-side
const { data: { session } } = await supabase.auth.getSession()
// SECURE — validates the token with Supabase
const { data: { user } } = await supabase.auth.getUser()getSession() simply reads the cookie without validating it. An attacker can forge a session cookie. getUser() sends the token to Supabase for verification. Always use getUser() on the server side.
Row Level Security (RLS): Your Last Line of Defense
PostgreSQL RLS is the most powerful security mechanism in a Supabase architecture. It applies at the database level, independently of application code.
-- Every table filtered by membership
CREATE POLICY "members_access" ON chat_sessions FOR ALL
USING (EXISTS (
SELECT 1 FROM memberships
WHERE memberships.account_id = chat_sessions.account_id
AND memberships.user_id = auth.uid()
));Benefits of RLS:
- Defense in depth: even if an API route is compromised, data remains protected
- Impossible to bypass: applies to all queries, including joins
- Performance: PostgreSQL optimizes RLS policies like regular WHERE clauses
OWASP Top 10: Implemented Protections
1. Injection (SQL, NoSQL, OS)
Supabase uses parameterized queries natively. Never SQL string concatenation. Zod validation on all inputs.
2. Broken Authentication
Magic links + OAuth. No passwords. Strict rate limiting on auth endpoints (5 requests/minute).
3. Sensitive Data Exposure
RLS on every table. Explicit column selection (.select()). Never SELECT * exposed to the user.
4. XXE (XML External Entities)
Not applicable — no XML parsing. JSON API only.
5. Broken Access Control
API security middleware with levels (apiSecurity.public(), .authenticated(), .admin()). Role and membership verification.
6. Security Misconfiguration
Strict HTTP headers in next.config.ts:
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Strict-Transport-Security: max-age=630720007. Cross-Site Scripting (XSS)
React escapes by default. Strict Content Security Policy. Input sanitization with sanitize.html().
8. Insecure Deserialization
Zod validation on all inputs. No eval(), no arbitrary deserialization.
9. Using Components with Known Vulnerabilities
Up-to-date dependencies. Regular audit with npm audit.
10. Insufficient Logging
admin_logs table for administrative actions. ai_requests for usage tracking. credit_transactions for financial audit.
Multi-Tier Rate Limiting
Rate limiting is essential to prevent abuse:
strict: 5/min — sensitive operations (auth, deletion)
standard: 30/min — general API
relaxed: 100/min — public reads
ai: 10/min — AI requests
contact: 3/min — contact form
webhook: 100/min — external webhooksImplemented with Upstash Redis in production, with an in-memory fallback for development.
CSRF Protection
The Double Submit Cookie pattern protects against CSRF attacks:
- A random token is generated and stored in an httpOnly cookie
- The same token is sent in the request header
- The server verifies that both match
- Additional Origin and Referer validation
Bot Protection
Cloudflare Turnstile is integrated on sensitive forms:
- Contact form
- Newsletter signup
- Login page (optional)
Turnstile is invisible to the user in most cases, unlike traditional CAPTCHAs.
Do Not Start from Scratch for Security
Correctly implementing all these security layers takes weeks and requires deep expertise. A single mistake can compromise everything.
Boilerplate-Stack integrates all these protections natively. Every API route, every component, every database query is secured according to OWASP best practices.
Explore the complete implementation at boilerplate-stack.com and launch your SaaS with professional-grade security from day one.