Protecting Against Common Vulnerabilities in Clojure

Learn the high-value defenses for SQL injection, XSS, CSRF, and related web weaknesses in Clojure applications, with an emphasis on boundaries rather than checkbox security.

Common web vulnerabilities: Recurring weaknesses such as SQL injection, cross-site scripting (XSS), and cross-site request forgery (CSRF) that appear when untrusted input crosses a boundary without the right controls.

Clojure gives you some structural advantages: immutable data, explicit data flow, and a culture of small pure functions. None of that removes the need to defend request boundaries, query boundaries, rendering boundaries, or browser-state boundaries. Most web security bugs still happen where untrusted data changes meaning.

SQL Injection Is a Query-Boundary Failure

SQL injection happens when attacker-controlled input is allowed to change the meaning of a database command. The primary defense is parameterization, not string filtering.

1(ns myapp.user-store
2  (:require [next.jdbc :as jdbc]))
3
4(defn find-user-by-email [ds email]
5  (jdbc/execute-one! ds
6    ["select user_id, email, status from users where email = ?" email]))

The important part is not the library name. It is the boundary rule:

  • SQL structure stays in the query text
  • untrusted values travel as parameters
  • callers do not get to splice raw fragments into the statement

Validation still matters for business rules, but OWASP is explicit that validation is not the primary SQL injection defense. Parameterized queries are.

XSS Is a Rendering-Boundary Failure

XSS happens when untrusted content becomes executable browser code. The primary defense is context-aware output encoding and safe templating defaults.

In practical Clojure web code, that means:

  • let the template layer escape plain text by default
  • use raw HTML insertion only for carefully reviewed trusted content
  • encode for the actual output context: HTML text, attribute, URL, JavaScript, or CSS
  • add a Content Security Policy (CSP) so one missed escape is less catastrophic

The biggest mistake here is thinking “we validated the input” is enough. Validation checks whether data is acceptable. It does not automatically make that data safe in an HTML or JavaScript context.

CSRF Exploits Browser State, Not Just Weak Handlers

CSRF matters when a browser automatically sends authenticated state such as cookies and the application accepts a forged state-changing request.

For classic server-rendered or cookie-backed Clojure apps, the usual controls are:

  • anti-forgery tokens
  • SameSite cookie settings
  • checking origin and referer where appropriate
  • keeping unsafe actions off GET
1(ns myapp.web
2  (:require [ring.middleware.anti-forgery :refer [wrap-anti-forgery]]))
3
4(def app
5  (wrap-anti-forgery handler))

That middleware is only part of the story. You still need routes and forms that treat write actions as privileged, intentional operations.

Security Headers Reduce Blast Radius

Even when the main bug is elsewhere, browser-facing headers matter:

  • Content-Security-Policy helps contain XSS
  • Strict-Transport-Security reinforces HTTPS use
  • X-Content-Type-Options: nosniff reduces content-type confusion
  • Referrer-Policy limits accidental data leakage

These do not replace application security, but they make common failures less exploitable.

Dependency Hygiene and Default Configurations Matter Too

Many production vulnerabilities are not in handwritten request handlers at all. They live in:

  • stale dependencies
  • unsafe defaults left enabled
  • admin consoles exposed to the public internet
  • debug routes and verbose stack traces in production

For Clojure services, that means reviewing not only application code but also:

  • JVM and base-image patch levels
  • reverse proxy and ingress configuration
  • Clojure and Java library updates
  • secrets handling and environment configuration

Common Failure Modes

Treating Input Validation as the Universal Defense

Validation is important, but it does not replace parameterized queries, output encoding, or CSRF protections.

Assuming the Front End Already Enforced the Rule

The browser is not the security boundary. The backend must still validate, authorize, and encode correctly.

Leaving State-Changing Browser Flows Without CSRF Protection

Cookie-backed apps need explicit CSRF controls even when the handlers look small and harmless.

Thinking Framework Use Equals Secure Defaults Everywhere

Frameworks help, but XSS, SQL injection, and browser-state bugs still appear when developers bypass the safe path.

Practical Heuristics

Protect the boundary where data changes meaning. Use parameterized queries for SQL, context-aware output encoding for browser rendering, and anti-forgery protections for cookie-backed state changes. Then add security headers, dependency hygiene, and operational review so one missed control does not become a full compromise. In Clojure, security improves most when the code makes trust boundaries obvious instead of pretending user input is just another map.

Ready to Test Your Knowledge?

Loading quiz…
Revised on Thursday, April 23, 2026