
Web security fundamentals: stopping XSS, CSRF and injection
The web security basics every application needs: preventing XSS, CSRF and SQL injection, Content Security Policy, security headers and safe dependencies.
In short: Most successful attacks on web applications exploit well-known weaknesses: injected scripts (XSS), forged requests (CSRF), SQL injection, broken access control and outdated dependencies. The countermeasures are proven: escape output consistently, use parameterised database queries, SameSite cookies and CSRF tokens, a Content Security Policy, server-side permission checks and regular updates. Web security is therefore mostly a matter of good habits in everyday development.
Why the basics matter more than exotic attacks
The OWASP Top 10, a widely used list of the most common web application risks, has shown a similar picture for years: broken access control, injection flaws and misconfiguration rank near the top. Sophisticated zero-day attacks are the exception. Most of these mistakes can be avoided with discipline.
For a small or mid-sized business, that means you do not need your own security team to rule out most risks. You need development that builds these basics in from the start.
Cross-site scripting (XSS)
With XSS, an attacker manages to inject their own JavaScript into your page, for example through a comment field or a manipulated link. The script then runs in other users' browsers and can steal session data or perform actions on their behalf.
Escape output
Frameworks like React escape content by default when it is rendered in JSX. The danger lies in the exceptions: dangerouslySetInnerHTML, setting innerHTML directly, or links with javascript: URLs taken from user input. If you really need HTML from user input, for example from a rich text editor, sanitise it with a library such as DOMPurify.
Content Security Policy
A Content Security Policy (CSP) is an HTTP header that defines where scripts, styles and other resources may be loaded from. A strict CSP with nonces or hashes stops injected inline scripts from running even if an XSS hole exists. That makes it an important second line of defence.
Cross-site request forgery (CSRF)
With CSRF, a third-party website gets a logged-in user's browser to send a request to your application, such as a payment or a password change. The browser attaches the session cookies automatically.
Modern browsers default cookies to SameSite=Lax, which already blocks many CSRF attacks. Do not rely on that alone: set SameSite explicitly, never use GET requests for state-changing actions and add CSRF tokens to forms. Many frameworks include this protection; Next.js Server Actions, for example, check the origin of the request.
SQL injection and other injection attacks
SQL injection happens when user input is inserted directly into a database query. An attacker can then run their own SQL and read or delete data.
The fix has been known for a long time: parameterised queries (prepared statements). ORMs and query builders such as Prisma, Drizzle or Knex use them by default. Be careful with raw queries built through string concatenation. The same principle applies to other kinds of injection, such as operating system commands or NoSQL queries: never pass input unchecked into anything that gets executed.
Access control, authentication and configuration
Check permissions on the server
A common mistake: the interface hides buttons, but the API never checks whether the user is allowed to perform the action. Anyone who sends the request manually gets through. Check every request on the server, including object ownership: is this user allowed to see this specific invoice? Our article on OAuth, JWT and sessions covers login and sessions, and API design best practices covers the interface side.
Security headers and HTTPS
Besides CSP, a few headers belong in every setup: Strict-Transport-Security enforces HTTPS, X-Content-Type-Options: nosniff stops browsers from misinterpreting files, and frame-ancestors in the CSP protects against clickjacking. Session cookies should be set with HttpOnly and Secure.
Dependencies and secrets
Modern applications are largely made of third-party packages. Tools like npm audit, Dependabot or Renovate report known vulnerabilities and keep dependencies current. API keys and passwords belong in environment variables or a secret manager, never in the repository.
Security and data protection go hand in hand. Our guide to GDPR-compliant software development explains what GDPR requires from development.
Frequently asked questions (FAQ)
Is my application secure because it uses React?
React protects well against the most common XSS mistakes because output is escaped automatically. It does nothing against CSRF, SQL injection or missing permission checks in the backend. Security comes from frontend, backend and infrastructure working together.
Do I need a penetration test?
For applications with sensitive data, payments or many users, an external penetration test makes sense, ideally before launch and after major changes. It checks your security basics; it cannot replace them.
How often should dependencies be updated?
Continuously. Automated tools like Dependabot or Renovate propose updates, and critical security fixes should be applied within days. Putting updates off for months makes the eventual upgrade much harder.
Is a web application firewall enough?
A WAF catches known attack patterns and is a useful addition. It does not know your application's logic, though, and cannot detect missing permission checks. It is an extra layer, not a substitute for secure code.
Conclusion
Most security holes in web applications are known and avoidable. Escape output, parameterise queries, check permissions on the server and keep dependencies current, and you close most real attack paths long before a penetration test becomes necessary.
If you are planning a web application with high security requirements or want an existing one reviewed, talk to us about web development.
Related posts
- React vs. Vue vs. Svelte: A Frontend Framework Comparison
React, Vue or Svelte for your next web project? A sober comparison of concepts, ecosystem, performance, hiring and long-term maintainability.
- Why we chose Next.js as our default web framework
Why we build web apps with Next.js by default: rendering options, SEO, TypeScript, self-hosting in Germany, and when we deliberately choose otherwise.
- Testing React Applications: Unit, Integration and E2E Tests
How to test React applications sensibly: which tests pay off, which tools fit, and how a test strategy catches bugs early instead of slowing the team down.
- Clean code in TypeScript: principles for maintainable projects
Practical clean code rules for TypeScript: strict types, clear names, small functions and validated boundaries, with examples and the mistakes to avoid.