Introduction to Configuration Hardening
Configuration hardening involves securing application configurations to minimize attack surfaces and prevent security vulnerabilities. This comprehensive guide covers security best practices for Java applications, including environment-specific configurations, security headers, encryption, and compliance standards.
Core Security Configuration
Application Properties Security
application-security.yml
# Primary security configuration spring: application: name: secure-java-app # Security: Do not expose build information build: info: enabled: false # Security: Disable autoconfiguration for sensitive components autoconfigure: exclude: - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration # Security: Servlet configuration servlet: multipart: max-file-size: 10MB max-request-size: 10MB enabled: true # Security: Jackson configuration jackson: default-property-inclusion: non_null deserialization: fail-on-unknown-properties: true parser: allow-unquoted-control-chars: false allow-comments: false visibility: field: any getter: none setter: none creator: none # Security: JPA configuration jpa: show-sql: false properties: hibernate: format_sql: false use_sql_comments: false jdbc: lob: non_contextual_creation: true # Security: Disable automatic DDL generation hbm2ddl: auto: validate # Security: Data source configuration datasource: # Security: Use environment variables for credentials url: ${DB_URL:jdbc:h2:mem:testdb} username: ${DB_USERNAME:sa} password: ${DB_PASSWORD:} hikari: connection-timeout: 30000 maximum-pool-size: 10 minimum-idle: 2 max-lifetime: 1800000 leak-detection-threshold: 60000 # Security: Mail configuration mail: properties: mail: smtp: auth: true starttls: enable: true # Security: Cache configuration cache: type: simple cache-names: - security-tokens - rate-limits caffeine: spec: maximumSize=1000,expireAfterWrite=300s # Security: Session configuration session: store-type: none timeout: 1800 servlet: filter-order: 100 # Security: Web configuration web: resources: add-mappings: false locale-resolver: fixed # Security: Messages configuration messages: encoding: UTF-8 cache-duration: 3600 # Security: Server configuration server: # Security: Server information hiding server-header: "" # Security: Port configuration port: ${SERVER_PORT:8080} # Security: SSL/TLS configuration ssl: enabled: ${SSL_ENABLED:false} key-store: ${SSL_KEY_STORE:} key-store-password: ${SSL_KEY_STORE_PASSWORD:} key-store-type: ${SSL_KEY_STORE_TYPE:PKCS12} key-alias: ${SSL_KEY_ALIAS:} protocol: TLS enabled-protocols: TLSv1.2,TLSv1.3 ciphers: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 # Security: Servlet configuration servlet: context-path: / session: cookie: http-only: true secure: ${COOKIE_SECURE:true} max-age: 1800 same-site: strict name: SESSION encoding: charset: UTF-8 enabled: true force: true # Security: HTTP configuration http2: enabled: true compression: enabled: true mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json min-response-size: 1024 # Security: Hide server version error: include-stacktrace: never include-message: never include-binding-errors: never include-exception: false # Security: Connection timeout connection-timeout: 30000 # Security: Maximum HTTP header size max-http-header-size: 8KB # Security: Logging configuration logging: level: # Security: Reduce sensitive logging com.example.secure: INFO org.springframework.security: WARN org.springframework.web: WARN org.hibernate: WARN # Security: Disable sensitive SQL logging org.hibernate.SQL: ERROR org.hibernate.type.descriptor.sql.BasicBinder: ERROR # Security: Log file configuration file: name: /var/log/secure-app/application.log max-size: 10MB max-history: 30 total-size-cap: 1GB pattern: # Security: Avoid logging sensitive information file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %mask%msg%n" console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %mask%msg%n" # Security: Actuator endpoints configuration management: endpoints: web: exposure: # Security: Limit exposed endpoints include: health,info,metrics,prometheus exclude: env,beans,configprops,dump,heapdump,threaddump,shutdown # Security: Custom management port base-path: /management enabled-by-default: false jmx: exposure: exclude: "*" endpoint: health: enabled: true show-details: when_authorized show-components: when_authorized probes: enabled: true info: enabled: true metrics: enabled: true prometheus: enabled: true # Security: Disable sensitive endpoints env: enabled: false beans: enabled: false configprops: enabled: false dump: enabled: false heapdump: enabled: false threaddump: enabled: false shutdown: enabled: false # Security: Management server configuration server: port: ${MANAGEMENT_PORT:8081} address: 127.0.0.1 ssl: enabled: true # Security: Custom application security configuration app: security: # Security: JWT configuration jwt: secret: ${JWT_SECRET:} expiration: 3600 issuer: secure-java-app audience: secure-users # Security: CORS configuration cors: allowed-origins: ${ALLOWED_ORIGINS:https://localhost:3000,https://example.com} allowed-methods: GET,POST,PUT,DELETE,OPTIONS allowed-headers: Authorization,Content-Type,X-Requested-With allow-credentials: true max-age: 3600 # Security: Rate limiting rate-limit: enabled: true requests-per-minute: 100 burst-capacity: 20 # Security: Password policy password: min-length: 12 require-uppercase: true require-lowercase: true require-digits: true require-special-chars: true max-age-days: 90 history-size: 5 # Security: Session management session: max-sessions-per-user: 1 prevent-session-fixation: true # Security: Encryption configuration encryption: algorithm: AES/GCM/NoPadding key-size: 256 iv-size: 96 salt-size: 128 # Security: Database encryption encryption: enabled: true algorithm: AES key: ${ENCRYPTION_KEY:} salt: ${ENCRYPTION_SALT:} Environment-Specific Configurations
application-development.yml
# Development environment configuration spring: config: activate: on-profile: development # Security: Development-specific relaxations security: require-ssl: false # Security: Development database datasource: url: jdbc:h2:mem:devdb username: devuser password: devpass hikari: maximum-pool-size: 5 # Security: Development logging jackson: serialization: indent-output: true jpa: show-sql: true properties: hibernate: format_sql: true hbm2ddl: auto: create-drop # Security: Development server server: ssl: enabled: false servlet: session: cookie: secure: false # Security: Development logging logging: level: com.example.secure: DEBUG org.springframework.security: DEBUG org.hibernate.SQL: DEBUG org.hibernate.type.descriptor.sql.BasicBinder: TRACE # Security: Development endpoints management: endpoints: web: exposure: include: "*" endpoint: health: show-details: always env: enabled: true beans: enabled: true app: security: rate-limit: enabled: false
application-production.yml
# Production environment configuration spring: config: activate: on-profile: production # Security: Production database datasource: url: ${DB_URL} username: ${DB_USERNAME} password: ${DB_PASSWORD} hikari: maximum-pool-size: 20 minimum-idle: 5 leak-detection-threshold: 120000 # Security: Production JPA jpa: properties: hibernate: hbm2ddl: auto: validate jdbc: batch_size: 50 order_inserts: true order_updates: true # Security: Production cache cache: type: redis redis: time-to-live: 3600s cache-null-values: false # Security: Production server server: ssl: enabled: true key-store: ${SSL_KEY_STORE} key-store-password: ${SSL_KEY_STORE_PASSWORD} servlet: session: timeout: 1800 cookie: secure: true same-site: strict # Security: Production logging logging: level: com.example.secure: INFO org.springframework: WARN org.hibernate: ERROR file: name: /var/log/secure-app/production.log # Security: Production management management: endpoints: web: exposure: include: health,info,metrics jmx: enabled: false endpoint: health: show-details: never app: security: rate-limit: enabled: true requests-per-minute: 60 burst-capacity: 10 session: max-sessions-per-user: 1 Security Configuration Classes
Main Security Configuration
package com.example.secure.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.info.InfoEndpoint; import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.Arrays; import java.util.List; /** * Main security configuration with comprehensive hardening */ @Configuration @EnableWebSecurity @EnableMethodSecurity( prePostEnabled = true, securedEnabled = true, jsr250Enabled = true ) public class SecurityConfig { @Value("${app.security.cors.allowed-origins}") private String[] allowedOrigins; @Value("${app.security.cors.allowed-methods}") private String[] allowedMethods; @Value("${app.security.cors.allowed-headers}") private String[] allowedHeaders; @Value("${app.security.cors.allow-credentials}") private boolean allowCredentials; @Value("${app.security.cors.max-age}") private long maxAge; @Autowired private JwtAuthenticationFilter jwtAuthenticationFilter; @Autowired private SecurityProperties securityProperties; /** * Password encoder with secure configuration */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); // High cost factor for better security } /** * Authentication manager */ @Bean public AuthenticationManager authenticationManager( AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } /** * Security filter chain with comprehensive hardening */ @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http // Disable CSRF for stateless API (handled by JWT) .csrf(csrf -> csrf.disable()) // CORS configuration .cors(cors -> cors.configurationSource(corsConfigurationSource())) // Session management - stateless .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) // Authorization rules .authorizeHttpRequests(authz -> authz // Public endpoints .requestMatchers( "/api/auth/login", "/api/auth/register", "/api/public/**", "/error" ).permitAll() // Actuator endpoints with role-based access .requestMatchers(EndpointRequest.to(HealthEndpoint.class)).permitAll() .requestMatchers(EndpointRequest.to(InfoEndpoint.class)).permitAll() .requestMatchers(EndpointRequest.to(PrometheusScrapeEndpoint.class)).hasRole("MONITORING") // All other endpoints require authentication .anyRequest().authenticated() ) // Security headers .headers(headers -> headers // Content Security Policy .contentSecurityPolicy(csp -> csp .policyDirectives("default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; media-src 'self'; object-src 'none'; child-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; manifest-src 'self'") ) // HTTP Strict Transport Security .httpStrictTransportSecurity(hsts -> hsts .includeSubDomains(true) .preload(true) .maxAgeInSeconds(31536000) // 1 year ) // Frame options .frameOptions(frame -> frame .deny() ) // XSS Protection .xssProtection(xss -> xss .headerValue(org.springframework.security.web.header.writers.XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK) ) // Content Type options .contentTypeOptions(contentType -> contentType .disable() // Spring Security enables this by default ) // Referrer Policy .referrerPolicy(referrer -> referrer .policy(org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN) ) // Permissions Policy .permissionsPolicy(permissions -> permissions .policy("geolocation=(), microphone=(), camera=(), payment=()") ) ) // JWT filter .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) // Exception handling .exceptionHandling(exception -> exception .authenticationEntryPoint(new JwtAuthenticationEntryPoint()) .accessDeniedHandler(new JwtAccessDeniedHandler()) ); return http.build(); } /** * CORS configuration source */ @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); // Set allowed origins configuration.setAllowedOrigins(Arrays.asList(allowedOrigins)); // Set allowed methods configuration.setAllowedMethods(Arrays.asList(allowedMethods)); // Set allowed headers configuration.setAllowedHeaders(Arrays.asList(allowedHeaders)); // Set exposed headers configuration.setExposedHeaders(Arrays.asList( "Authorization", "X-Request-ID", "X-Rate-Limit-Remaining" )); // Set allow credentials configuration.setAllowCredentials(allowCredentials); // Set max age configuration.setMaxAge(maxAge); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/api/**", configuration); return source; } /** * Development-specific security configuration */ @Configuration @Profile("development") public static class DevelopmentSecurityConfig { @Bean public SecurityFilterChain developmentSecurityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authz -> authz // Allow access to development endpoints .requestMatchers( "/h2-console/**", "/actuator/**", "/swagger-ui/**", "/v3/api-docs/**" ).permitAll() ) // Allow H2 console in development .headers(headers -> headers .frameOptions(frame -> frame.sameOrigin()) ) .csrf(csrf -> csrf .ignoringRequestMatchers("/h2-console/**") ); return http.build(); } } } JWT Security Configuration
package com.example.secure.config; import com.example.secure.security.JwtAuthenticationFilter; import com.example.secure.security.JwtTokenProvider; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.password.PasswordEncoder; import java.security.Key; import java.util.Base64; import javax.crypto.spec.SecretKeySpec; /** * JWT Security Configuration */ @Configuration public class JwtSecurityConfig { @Value("${app.security.jwt.secret}") private String jwtSecret; @Value("${app.security.jwt.expiration}") private long jwtExpiration; @Value("${app.security.jwt.issuer}") private String jwtIssuer; @Value("${app.security.jwt.audience}") private String jwtAudience; /** * JWT token provider */ @Bean public JwtTokenProvider jwtTokenProvider(PasswordEncoder passwordEncoder, ObjectMapper objectMapper) { Key key = new SecretKeySpec( Base64.getDecoder().decode(jwtSecret), "HmacSHA512" ); return new JwtTokenProvider( key, jwtExpiration, jwtIssuer, jwtAudience, passwordEncoder, objectMapper ); } /** * JWT authentication filter */ @Bean public JwtAuthenticationFilter jwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { return new JwtAuthenticationFilter(jwtTokenProvider); } } Encryption Configuration
package com.example.secure.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; /** * Encryption configuration for data protection */ @Configuration public class EncryptionConfig { @Value("${app.security.encryption.algorithm:AES/GCM/NoPadding}") private String encryptionAlgorithm; @Value("${app.security.encryption.key-size:256}") private int keySize; @Value("${app.security.encryption.iv-size:96}") private int ivSize; @Value("${app.security.encryption.salt-size:128}") private int saltSize; @Value("${encryption.key:}") private String encryptionKey; @Value("${encryption.salt:}") private String encryptionSalt; /** * Encryption service for sensitive data */ @Bean public EncryptionService encryptionService() throws NoSuchAlgorithmException { SecretKey secretKey; if (encryptionKey != null && !encryptionKey.isEmpty()) { // Use configured key byte[] keyBytes = Base64.getDecoder().decode(encryptionKey); secretKey = new SecretKeySpec(keyBytes, "AES"); } else { // Generate new key (for development only) KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(keySize); secretKey = keyGenerator.generateKey(); } return new EncryptionService( secretKey, encryptionAlgorithm, ivSize, saltSize ); } /** * Secure random number generator */ @Bean public SecureRandom secureRandom() { return new SecureRandom(); } /** * Encryption service implementation */ public static class EncryptionService { private final SecretKey secretKey; private final String algorithm; private final int ivSize; private final int saltSize; private final SecureRandom secureRandom; public EncryptionService(SecretKey secretKey, String algorithm, int ivSize, int saltSize) { this.secretKey = secretKey; this.algorithm = algorithm; this.ivSize = ivSize; this.saltSize = saltSize; this.secureRandom = new SecureRandom(); } public String encrypt(String plaintext) throws Exception { byte[] iv = new byte[ivSize / 8]; secureRandom.nextBytes(iv); Cipher cipher = Cipher.getInstance(algorithm); GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec); byte[] ciphertext = cipher.doFinal(plaintext.getBytes()); 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); } public String decrypt(String encrypted) throws Exception { byte[] decoded = Base64.getDecoder().decode(encrypted); byte[] iv = new byte[ivSize / 8]; System.arraycopy(decoded, 0, iv, 0, iv.length); Cipher cipher = Cipher.getInstance(algorithm); GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec); byte[] ciphertext = new byte[decoded.length - iv.length]; System.arraycopy(decoded, iv.length, ciphertext, 0, ciphertext.length); byte[] plaintext = cipher.doFinal(ciphertext); return new String(plaintext); } public byte[] generateSalt() { byte[] salt = new byte[saltSize / 8]; secureRandom.nextBytes(salt); return salt; } } } Security Headers Configuration
Custom Security Headers Filter
package com.example.secure.config; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * Custom security headers configuration */ @Configuration public class SecurityHeadersConfig { /** * Custom security headers filter */ @Bean public FilterRegistrationBean<SecurityHeadersFilter> securityHeadersFilter() { FilterRegistrationBean<SecurityHeadersFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new SecurityHeadersFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); return registrationBean; } /** * Custom security headers filter implementation */ public static class SecurityHeadersFilter implements Filter { private final Map<String, String> securityHeaders = new HashMap<>(); public SecurityHeadersFilter() { // Security headers configuration securityHeaders.put("X-Content-Type-Options", "nosniff"); securityHeaders.put("X-Frame-Options", "DENY"); securityHeaders.put("X-XSS-Protection", "1; mode=block"); securityHeaders.put("Referrer-Policy", "strict-origin-when-cross-origin"); securityHeaders.put("Permissions-Policy", "geolocation=(), microphone=(), camera=()"); securityHeaders.put("X-Permitted-Cross-Domain-Policies", "none"); securityHeaders.put("X-Download-Options", "noopen"); securityHeaders.put("X-DNS-Prefetch-Control", "off"); securityHeaders.put("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload"); // Custom headers securityHeaders.put("X-Request-ID", ""); securityHeaders.put("X-Runtime", ""); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; // Add security headers securityHeaders.forEach((key, value) -> { if (!httpResponse.containsHeader(key)) { if (key.equals("X-Request-ID")) { // Generate unique request ID String requestId = java.util.UUID.randomUUID().toString(); httpResponse.setHeader(key, requestId); } else if (key.equals("X-Runtime")) { // Will be set after processing } else { httpResponse.setHeader(key, value); } } }); // Measure processing time long startTime = System.currentTimeMillis(); try { chain.doFilter(request, response); } finally { long endTime = System.currentTimeMillis(); httpResponse.setHeader("X-Runtime", String.valueOf(endTime - startTime)); } } @Override public void init(FilterConfig filterConfig) throws ServletException { // Initialization logic if needed } @Override public void destroy() { // Cleanup logic if needed } } } Rate Limiting Configuration
package com.example.secure.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.util.concurrent.TimeUnit; /** * Rate limiting configuration */ @Configuration public class RateLimitingConfig { @Value("${app.security.rate-limit.enabled:true}") private boolean rateLimitEnabled; @Value("${app.security.rate-limit.requests-per-minute:100}") private int requestsPerMinute; @Value("${app.security.rate-limit.burst-capacity:20}") private int burstCapacity; /** * Rate limiting service */ @Bean public RateLimitingService rateLimitingService(RedisTemplate<String, Object> redisTemplate) { return new RateLimitingService( redisTemplate, rateLimitEnabled, requestsPerMinute, burstCapacity ); } /** * Redis template for rate limiting */ @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setEnableTransactionSupport(true); return template; } /** * Rate limiting service implementation */ public static class RateLimitingService { private final RedisTemplate<String, Object> redisTemplate; private final boolean enabled; private final int requestsPerMinute; private final int burstCapacity; public RateLimitingService(RedisTemplate<String, Object> redisTemplate, boolean enabled, int requestsPerMinute, int burstCapacity) { this.redisTemplate = redisTemplate; this.enabled = enabled; this.requestsPerMinute = requestsPerMinute; this.burstCapacity = burstCapacity; } public boolean isAllowed(String clientId, String endpoint) { if (!enabled) { return true; } String key = String.format("rate_limit:%s:%s", clientId, endpoint); long currentTime = System.currentTimeMillis(); long windowSize = TimeUnit.MINUTES.toMillis(1); // Get current count Long currentCount = redisTemplate.opsForValue().increment(key, 1); if (currentCount == 1) { // First request in this window, set expiration redisTemplate.expire(key, windowSize, TimeUnit.MILLISECONDS); } return currentCount <= requestsPerMinute; } public RateLimitInfo getRateLimitInfo(String clientId, String endpoint) { String key = String.format("rate_limit:%s:%s", clientId, endpoint); Long currentCount = redisTemplate.opsForValue().increment(key, 0); // Get without incrementing if (currentCount == null) { currentCount = 0L; } long remaining = Math.max(0, requestsPerMinute - currentCount); long resetTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1); return new RateLimitInfo(remaining, resetTime); } } /** * Rate limit information */ public static class RateLimitInfo { private final long remaining; private final long resetTime; public RateLimitInfo(long remaining, long resetTime) { this.remaining = remaining; this.resetTime = resetTime; } public long getRemaining() { return remaining; } public long getResetTime() { return resetTime; } } } Logging Security Configuration
Secure Logging Configuration
package com.example.secure.config; import ch.qos.logback.classic.PatternLayout; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.pattern.PatternLayoutEncoderBase; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.regex.Pattern; /** * Secure logging configuration */ @Configuration public class LoggingSecurityConfig { /** * Pattern layout with sensitive data masking */ @Bean public PatternLayoutEncoderBase<ILoggingEvent> patternLayoutEncoder() { return new SecurePatternLayoutEncoder(); } /** * Secure pattern layout that masks sensitive information */ public static class SecurePatternLayoutEncoder extends PatternLayoutEncoderBase<ILoggingEvent> { private static final Pattern SENSITIVE_PATTERNS = Pattern.compile( "(?i)(password|pwd|secret|key|token|auth|credential|ssn|credit.?card)=[^&,\\s]+", Pattern.CASE_INSENSITIVE ); @Override public void start() { PatternLayout patternLayout = new PatternLayout() { @Override public String doLayout(ILoggingEvent event) { String original = super.doLayout(event); return maskSensitiveData(original); } }; patternLayout.setContext(context); patternLayout.setPattern(getPattern()); patternLayout.setOutputPatternAsHeader(outputPatternAsHeader); patternLayout.start(); this.layout = patternLayout; super.start(); } /** * Mask sensitive data in log messages */ private String maskSensitiveData(String message) { if (message == null) { return null; } // Mask passwords, tokens, etc. String masked = SENSITIVE_PATTERNS.matcher(message) .replaceAll("$1=***"); // Mask email addresses masked = Pattern.compile("\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b") .matcher(masked) .replaceAll("***@***.***"); // Mask IP addresses masked = Pattern.compile("\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}\\b") .matcher(masked) .replaceAll("***.***.***.***"); // Mask credit card numbers masked = Pattern.compile("\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\\d{3})\\d{11})\\b") .matcher(masked) .replaceAll("****************"); return masked; } } } Database Security Configuration
JPA Configuration with Security
```java
package com.example.secure.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
- JPA configuration with security enhancements
*/
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.secure.repository",
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager"
)
@EnableTransactionManagement
@EnableJpaAuditing
public class DatabaseSecurityConfig { @Autowired
private DataSource dataSource; /**- Entity manager factory with security properties
*/
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder) { Map properties = new HashMap<>();
- Entity manager factory with security properties