Web Vulnerabilities & Defenses
Based on OWASP Top 10 โ the most critical web application security risks.
A01 โ Broken Access Controlโ
The #1 risk. Restrictions on what users can do are not properly enforced server-side.
Attack Examplesโ
# IDOR โ Insecure Direct Object Reference
GET /api/orders/1234 โ User A's order
GET /api/orders/1235 โ User B's order (attacker increments ID)
# Privilege escalation via mass assignment
PUT /api/users/42 { "role": "ADMIN" } โ Regular user sets own role
# Forced browsing
GET /admin/dashboard โ No role check on backend
Defensesโ
@GetMapping("/api/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId,
@AuthenticationPrincipal UserDetails user) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new ResourceNotFoundException("Order not found"));
// ALWAYS verify ownership server-side โ never trust client-provided user ID
if (!order.getUserId().equals(((AppUser) user).getId())
&& !user.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
// Return 404 (not 403) to avoid confirming the resource exists
throw new ResourceNotFoundException("Order not found");
}
return order;
}
// Deny by default
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated() // Default: require auth
);
// Use UUIDs in public-facing IDs instead of sequential integers
@Entity
public class Order {
@Id Long internalId;
String publicId = UUID.randomUUID().toString(); // Exposed in API
}
A02 โ Cryptographic Failuresโ
Sensitive data exposed due to weak or missing cryptography.
Attack Examplesโ
- Passwords stored in plaintext or MD5
- Sensitive data transmitted over HTTP
- Weak TLS configurations (TLS 1.0, weak ciphers)
- Hardcoded secrets in source code
Defensesโ
// Encrypt PII at rest using JPA converter
@Converter
public class EncryptedStringConverter implements AttributeConverter<String, String> {
@Autowired private AesEncryptionService aes;
@Override
public String convertToDatabaseColumn(String attribute) {
return attribute != null ? aes.encrypt(attribute) : null;
}
@Override
public String convertToEntityAttribute(String dbData) {
return dbData != null ? aes.decrypt(dbData) : null;
}
}
@Entity
public class UserProfile {
@Convert(converter = EncryptedStringConverter.class)
private String ssn;
@Convert(converter = EncryptedStringConverter.class)
private String phoneNumber;
}
A03 โ SQL Injectionโ
// โ VULNERABLE โ string concatenation
String query = "SELECT * FROM users WHERE email = '" + email + "'";
// Attacker input: ' OR '1'='1 โ Returns ALL users
// โ
SAFE โ parameterized queries via Spring Data JPA
Optional<User> findByEmail(String email); // Auto-parameterized
// โ
SAFE โ JPQL with named parameters
@Query("SELECT u FROM User u WHERE u.email = :email AND u.active = true")
Optional<User> findActiveByEmail(@Param("email") String email);
// โ
SAFE โ JdbcTemplate
jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE email = ?",
userRowMapper, email
);
// โ
Dynamic ORDER BY โ whitelist approach (column names can't be parameterized)
private static final Set<String> ALLOWED_SORT = Set.of("name", "email", "created_at");
public List<User> findUsers(String sortBy) {
if (!ALLOWED_SORT.contains(sortBy)) {
throw new IllegalArgumentException("Invalid sort column");
}
return jdbcTemplate.query("SELECT * FROM users ORDER BY " + sortBy, userRowMapper);
}
Defense in depth: DB user should have only SELECT/INSERT/UPDATE permissions โ never DROP.
A04 โ Insecure Designโ
Security not considered in the design phase.
Examplesโ
- No rate limiting on login โ brute force possible
- Password reset link valid forever
- Security questions as MFA factor
- No fraud detection on financial transactions
Defenseโ
- Threat modeling before building (STRIDE framework)
- Security requirements in every user story
- Defense-in-depth: multiple controls, not just one
A05 โ Security Misconfigurationโ
// โ Spring Boot Actuator exposed without auth
// GET /actuator/env โ leaks ALL environment variables including secrets
// GET /actuator/heapdump โ dumps full JVM heap โ secrets extractable
// โ
Restrict actuator
management:
endpoints:
web:
exposure:
include: health,info
endpoint:
health:
show-details: when-authorized
server:
port: 8081 # Separate internal port
// โ
Generic error responses โ never leak internals
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleUnexpected(Exception ex) {
log.error("Unexpected error", ex); // Full details in server logs only
return ResponseEntity.status(500)
.body(new ErrorResponse("An unexpected error occurred")); // Generic to client
}
}
A07 โ Authentication Failuresโ
// Account lockout after N failed attempts
@Service
public class LoginAttemptService {
private final int MAX_ATTEMPTS = 5;
private final Duration LOCKOUT = Duration.ofMinutes(15);
public void recordFailure(String username) {
String key = "login_fail:" + username;
Long count = redis.opsForValue().increment(key);
if (count == 1) redis.expire(key, LOCKOUT);
if (count >= MAX_ATTEMPTS) {
redis.opsForValue().set("locked:" + username, "1", LOCKOUT);
}
}
public boolean isLocked(String username) {
return Boolean.TRUE.equals(redis.hasKey("locked:" + username));
}
}
A10 โ SSRF (Server-Side Request Forgery)โ
# Feature: import image from URL
POST /api/import { "imageUrl": "http://169.254.169.254/latest/meta-data/iam/credentials" }
# Server fetches AWS EC2 metadata โ leaks IAM credentials!
@Service
public class SafeHttpClient {
private static final Set<String> BLOCKED_HOSTS = Set.of(
"169.254.169.254", "metadata.google.internal",
"localhost", "127.0.0.1", "::1", "0.0.0.0"
);
public byte[] fetchExternalResource(String urlString) throws Exception {
URL url = new URL(urlString);
if (!Set.of("https").contains(url.getProtocol())) {
throw new SecurityException("Only HTTPS URLs allowed");
}
InetAddress address = InetAddress.getByName(url.getHost());
if (BLOCKED_HOSTS.contains(url.getHost())
|| isPrivateAddress(address)) {
throw new SecurityException("Access to internal resources denied");
}
return restTemplate.getForObject(urlString, byte[].class);
}
private boolean isPrivateAddress(InetAddress addr) {
return addr.isLoopbackAddress() || addr.isLinkLocalAddress()
|| addr.isSiteLocalAddress() || addr.isAnyLocalAddress();
}
}
XSS โ Cross-Site Scriptingโ
| Type | Persistence | Source |
|---|---|---|
| Stored | DB | Comment, profile bio โ highest impact |
| Reflected | URL parameter | Search query reflected in response |
| DOM-based | Client-side only | JS reads URL, writes to DOM |
// โ
Thymeleaf auto-escapes โ use th:text, not th:utext
// <p th:text="${userComment}">...</p>
// โ
Content Security Policy
http.headers(headers -> headers
.contentSecurityPolicy(csp -> csp.policyDirectives(
"default-src 'self'; " +
"script-src 'self'; " +
"object-src 'none'; " +
"frame-ancestors 'none'"
))
);
// โ
Sanitize user-provided HTML (rich text editor)
PolicyFactory policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
String safeHtml = policy.sanitize(userProvidedHtml);
CSRF โ Cross-Site Request Forgeryโ
<!-- On attacker's site โ victim's browser auto-sends session cookie -->
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="to" value="attacker">
</form>
<script>document.forms[0].submit();</script>
// โ
SameSite=Lax or Strict cookie โ browser won't send cross-site
// โ
For traditional web apps โ synchronizer CSRF token
http.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
// SPA reads XSRF-TOKEN cookie, sends as X-XSRF-TOKEN header
// โ
For stateless JWT APIs โ CSRF is NOT needed
// JWT is sent via Authorization header (not a cookie), browser won't auto-send it
http.csrf(AbstractHttpConfigurer::disable); // OK for JWT-only APIs
Interview Questionsโ
- What is SQL injection and how do you prevent it in Spring Boot?
- What is the difference between Stored XSS, Reflected XSS, and DOM-based XSS?
- What is CSRF? When does it NOT apply (JWT + stateless API)?
- What is SSRF? Give a cloud metadata attack example.
- What is an IDOR vulnerability? Give a real-world example.
- What HTTP security headers should every web application include?
- Why is Java object deserialization dangerous?
- What is the purpose of Content Security Policy (CSP)?
- How does clickjacking work and what prevents it?
- What should and should not be included in error messages returned to clients?
- How do you detect and defend against credential stuffing attacks?
- What is the difference between
403and404when returning an unauthorized resource access response โ and why might you prefer404?