Your framework is doing more security work than you probably give it credit for. Stay inside the patterns it expects, and a lot of protection comes for free. Step outside them, reach for a raw query or hand-built HTML, and you quietly opt out of that protection without any warning. So the skill here isn't memorising every default. It's knowing three things for each framework: what it protects by default, where the escape hatches are, and what it leaves entirely to you.
XSS protection
Every major framework auto-escapes template output. Every one of them also ships a way to turn that off.
| Framework | Default | The escape hatch |
|---|---|---|
| React / Next.js | JSX auto-escapes | dangerouslySetInnerHTML; href/src don't check schemes |
| Vue / Nuxt | {{ }} auto-escapes | v-html renders raw |
| Angular | Sanitises DOM bindings | bypassSecurityTrustHtml() |
| Django | Templates auto-escape | ` |
| Rails ERB | <%= %> auto-escapes | raw() and html_safe |
| Thymeleaf | th:text escapes | th:utext is unescaped |
SQL injection
Same story: the ORM parameterises by default, and there's always a door back to raw SQL.
| Framework | Default | The escape hatch |
|---|---|---|
| Django ORM | Parameterised | raw(), extra() |
| ActiveRecord | Parameterised | find_by_sql, where("name = '#{name}'") |
| SQLAlchemy | Parameterised | text() with string formatting |
| Sequelize | Parameterised | sequelize.query() misused |
| Prisma | Parameterised via tagged-template $queryRaw | $queryRawUnsafe, or $queryRaw built by concatenation |
CSRF
Here the defaults genuinely diverge, so this is one to check rather than assume.
| Framework | Default | The gap |
|---|---|---|
| Django | Middleware on | @csrf_exempt |
| Rails | protect_from_forgery on | APIs often skip it |
| Next.js / Express | Nothing built in | You add it yourself |
Authentication
| Framework | What you get |
|---|---|
| Django | Built-in auth, sessions, CSRF |
| Rails | Devise is common, but not built in |
| Next.js | Nothing built in; NextAuth.js is common |
| Express | Nothing built in; Passport.js is common |
The pattern to notice: the more you assemble auth yourself, as you do in Node and Express, the more places there are to misconfigure it.
What no framework does for you
A few things are almost always left to you, so treat them as a standing checklist:
- Security headers. Most frameworks don't set HSTS, CSP, or X-Frame-Options by default. Add them through middleware (Django's
SecurityMiddleware, Helmet.js for Express). - File uploads. No framework gives you safe upload handling out of the box. Validate content, generate your own filenames, store outside the web root.
- Production hardening. Never run debug mode in production, change the default secret keys, and lock down admin endpoints.
The single most dangerous assumption you can make is that the framework handles everything. It handles a lot. Know exactly where "a lot" stops.
