OWASP Top 10 Mitigation in Java

Introduction to OWASP Top 10

The OWASP (Open Web Application Security Project) Top 10 is a standard awareness document representing the most critical security risks to web applications. This comprehensive guide covers mitigation strategies for each vulnerability category using Java best practices.

1. Broken Access Control

Prevention Techniques

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.stereotype.Controller;
import javax.servlet.http.HttpSession;
@Controller
public class AccessControlMitigation {
// 1.1 Role-Based Access Control (RBAC)
@PreAuthorize("hasRole('ADMIN')")
public void adminOnlyMethod() {
// Only users with ADMIN role can access this method
}
@PreAuthorize("hasPermission(#userId, 'USER', 'READ')")
public User getUserProfile(String userId) {
// Users can only access their own profile
return userService.findUserById(userId);
}
// 1.2 Method-Level Security
@PreAuthorize("#user.id == authentication.principal.id")
public void updateUserProfile(User user) {
// Users can only update their own profile
userService.updateUser(user);
}
}
// Secure Configuration
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.sessionManagement()
.sessionFixation().migrateSession()
.maximumSessions(1)
.maxSessionsPreventsLogin(true);
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return (request, response, accessDeniedException) -> {
// Log access denied attempts
logger.warn("Access denied for user: " + 
request.getUserPrincipal().getName() + " to URL: " + request.getRequestURI());
response.sendError(HttpStatus.FORBIDDEN.value(), "Access Denied");
};
}
}
// Business Logic Access Control
@Service
public class OrderService {
public Order getOrder(String orderId, String currentUserId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
// Verify the current user owns this order
if (!order.getUserId().equals(currentUserId) && 
!hasAdminRole(currentUserId)) {
throw new AccessDeniedException("User cannot access this order");
}
return order;
}
private boolean hasAdminRole(String userId) {
// Check if user has admin role
return userService.getUserRoles(userId).contains("ROLE_ADMIN");
}
}

2. Cryptographic Failures

Secure Cryptography Implementation

import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class CryptographicSecurity {
// 2.1 Secure Password Hashing
public class PasswordService {
private static final int SALT_LENGTH = 32;
private static final int HASH_LENGTH = 64;
private static final int ITERATIONS = 210000;
public String hashPassword(String password) {
try {
// Generate random salt
SecureRandom random = new SecureRandom();
byte[] salt = new byte[SALT_LENGTH];
random.nextBytes(salt);
// Hash password with salt
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(), salt, ITERATIONS, HASH_LENGTH * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = skf.generateSecret(spec).getEncoded();
// Combine salt and hash
byte[] combined = new byte[salt.length + hash.length];
System.arraycopy(salt, 0, combined, 0, salt.length);
System.arraycopy(hash, 0, combined, salt.length, hash.length);
return Base64.getEncoder().encodeToString(combined);
} catch (Exception e) {
throw new SecurityException("Password hashing failed", e);
}
}
public boolean verifyPassword(String password, String storedHash) {
try {
byte[] combined = Base64.getDecoder().decode(storedHash);
byte[] salt = new byte[SALT_LENGTH];
byte[] storedPasswordHash = new byte[HASH_LENGTH];
System.arraycopy(combined, 0, salt, 0, SALT_LENGTH);
System.arraycopy(combined, SALT_LENGTH, storedPasswordHash, 0, HASH_LENGTH);
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(), salt, ITERATIONS, HASH_LENGTH * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] testHash = skf.generateSecret(spec).getEncoded();
return MessageDigest.isEqual(storedPasswordHash, testHash);
} catch (Exception e) {
throw new SecurityException("Password verification failed", e);
}
}
}
// 2.2 Secure Encryption
public class EncryptionService {
private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final int TAG_LENGTH = 128;
private static final int IV_LENGTH = 12;
public String encrypt(String plaintext, SecretKey key) {
try {
// Generate random IV
SecureRandom random = new SecureRandom();
byte[] iv = new byte[IV_LENGTH];
random.nextBytes(iv);
Cipher cipher = Cipher.getInstance(ALGORITHM);
GCMParameterSpec parameterSpec = new GCMParameterSpec(TAG_LENGTH, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
// Combine IV and ciphertext
byte[] encrypted = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, encrypted, 0, iv.length);
System.arraycopy(ciphertext, 0, encrypted, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new SecurityException("Encryption failed", e);
}
}
public String decrypt(String encryptedData, SecretKey key) {
try {
byte[] encrypted = Base64.getDecoder().decode(encryptedData);
// Extract IV and ciphertext
byte[] iv = new byte[IV_LENGTH];
byte[] ciphertext = new byte[encrypted.length - IV_LENGTH];
System.arraycopy(encrypted, 0, iv, 0, IV_LENGTH);
System.arraycopy(encrypted, IV_LENGTH, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance(ALGORITHM);
GCMParameterSpec parameterSpec = new GCMParameterSpec(TAG_LENGTH, iv);
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
byte[] plaintext = cipher.doFinal(ciphertext);
return new String(plaintext, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new SecurityException("Decryption failed", e);
}
}
}
// 2.3 Secure Key Management
@Component
public class KeyManagementService {
private final String KEY_ALIAS = "app-encryption-key";
private final KeyStore keyStore;
@PostConstruct
public void initialize() throws Exception {
keyStore = KeyStore.getInstance("JCEKS");
char[] password = System.getenv("KEYSTORE_PASSWORD").toCharArray();
// Load or create keystore
try (InputStream is = getClass().getResourceAsStream("/keystore.jceks")) {
if (is != null) {
keyStore.load(is, password);
} else {
keyStore.load(null, password);
generateNewKey();
}
}
}
private void generateNewKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey key = keyGen.generateKey();
KeyStore.SecretKeyEntry keyEntry = new KeyStore.SecretKeyEntry(key);
KeyStore.ProtectionParameter protection = 
new KeyStore.PasswordProtection(
System.getenv("KEY_PASSWORD").toCharArray());
keyStore.setEntry(KEY_ALIAS, keyEntry, protection);
// Save keystore to secure location
try (FileOutputStream fos = new FileOutputStream("keystore.jceks")) {
keyStore.store(fos, System.getenv("KEYSTORE_PASSWORD").toCharArray());
}
}
public SecretKey getEncryptionKey() throws Exception {
KeyStore.ProtectionParameter protection = 
new KeyStore.PasswordProtection(
System.getenv("KEY_PASSWORD").toCharArray());
KeyStore.Entry entry = keyStore.getEntry(KEY_ALIAS, protection);
return ((KeyStore.SecretKeyEntry) entry).getSecretKey();
}
}
}

3. Injection

SQL Injection Prevention

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowMapper;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.sql.*;
@Repository
public class InjectionPrevention {
// 3.1 Prepared Statements (JDBC)
public User findUserById(String userId) {
String sql = "SELECT * FROM users WHERE id = ? AND status = 'ACTIVE'";
return jdbcTemplate.queryForObject(sql, new Object[]{userId}, (rs, rowNum) -> {
User user = new User();
user.setId(rs.getString("id"));
user.setUsername(rs.getString("username"));
user.setEmail(rs.getString("email"));
return user;
});
}
// 3.2 JPA/Hibernate Parameterized Queries
@PersistenceContext
private EntityManager entityManager;
public List<User> findUsersByRole(String role) {
String jpql = "SELECT u FROM User u WHERE u.role = :role AND u.active = true";
return entityManager.createQuery(jpql, User.class)
.setParameter("role", role)
.getResultList();
}
// 3.3 Stored Procedures
public void updateUserStatus(String userId, String status) {
String sql = "CALL update_user_status(?, ?)";
jdbcTemplate.update(sql, userId, status);
}
}
// 3.4 Input Validation and Sanitization
@Component
public class InputValidationService {
private static final Pattern USERNAME_PATTERN = 
Pattern.compile("^[a-zA-Z0-9_]{3,20}$");
private static final Pattern EMAIL_PATTERN = 
Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
private static final Pattern SQL_INJECTION_PATTERN = 
Pattern.compile("([';\"\\\\]|(--[\\r\\n]|(;|--|#)[\\r\\n])|\\b(ALTER|CREATE|DELETE|DROP|EXEC(UTE){0,1}|INSERT( +INTO){0,1}|MERGE|SELECT|UPDATE|UNION( +ALL){0,1})\\b)", Pattern.CASE_INSENSITIVE);
public ValidationResult validateUserInput(UserInput input) {
List<String> errors = new ArrayList<>();
if (!isValidUsername(input.getUsername())) {
errors.add("Invalid username format");
}
if (!isValidEmail(input.getEmail())) {
errors.add("Invalid email format");
}
if (containsSQLInjection(input.getSearchQuery())) {
errors.add("Potential SQL injection detected");
}
return new ValidationResult(errors.isEmpty(), errors);
}
private boolean isValidUsername(String username) {
return username != null && USERNAME_PATTERN.matcher(username).matches();
}
private boolean isValidEmail(String email) {
return email != null && EMAIL_PATTERN.matcher(email).matches();
}
private boolean containsSQLInjection(String input) {
return input != null && SQL_INJECTION_PATTERN.matcher(input).find();
}
public String sanitizeHTML(String input) {
if (input == null) return null;
// Basic HTML sanitization
return input.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll("\"", "&quot;")
.replaceAll("'", "&#x27;")
.replaceAll("/", "&#x2F;");
}
}
// 3.5 NoSQL Injection Prevention
@Repository
public class MongoRepositorySecurity {
@Autowired
private MongoTemplate mongoTemplate;
public List<User> findUsersByUsername(String username) {
// Use typed queries instead of string concatenation
Query query = new Query();
query.addCriteria(Criteria.where("username").is(username)
.and("active").is(true));
return mongoTemplate.find(query, User.class);
}
// Secure aggregation queries
public List<UserAggregation> getUserStatistics(String role) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("role").is(role)),
Aggregation.group("department").count().as("userCount")
);
return mongoTemplate.aggregate(aggregation, "users", UserAggregation.class)
.getMappedResults();
}
}

4. Insecure Design

Secure Architecture Patterns

// 4.1 Secure by Design Principles
@Component
public class SecureDesignPatterns {
// Principle of Least Privilege
@Service
public class OrderService {
private final PaymentProcessor paymentProcessor;
private final AuditLogger auditLogger;
@PreAuthorize("hasRole('USER')")
@Transactional
public Order processOrder(OrderRequest request, String currentUserId) {
// Validate business rules
if (!isValidOrder(request)) {
throw new InvalidOrderException("Order validation failed");
}
// Process payment with limited context
PaymentResult payment = paymentProcessor.charge(
request.getAmount(), 
request.getPaymentToken(),
currentUserId
);
if (!payment.isSuccessful()) {
throw new PaymentFailedException("Payment processing failed");
}
// Create order
Order order = createOrder(request, currentUserId, payment);
// Audit the transaction
auditLogger.logOrderCreation(order, currentUserId);
return order;
}
private boolean isValidOrder(OrderRequest request) {
return request.getAmount().compareTo(BigDecimal.ZERO) > 0 &&
request.getItems() != null && !request.getItems().isEmpty() &&
isValidPaymentToken(request.getPaymentToken());
}
}
// 4.2 Fail-Safe Defaults
@Configuration
public class SecurityDefaults {
@Bean
public SecurityFilterChain defaultSecurity(HttpSecurity http) throws Exception {
return http
// Default deny all
.authorizeRequests(authz -> authz
.anyRequest().denyAll()
)
// Then explicitly allow what's needed
.authorizeRequests(authz -> authz
.antMatchers("/public/**").permitAll()
.antMatchers("/api/**").authenticated()
)
// Secure headers
.headers(headers -> headers
.contentSecurityPolicy(csp -> csp
.policyDirectives("default-src 'self'; script-src 'self' 'unsafe-inline'")
)
.frameOptions().deny()
)
.build();
}
}
// 4.3 Complete Mediation
@Component
public class AccessMediator {
private final Map<String, AccessPolicy> policies = new ConcurrentHashMap<>();
public boolean checkAccess(String resource, String action, User user) {
AccessPolicy policy = policies.get(resource);
if (policy == null) {
// Default deny
return false;
}
return policy.isAllowed(action, user);
}
public void validateAccess(String resource, String action, User user) {
if (!checkAccess(resource, action, user)) {
throw new AccessDeniedException(
"Access denied to " + resource + " for action " + action);
}
}
}
// 4.4 Economy of Mechanism
@Service
public class SimpleAuthService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public AuthenticationResult authenticate(String username, String password) {
// Simple, clear authentication logic
Optional<User> userOpt = userRepository.findByUsername(username);
if (userOpt.isEmpty()) {
return AuthenticationResult.failure("User not found");
}
User user = userOpt.get();
if (!passwordEncoder.matches(password, user.getPasswordHash())) {
return AuthenticationResult.failure("Invalid password");
}
if (!user.isActive()) {
return AuthenticationResult.failure("Account inactive");
}
return AuthenticationResult.success(user);
}
}
}
// 4.5 Threat Modeling Implementation
@Component
public class ThreatModelingService {
private final List<ThreatDetector> threatDetectors;
public SecurityAssessment assessApplication() {
SecurityAssessment assessment = new SecurityAssessment();
for (ThreatDetector detector : threatDetectors) {
List<Threat> threats = detector.detectThreats();
assessment.addThreats(threats);
}
return assessment;
}
public void validateAgainstThreatModel(HttpServletRequest request) {
// Check for common attack patterns
if (isSuspiciousRequest(request)) {
securityMonitor.alertSuspiciousActivity(request);
throw new SecurityException("Request blocked by threat model");
}
}
private boolean isSuspiciousRequest(HttpServletRequest request) {
String userAgent = request.getHeader("User-Agent");
String path = request.getRequestURI();
// Detect potential scanning or attack tools
return userAgent.contains("nmap") ||
userAgent.contains("sqlmap") ||
userAgent.contains("w3af") ||
path.contains("../") || // Path traversal
path.contains("//");    // Double slash attacks
}
}

5. Security Misconfiguration

Secure Configuration Management

import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
@Configuration
public class SecurityConfiguration {
// 5.1 Secure Spring Boot Configuration
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Value("${app.security.require-ssl:true}")
private boolean requireSSL;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
HttpSecurity config = http
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.headers(headers -> headers
.contentSecurityPolicy("default-src 'self'; script-src 'self' 'unsafe-inline'")
.frameOptions().deny()
.xssProtection().block(true)
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
);
if (requireSSL) {
config.requiresChannel().anyRequest().requiresSecure();
}
return config.build();
}
}
// 5.2 Secure Server Configuration
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainer() {
return factory -> {
factory.addConnectorCustomizers(connector -> {
connector.setAttribute("relaxedQueryChars", "");
connector.setAttribute("relaxedPathChars", "");
connector.setAttribute("maxParameterCount", "1000");
});
};
}
// 5.3 Error Handling Configuration
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex) {
// Log the full error internally
logger.error("Internal server error", ex);
// Return generic error message to client
ErrorResponse error = new ErrorResponse(
"An unexpected error occurred. Please try again later.");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(error);
}
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorResponse> handleAccessDenied(AccessDeniedException ex) {
ErrorResponse error = new ErrorResponse("Access denied");
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
}
}
// 5.4 Environment-Specific Configuration
@Configuration
@Profile("prod")
public class ProductionSecurityConfig {
@Bean
public SecurityFilterChain productionSecurity(HttpSecurity http) throws Exception {
return http
.headers(headers -> headers
.httpStrictTransportSecurity()
.includeSubDomains(true)
.maxAgeInSeconds(31536000)
)
.requiresChannel(channel -> channel
.anyRequest().requiresSecure()
)
.build();
}
}
}
// 5.5 Security Headers Configuration
@Component
public class SecurityHeadersFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
// Set security headers
httpResponse.setHeader("X-Content-Type-Options", "nosniff");
httpResponse.setHeader("X-Frame-Options", "DENY");
httpResponse.setHeader("X-XSS-Protection", "1; mode=block");
httpResponse.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
httpResponse.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
httpResponse.setHeader("Feature-Policy", "geolocation 'none'; microphone 'none'; camera 'none'");
chain.doFilter(request, response);
}
}
// 5.6 Secure CORS Configuration
@Configuration
public class CorsConfiguration {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://trusted-domain.com"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
configuration.setExposedHeaders(Arrays.asList("X-CSRF-TOKEN"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", configuration);
return source;
}
}

6. Vulnerable and Outdated Components

Dependency Security Management

// 6.1 Dependency Security Scanner
@Component
public class DependencySecurityScanner {
public void scanDependencies() {
try {
// Integrate with OWASP Dependency Check
Process process = Runtime.getRuntime().exec(
"dependency-check.sh --project myapp --scan . --format HTML");
int exitCode = process.waitFor();
if (exitCode != 0) {
logger.warn("Dependency security scan found vulnerabilities");
securityAlertService.sendAlert("Vulnerable dependencies detected");
}
} catch (Exception e) {
logger.error("Dependency security scan failed", e);
}
}
}
// 6.2 Secure Dependency Configuration
// pom.xml security configuration example
/*
<project>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>6.5.3</version>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
*/
// 6.3 Runtime Component Validation
@Component
public class ComponentValidator {
@PostConstruct
public void validateComponents() {
validateSpringSecurityVersion();
validateJacksonVersion();
validateLog4jVersion();
}
private void validateSpringSecurityVersion() {
String version = SpringVersion.getVersion();
if (version != null && isVulnerableVersion(version, "5.7.0")) {
throw new SecurityException(
"Vulnerable Spring Security version detected: " + version);
}
}
private boolean isVulnerableVersion(String currentVersion, String minSafeVersion) {
// Simple version comparison
return currentVersion.compareTo(minSafeVersion) < 0;
}
}
// 6.4 Security Update Automation
@Service
public class SecurityUpdateService {
@Scheduled(cron = "0 0 2 * * ?") // Daily at 2 AM
public void checkForSecurityUpdates() {
try {
List<SecurityUpdate> updates = securityFeedService.getSecurityUpdates();
for (SecurityUpdate update : updates) {
if (affectsOurDependencies(update)) {
securityTeamNotificationService.notify(update);
if (update.getSeverity() == Severity.CRITICAL) {
emergencyUpdateService.scheduleUpdate(update);
}
}
}
} catch (Exception e) {
logger.error("Security update check failed", e);
}
}
private boolean affectsOurDependencies(SecurityUpdate update) {
// Check if update affects any of our dependencies
return dependencyInventoryService.getDependencies().stream()
.anyMatch(dep -> dep.getName().equals(update.getComponent()) &&
isVersionAffected(dep.getVersion(), update.getAffectedVersions()));
}
}

7. Identification and Authentication Failures

Secure Authentication Implementation

import org.springframework.security.authentication.*;
import org.springframework.security.core.userdetails.UserDetailsService;
@Service
public class AuthenticationSecurity {
// 7.1 Secure Authentication Service
@Service
public class SecureAuthenticationService {
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
private final LoginAttemptService loginAttemptService;
public AuthenticationResult authenticate(LoginRequest request, 
HttpServletRequest httpRequest) {
String clientIP = getClientIP(httpRequest);
// Check for brute force attempts
if (loginAttemptService.isBlocked(clientIP)) {
throw new AuthenticationException("Too many failed attempts. Please try again later.");
}
try {
UserDetails userDetails = userDetailsService.loadUserByUsername(request.getUsername());
if (!passwordEncoder.matches(request.getPassword(), userDetails.getPassword())) {
loginAttemptService.recordFailedAttempt(clientIP);
throw new BadCredentialsException("Invalid credentials");
}
if (!userDetails.isEnabled()) {
throw new DisabledException("Account is disabled");
}
if (!userDetails.isAccountNonLocked()) {
throw new LockedException("Account is locked");
}
if (!userDetails.isAccountNonExpired()) {
throw new AccountExpiredException("Account has expired");
}
// Successful login
loginAttemptService.recordSuccessfulAttempt(clientIP);
return AuthenticationResult.success(userDetails);
} catch (UsernameNotFoundException e) {
// Don't reveal that username doesn't exist
loginAttemptService.recordFailedAttempt(clientIP);
throw new BadCredentialsException("Invalid credentials");
}
}
private String getClientIP(HttpServletRequest request) {
String xfHeader = request.getHeader("X-Forwarded-For");
if (xfHeader == null) {
return request.getRemoteAddr();
}
return xfHeader.split(",")[0];
}
}
// 7.2 Multi-Factor Authentication
@Service
public class MFAService {
private final Map<String, String> mfaSecrets = new ConcurrentHashMap<>();
private final JavaMailSender mailSender;
public void setupMFA(String username) {
String secret = generateSecretKey();
mfaSecrets.put(username, secret);
// Generate QR code URL for authenticator app
String qrCodeUrl = generateQRCodeUrl(username, secret);
sendMFASetupEmail(username, qrCodeUrl);
}
public boolean verifyMFA(String username, String code) {
String secret = mfaSecrets.get(username);
if (secret == null) {
throw new MFAException("MFA not setup for user");
}
return verifyCode(secret, code);
}
private String generateSecretKey() {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[20];
random.nextBytes(bytes);
Base32 base32 = new Base32();
return base32.encodeToString(bytes);
}
}
// 7.3 Session Management
@Component
public class SecureSessionManagement {
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
public void configureSessionManagement(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry())
.and()
.sessionFixation().migrateSession();
}
}
// 7.4 Password Policy Enforcement
@Component
public class PasswordPolicyEnforcer {
private static final int MIN_LENGTH = 12;
private static final int MAX_LENGTH = 128;
private static final Pattern COMPLEXITY_PATTERN = 
Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{" + 
MIN_LENGTH + "," + MAX_LENGTH + "}$");
public PasswordValidationResult validatePassword(String password) {
List<String> errors = new ArrayList<>();
if (password == null || password.length() < MIN_LENGTH) {
errors.add("Password must be at least " + MIN_LENGTH + " characters long");
}
if (password != null && password.length() > MAX_LENGTH) {
errors.add("Password must not exceed " + MAX_LENGTH + " characters");
}
if (!COMPLEXITY_PATTERN.matcher(password).matches()) {
errors.add("Password must contain uppercase, lowercase, number and special character");
}
if (isCommonPassword(password)) {
errors.add("Password is too common");
}
return new PasswordValidationResult(errors.isEmpty(), errors);
}
private boolean isCommonPassword(String password) {
// Check against common passwords list
Set<String> commonPasswords = Set.of(
"password", "123456", "qwerty", "letmein", "welcome");
return commonPasswords.contains(password.toLowerCase());
}
}
}

8. Software and Data Integrity Failures

Integrity Protection

import java.security.*;
import java.util.zip.CRC32;
@Component
public class IntegrityProtection {
// 8.1 Data Integrity Verification
public class DataIntegrityService {
private final Mac hmac;
public DataIntegrityService(SecretKey key) throws Exception {
this.hmac = Mac.getInstance("HmacSHA256");
this.hmac.init(key);
}
public String calculateHMAC(byte[] data) {
byte[] hash = hmac.doFinal(data);
return Base64.getEncoder().encodeToString(hash);
}
public boolean verifyHMAC(byte[] data, String expectedHMAC) {
String actualHMAC = calculateHMAC(data);
return MessageDigest.isEqual(
actualHMAC.getBytes(StandardCharsets.UTF_8),
expectedHMAC.getBytes(StandardCharsets.UTF_8)
);
}
public SignedData signData(byte[] data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data);
byte[] digitalSignature = signature.sign();
return new SignedData(data, digitalSignature);
}
public boolean verifySignature(SignedData signedData, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(signedData.getData());
return signature.verify(signedData.getSignature());
}
}
// 8.2 Secure Deserialization
@Component
public class SecureDeserialization {
private final Set<String> allowedClasses = Set.of(
"com.example.dto.*",
"java.util.*",
"java.lang.String"
);
public Object deserializeSafely(byte[] data) throws Exception {
try (ObjectInputStream ois = new SecureObjectInputStream(
new ByteArrayInputStream(data), allowedClasses)) {
return ois.readObject();
}
}
private static class SecureObjectInputStream extends ObjectInputStream {
private final Set<String> allowedClasses;
public SecureObjectInputStream(InputStream in, Set<String> allowedClasses) 
throws IOException {
super(in);
this.allowedClasses = allowedClasses;
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) 
throws IOException, ClassNotFoundException {
String className = desc.getName();
if (!isClassAllowed(className)) {
throw new SecurityException("Deserialization of class not allowed: " + className);
}
return super.resolveClass(desc);
}
private boolean isClassAllowed(String className) {
return allowedClasses.stream()
.anyMatch(pattern -> className.matches(pattern.replace("*", ".*")));
}
}
}
// 8.3 Dependency Integrity Verification
@Component
public class DependencyIntegrityChecker {
public boolean verifyDependencyIntegrity(File jarFile, String expectedHash) 
throws Exception {
String actualHash = calculateFileHash(jarFile);
return MessageDigest.isEqual(
actualHash.getBytes(StandardCharsets.UTF_8),
expectedHash.getBytes(StandardCharsets.UTF_8)
);
}
private String calculateFileHash(File file) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
try (InputStream is = new FileInputStream(file)) {
byte[] buffer = new byte[8192];
int read;
while ((read = is.read(buffer)) > 0) {
digest.update(buffer, 0, read);
}
}
return Base64.getEncoder().encodeToString(digest.digest());
}
}
// 8.4 Configuration Integrity
@Component
public class ConfigurationIntegrity {
private final String CONFIG_SIGNATURE_HEADER = "X-Config-Signature";
public void validateConfigurationSignature(Properties config, String signature) 
throws Exception {
String configString = propertiesToString(config);
String calculatedSignature = calculateSignature(configString);
if (!MessageDigest.isEqual(
calculatedSignature.getBytes(StandardCharsets.UTF_8),
signature.getBytes(StandardCharsets.UTF_8))) {
throw new SecurityException("Configuration integrity check failed");
}
}
private String propertiesToString(Properties props) {
return props.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining("|"));
}
private String calculateSignature(String data) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
}
}
}

9. Security Logging and Monitoring

Comprehensive Security Monitoring

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
@Component
public class SecurityLogging {
private static final Logger securityLogger = LoggerFactory.getLogger("SECURITY");
// 9.1 Security Event Logging
@Component
public class SecurityEventLogger {
public void logLoginSuccess(Authentication authentication, HttpServletRequest request) {
Map<String, String> event = Map.of(
"event", "LOGIN_SUCCESS",
"username", authentication.getName(),
"ip", getClientIP(request),
"userAgent", request.getHeader("User-Agent"),
"timestamp", Instant.now().toString()
);
securityLogger.info("Security event: {}", event);
}
public void logLoginFailure(String username, HttpServletRequest request, String reason) {
Map<String, String> event = Map.of(
"event", "LOGIN_FAILURE",
"username", username,
"ip", getClientIP(request),
"reason", reason,
"timestamp", Instant.now().toString()
);
securityLogger.warn("Security event: {}", event);
}
public void logAccessDenied(Authentication authentication, HttpServletRequest request) {
Map<String, String> event = Map.of(
"event", "ACCESS_DENIED",
"username", authentication != null ? authentication.getName() : "anonymous",
"ip", getClientIP(request),
"resource", request.getRequestURI(),
"timestamp", Instant.now().toString()
);
securityLogger.warn("Security event: {}", event);
}
private String getClientIP(HttpServletRequest request) {
String xfHeader = request.getHeader("X-Forwarded-For");
if (xfHeader == null) {
return request.getRemoteAddr();
}
return xfHeader.split(",")[0];
}
}
// 9.2 Audit Logging
@Aspect
@Component
public class AuditLoggingAspect {
@AfterReturning(pointcut = "@annotation(auditable)", returning = "result")
public void logAuditEvent(JoinPoint joinPoint, Auditable auditable, Object result) {
String action = auditable.action();
String resource = auditable.resource();
Object[] args = joinPoint.getArgs();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication != null ? authentication.getName() : "system";
Map<String, Object> auditEvent = Map.of(
"action", action,
"resource", resource,
"user", username,
"timestamp", Instant.now(),
"parameters", Arrays.toString(args),
"result", result != null ? "SUCCESS" : "FAILURE"
);
securityLogger.info("Audit event: {}", auditEvent);
}
}
// Custom annotation for audit logging
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auditable {
String action();
String resource();
}
// 9.3 Security Monitoring Service
@Component
public class SecurityMonitoringService {
private final Map<String, Integer> failedLoginAttempts = new ConcurrentHashMap<>();
private final Map<String, Instant> ipBlockTimes = new ConcurrentHashMap<>();
private static final int MAX_FAILED_ATTEMPTS = 5;
private static final Duration BLOCK_DURATION = Duration.ofMinutes(30);
public void recordFailedLogin(String ipAddress) {
failedLoginAttempts.merge(ipAddress, 1, Integer::sum);
if (failedLoginAttempts.get(ipAddress) >= MAX_FAILED_ATTEMPTS) {
blockIPAddress(ipAddress);
}
}
public boolean isIPBlocked(String ipAddress) {
Instant blockTime = ipBlockTimes.get(ipAddress);
if (blockTime == null) {
return false;
}
if (Instant.now().isAfter(blockTime.plus(BLOCK_DURATION))) {
ipBlockTimes.remove(ipAddress);
failedLoginAttempts.remove(ipAddress);
return false;
}
return true;
}
private void blockIPAddress(String ipAddress) {
ipBlockTimes.put(ipAddress, Instant.now());
securityLogger.warn("IP address blocked due to excessive failed logins: {}", ipAddress);
}
}
// 9.4 Real-time Alerting
@Component
public class SecurityAlertService {
private final List<AlertHandler> alertHandlers;
public void sendAlert(SecurityAlert alert) {
securityLogger.error("SECURITY ALERT: {}", alert);
for (AlertHandler handler : alertHandlers) {
try {
handler.handleAlert(alert);
} catch (Exception e) {
securityLogger.error("Failed to send alert via handler: {}", handler.getClass().getSimpleName(), e);
}
}
}
public void alertSuspiciousActivity(HttpServletRequest request, String description) {
SecurityAlert alert = SecurityAlert.builder()
.level(Severity.HIGH)
.type("SUSPICIOUS_ACTIVITY")
.description(description)
.ipAddress(getClientIP(request))
.userAgent(request.getHeader("User-Agent"))
.timestamp(Instant.now())
.build();
sendAlert(alert);
}
}
// 9.5 Log Injection Prevention
@Component
public class LogSanitizer {
public String sanitizeLogEntry(String input) {
if (input == null) return null;
// Remove CRLF characters to prevent log injection
return input.replaceAll("[\r\n]", "");
}
public Map<String, String> sanitizeLogMap(Map<String, String> input) {
return input.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> sanitizeLogEntry(entry.getValue())
));
}
}
}

10. Server-Side Request Forgery (SSRF)

SSRF Prevention

import java.net.*;
import java.util.Set;
@Component
public class SSRFProtection {
// 10.1 URL Validation and Filtering
@Component
public class URLValidator {
private final Set<String> allowedProtocols = Set.of("http", "https");
private final Set<String> blockedIPs = Set.of(
"127.0.0.1", "localhost", "0.0.0.0", "::1",
"169.254.169.254", // AWS metadata service
"192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12" // Private networks
);
public ValidationResult validateURL(String urlString) {
try {
URL url = new URL(urlString);
// Validate protocol
if (!allowedProtocols.contains(url.getProtocol().toLowerCase())) {
return ValidationResult.failure("Protocol not allowed: " + url.getProtocol());
}
// Resolve and validate host
String host = url.getHost();
InetAddress address = InetAddress.getByName(host);
// Check for blocked IPs
if (isBlockedIP(address)) {
return ValidationResult.failure("Access to internal resource blocked");
}
// Check for DNS rebinding
if (!isConsistentResolution(host, address)) {
return ValidationResult.failure("DNS resolution inconsistency detected");
}
return ValidationResult.success();
} catch (Exception e) {
return ValidationResult.failure("Invalid URL: " + e.getMessage());
}
}
private boolean isBlockedIP(InetAddress address) {
String ip = address.getHostAddress();
// Check exact matches
if (blockedIPs.contains(ip)) {
return true;
}
// Check CIDR blocks
return blockedIPs.stream()
.filter(blocked -> blocked.contains("/"))
.anyMatch(cidr -> isInRange(ip, cidr));
}
private boolean isInRange(String ip, String cidr) {
try {
String[] parts = cidr.split("/");
String network = parts[0];
int prefix = Integer.parseInt(parts[1]);
// Simplified CIDR check - in production use proper IP math library
return ip.startsWith(network.substring(0, network.lastIndexOf('.')));
} catch (Exception e) {
return false;
}
}
private boolean isConsistentResolution(String host, InetAddress resolvedAddress) {
try {
// Double-check DNS resolution
InetAddress[] allAddresses = InetAddress.getAllByName(host);
return Arrays.stream(allAddresses)
.anyMatch(addr -> addr.equals(resolvedAddress));
} catch (Exception e) {
return false;
}
}
}
// 10.2 Safe HTTP Client
@Component
public class SafeHttpClient {
private final URLValidator urlValidator;
private final HttpClient httpClient;
public SafeHttpClient(URLValidator urlValidator) {
this.urlValidator = urlValidator;
this.httpClient = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NEVER) // Don't follow redirects
.connectTimeout(Duration.ofSeconds(10))
.build();
}
public String makeSafeRequest(String urlString) throws Exception {
// Validate URL first
ValidationResult validation = urlValidator.validateURL(urlString);
if (!validation.isValid()) {
throw new SecurityException("URL validation failed: " + validation.getErrorMessage());
}
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(urlString))
.timeout(Duration.ofSeconds(30))
.header("User-Agent", "SafeApp/1.0") // Identify our requests
.build();
HttpResponse<String> response = httpClient.send(
request, HttpResponse.BodyHandlers.ofString());
// Validate response
if (response.statusCode() >= 400) {
throw new IOException("HTTP error: " + response.statusCode());
}
// Check content type
String contentType = response.headers().firstValue("Content-Type").orElse("");
if (!contentType.startsWith("text/") && !contentType.contains("json")) {
throw new SecurityException("Unsupported content type: " + contentType);
}
return response.body();
}
}
// 10.3 Outbound Request Monitoring
@Component
public class OutboundRequestMonitor {
private static final Logger outboundLogger = LoggerFactory.getLogger("OUTBOUND_REQUESTS");
public void monitorOutboundRequest(String url, String method, String user) {
Map<String, String> event = Map.of(
"type", "OUTBOUND_REQUEST",
"url", url,
"method", method,
"user", user,
"timestamp", Instant.now().toString(),
"ip", getOutboundIP()
);
outboundLogger.info("Outbound request: {}", event);
// Alert on suspicious patterns
if (isSuspiciousURL(url)) {
securityAlertService.alertSuspiciousOutboundRequest(url, user);
}
}
private boolean isSuspiciousURL(String url) {
return url.contains("169.254.169.254") || // AWS metadata
url.contains("metadata.google.internal") || // GCP metadata
url.contains("localhost") ||
url.contains("127.0.0.1") ||
url.contains("::1") ||
url.contains(".internal");
}
private String getOutboundIP() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (Exception e) {
return "unknown";
}
}
}
// 10.4 Whitelist-Based URL Access
@Component
public class URLWhitelistService {
private final Set<String> allowedDomains;
private final Set<Pattern> allowedPatterns;
public URLWhitelistService() {
// Load from configuration
this.allowedDomains = Set.of(
"api.trusted-service.com",
"images.safe-cdn.net",
"data.legitimate-source.org"
);
this.allowedPatterns = Set.of(
Pattern.compile("^https://cdn\\d+\\.safedomain\\.com/.*"),
Pattern.compile("^https://api\\-\\w+\\.validservice\\.com/v\\d+/.*")
);
}
public boolean isURLAllowed(String urlString) {
try {
URL url = new URL(urlString);
String host = url.getHost();
// Check exact domain matches
if (allowedDomains.contains(host)) {
return true;
}
// Check pattern matches
return allowedPatterns.stream()
.anyMatch(pattern -> pattern.matcher(urlString).matches());
} catch (Exception e) {
return false;
}
}
}
}
// Utility classes
class ValidationResult {
private final boolean valid;
private final String errorMessage;
private ValidationResult(boolean valid, String errorMessage) {
this.valid = valid;
this.errorMessage = errorMessage;
}
public static ValidationResult success() {
return new ValidationResult(true, null);
}
public static ValidationResult failure(String errorMessage) {
return new ValidationResult(false, errorMessage);
}
public boolean isValid() { return valid; }
public String getErrorMessage() { return errorMessage; }
}
class SecurityAlert {
private final Severity level;
private final String type;
private final String description;
private final String ipAddress;
private final String userAgent;
private final Instant timestamp;
// Builder pattern implementation
public static Builder builder() {
return new Builder();
}
public static class Builder {
private Severity level;
private String type;
private String description;
private String ipAddress;
private String userAgent;
private Instant timestamp;
public Builder level(Severity level) { this.level = level; return this; }
public Builder type(String type) { this.type = type; return this; }
public Builder description(String description) { this.description = description; return this; }
public Builder ipAddress(String ipAddress) { this.ipAddress = ipAddress; return this; }
public Builder userAgent(String userAgent) { this.userAgent = userAgent; return this; }
public Builder timestamp(Instant timestamp) { this.timestamp = timestamp; return this; }
public SecurityAlert build() {
return new SecurityAlert(this);
}
}
private SecurityAlert(Builder builder) {
this.level = builder.level;
this.type = builder.type;
this.description = builder.description;
this.ipAddress = builder.ipAddress;
this.userAgent = builder.userAgent;
this.timestamp = builder.timestamp;
}
// Getters
public Severity getLevel() { return level; }
public String getType() { return type; }
public String getDescription() { return description; }
public String getIpAddress() { return ipAddress; }
public String getUserAgent() { return userAgent; }
public Instant getTimestamp() { return timestamp; }
}
enum Severity {
LOW, MEDIUM, HIGH, CRITICAL
}

Conclusion

Implementing OWASP Top 10 mitigations in Java requires a comprehensive approach covering:

  1. Defense in Depth: Multiple layers of security controls
  2. Secure Defaults: Safe configurations out of the box
  3. Continuous Monitoring: Real-time security event tracking
  4. Input Validation: Comprehensive data validation at all layers
  5. Principle of Least Privilege: Minimal required access rights
  6. Secure Development Lifecycle: Security integrated throughout development

Regular security testing, code reviews, and staying updated with security advisories are essential for maintaining application security.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper