Modern frameworks do a lot of security work for you — but only if you understand what they protect against and where the gaps are. This lesson maps the secure defaults in Rails, Django, Next.js, Spring Boot, and Express, and highlights where each framework leaves you on your own.
The framework contract
When you choose a framework, you are entering an implicit security contract: "use the framework as intended and we will protect you from common vulnerability classes." Break from the framework's patterns — raw queries, manual HTML construction, custom session handling — and you lose those protections.
Cross-site scripting (XSS)
| Framework | Default behaviour | Gap |
|---|---|---|
| React / Next.js | JSX auto-escapes all interpolated values | dangerouslySetInnerHTML bypasses encoding; href and src do not validate URL schemes |
| Vue / Nuxt | Template interpolation {{ }} auto-escapes | v-html directive renders raw HTML |
| Angular | Sanitises DOM bindings automatically | bypassSecurityTrustHtml() disables sanitisation |
| Django | Template engine auto-escapes by default | ` |
| Rails (ERB) | <%= %> auto-escapes | raw() and html_safe bypass encoding |
| Spring (Thymeleaf) | th:text auto-escapes | th:utext renders unescaped HTML |
Takeaway: Every framework provides an escape hatch for raw HTML. Know what it is. Use it only with sanitised content.
SQL injection
| Framework | Default behaviour | Gap |
|---|---|---|
| Django ORM | Parameterised queries | raw(), extra(), and string formatting in RawSQL() |
| Rails ActiveRecord | Parameterised queries | find_by_sql with string interpolation, where("name = '#{name}'") |
| SQLAlchemy | Parameterised queries via ORM | text() with string formatting, engine.execute() with raw SQL |
| Sequelize | Parameterised queries | sequelize.query() with replacements improperly used |
| Prisma | Parameterised by design | $queryRaw and $executeRaw with template literals (use Prisma.sql tagged template instead) |
| Spring Data JPA | Parameterised queries via JPQL | @Query with string concatenation, native queries without parameters |
Takeaway: ORMs protect you when you use their query builders. Raw query methods bypass protections. When raw SQL is necessary, always parameterise.
CSRF protection
| Framework | Default behaviour | Gap |
|---|---|---|
| Django | CSRF middleware enabled by default; tokens required for POST/PUT/DELETE | Must include {% csrf_token %} in forms; API views using @csrf_exempt are unprotected |
| Rails | CSRF protection enabled by default with protect_from_forgery | API-only controllers often skip CSRF; token must be included in AJAX requests |
| Next.js / Express | No built-in CSRF protection | You must add it yourself (use SameSite=Lax cookies as baseline; add CSRF tokens for sensitive actions) |
| Spring Boot | Spring Security includes CSRF by default | Often disabled for REST APIs; must be paired with SameSite cookies |
Takeaway: Django and Rails have CSRF protection out of the box. Node.js frameworks generally do not. SameSite=Lax cookies provide baseline protection in modern browsers, but tokens are still needed for sensitive state-changing operations in older configurations.
Authentication and session management
| Framework | Default behaviour | Gap |
|---|---|---|
| Django | Built-in auth system, sessions in database, CSRF tokens | Password hashing uses PBKDF2 by default (Argon2 available, not default) |
| Rails | Devise gem (common but not built-in), signed cookie sessions | Cookie sessions expose session data client-side (signed, not encrypted, by default in older versions) |
| Next.js | No built-in auth | NextAuth.js / Auth.js is the common choice; configuration is your responsibility |
| Spring Boot | Spring Security provides comprehensive auth | Complex configuration; easy to misconfigure role hierarchies |
| Express | No built-in auth | Passport.js is common; session config, cookie flags, and storage are all manual |
Takeaway: Django provides the most complete out-of-the-box authentication. Node.js and Express require you to assemble auth from components, which means more opportunities for misconfiguration.
Security headers
Most frameworks do not set security headers by default. You need to add them yourself:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'
Permissions-Policy: camera=(), microphone=(), geolocation=()
Some frameworks and middleware help:
- Django —
SecurityMiddlewaresets HSTS,X-Content-Type-Options, and others when configured - Rails — sets
X-Frame-Options,X-Content-Type-Options, andX-XSS-Protectionby default - Helmet.js (Express/Node.js) — middleware that sets a sensible set of security headers
- Spring Security — sets several security headers by default when enabled
File uploads
No major framework provides safe file upload handling by default. You are responsible for:
- Validating file content (not just the extension or MIME type)
- Sanitising filenames
- Storing files outside the web root
- Setting correct content types when serving
This is consistently a gap across all frameworks.
Default credentials and debug modes
| Risk | Frameworks affected |
|---|---|
| Debug mode in production | Django (DEBUG=True), Rails (config.consider_all_requests_local), Next.js (error overlay), Spring Boot (Actuator endpoints) |
| Default secret keys | Django (SECRET_KEY in settings.py template), Rails (secret_key_base), Express (session secret) |
| Admin panels | Django Admin is enabled by default in new projects; Spring Boot Actuator exposes management endpoints |
Takeaway: Always change default secret keys before deploying. Never run debug mode in production. Restrict or disable admin panels and management endpoints in production.
The framework checklist
When starting a new project with any framework, verify:
- Is output encoding enabled by default? Where are the escape hatches?
- Does the ORM parameterise queries? What are the raw query methods?
- Is CSRF protection built in? Is it enabled for your use case?
- What security headers are set? What is missing?
- What is the default session storage and cookie configuration?
- Are there any default credentials, secret keys, or debug settings that must be changed?
- How does the framework handle file uploads?
Summary
Frameworks provide significant security benefits — but only when used as intended. Auto-encoding, parameterised queries, and CSRF tokens protect you from the top vulnerability classes, but every framework has escape hatches. Know what your framework does by default, where the gaps are, and what you need to add yourself. The most dangerous assumption is that the framework handles everything.
