ProCust
Web security fundamentals: stopping XSS, CSRF and injection

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.