Implementing Sqreen (Datadog) Security in Java

Sqreen, now part of Datadog, provides Runtime Application Self-Protection (RASP) and application security monitoring. This comprehensive guide covers Java integration for real-time threat detection, security monitoring, and automated protection.

Understanding Sqreen/Datadog Application Security

Key Security Features:

  • RASP (Runtime Application Self-Protection): Real-time attack detection and blocking
  • Application Security Monitoring: Continuous security observability
  • Threat Intelligence: Leverages Datadog's global threat intelligence
  • Automated Protection: Blocks attacks without manual intervention
  • Security Analytics: Correlates application security with performance data

Java Dependencies Setup

<dependencies>
<!-- Sqreen Agent (usually loaded via -javaagent) -->
<!-- Datadog Java Tracer (includes security features) -->
<dependency>
<groupId>com.datadoghq</groupId>
<artifactId>dd-trace-api</artifactId>
<version>1.10.0</version>
</dependency>
<!-- Spring Boot Security Integration -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Web application security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<!-- JSON processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<!-- HTTP client for Datadog API -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- Logging integration -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>

Security Configuration Models

package com.example.sqreen.security;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.*;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class SecurityEvent {
private String eventId;
private EventType eventType;
private Date timestamp;
private String sessionId;
private String userId;
private String ipAddress;
private String userAgent;
private Map<String, Object> context;
private Map<String, Object> requestData;
private Map<String, Object> responseData;
private ThreatLevel threatLevel;
private boolean blocked;
private String ruleName;
private String applicationId;
public enum EventType {
SQL_INJECTION, XSS, PATH_TRAVERSAL, 
RCE_ATTEMPT, AUTH_BYPASS, RATE_LIMIT,
SUSPICIOUS_ACTIVITY, LOGIN_ATTEMPT,
DATA_ACCESS, CONFIGURATION_CHANGE
}
public enum ThreatLevel {
LOW, MEDIUM, HIGH, CRITICAL
}
// Constructors
public SecurityEvent() {
this.context = new HashMap<>();
this.requestData = new HashMap<>();
this.responseData = new HashMap<>();
this.timestamp = new Date();
}
public SecurityEvent(EventType eventType, ThreatLevel threatLevel) {
this();
this.eventType = eventType;
this.threatLevel = threatLevel;
this.eventId = UUID.randomUUID().toString();
}
// Getters and setters
public String getEventId() { return eventId; }
public void setEventId(String eventId) { this.eventId = eventId; }
public EventType getEventType() { return eventType; }
public void setEventType(EventType eventType) { this.eventType = eventType; }
public Date getTimestamp() { return timestamp; }
public void setTimestamp(Date timestamp) { this.timestamp = timestamp; }
public String getSessionId() { return sessionId; }
public void setSessionId(String sessionId) { this.sessionId = sessionId; }
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getIpAddress() { return ipAddress; }
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
public String getUserAgent() { return userAgent; }
public void setUserAgent(String userAgent) { this.userAgent = userAgent; }
public Map<String, Object> getContext() { return context; }
public void setContext(Map<String, Object> context) { this.context = context; }
public Map<String, Object> getRequestData() { return requestData; }
public void setRequestData(Map<String, Object> requestData) { this.requestData = requestData; }
public Map<String, Object> getResponseData() { return responseData; }
public void setResponseData(Map<String, Object> responseData) { this.responseData = responseData; }
public ThreatLevel getThreatLevel() { return threatLevel; }
public void setThreatLevel(ThreatLevel threatLevel) { this.threatLevel = threatLevel; }
public boolean isBlocked() { return blocked; }
public void setBlocked(boolean blocked) { this.blocked = blocked; }
public String getRuleName() { return ruleName; }
public void setRuleName(String ruleName) { this.ruleName = ruleName; }
public String getApplicationId() { return applicationId; }
public void setApplicationId(String applicationId) { this.applicationId = applicationId; }
// Builder methods
public SecurityEvent withContext(String key, Object value) {
this.context.put(key, value);
return this;
}
public SecurityEvent withRequestData(String key, Object value) {
this.requestData.put(key, value);
return this;
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public class SecurityRule {
private String name;
private RuleType type;
private String description;
private Map<String, Object> conditions;
private RuleAction action;
private boolean enabled;
private int priority;
private Date createdAt;
private Date updatedAt;
public enum RuleType {
RATE_LIMIT, PATTERN_MATCH, BEHAVIORAL, 
GEO_LOCATION, USER_AGENT, IP_REPUTATION
}
public enum RuleAction {
BLOCK, ALERT, CAPTCHA, THROTTLE, LOG
}
// Constructors
public SecurityRule() {
this.conditions = new HashMap<>();
this.enabled = true;
this.priority = 1;
this.createdAt = new Date();
}
public SecurityRule(String name, RuleType type) {
this();
this.name = name;
this.type = type;
}
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public RuleType getType() { return type; }
public void setType(RuleType type) { this.type = type; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Map<String, Object> getConditions() { return conditions; }
public void setConditions(Map<String, Object> conditions) { this.conditions = conditions; }
public RuleAction getAction() { return action; }
public void setAction(RuleAction action) { this.action = action; }
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public int getPriority() { return priority; }
public void setPriority(int priority) { this.priority = priority; }
public Date getCreatedAt() { return createdAt; }
public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; }
public Date getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(Date updatedAt) { this.updatedAt = updatedAt; }
// Builder methods
public SecurityRule withCondition(String key, Object value) {
this.conditions.put(key, value);
return this;
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public class SecurityMetrics {
private Date timestamp;
private String applicationId;
private Map<String, Long> eventCounts;
private Map<String, Long> blockedCounts;
private Map<SecurityEvent.ThreatLevel, Long> threatLevelCounts;
private long totalRequests;
private long maliciousRequests;
private double securityScore;
// Constructors
public SecurityMetrics() {
this.timestamp = new Date();
this.eventCounts = new HashMap<>();
this.blockedCounts = new HashMap<>();
this.threatLevelCounts = new HashMap<>();
}
// Getters and setters
public Date getTimestamp() { return timestamp; }
public void setTimestamp(Date timestamp) { this.timestamp = timestamp; }
public String getApplicationId() { return applicationId; }
public void setApplicationId(String applicationId) { this.applicationId = applicationId; }
public Map<String, Long> getEventCounts() { return eventCounts; }
public void setEventCounts(Map<String, Long> eventCounts) { this.eventCounts = eventCounts; }
public Map<String, Long> getBlockedCounts() { return blockedCounts; }
public void setBlockedCounts(Map<String, Long> blockedCounts) { this.blockedCounts = blockedCounts; }
public Map<SecurityEvent.ThreatLevel, Long> getThreatLevelCounts() { return threatLevelCounts; }
public void setThreatLevelCounts(Map<SecurityEvent.ThreatLevel, Long> threatLevelCounts) { this.threatLevelCounts = threatLevelCounts; }
public long getTotalRequests() { return totalRequests; }
public void setTotalRequests(long totalRequests) { this.totalRequests = totalRequests; }
public long getMaliciousRequests() { return maliciousRequests; }
public void setMaliciousRequests(long maliciousRequests) { this.maliciousRequests = maliciousRequests; }
public double getSecurityScore() { return securityScore; }
public void setSecurityScore(double securityScore) { this.securityScore = securityScore; }
}

Sqreen/Datadog Security Manager

package com.example.sqreen.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class SqreenSecurityManager {
private static final Logger logger = LoggerFactory.getLogger(SqreenSecurityManager.class);
private final ObjectMapper objectMapper;
private final OkHttpClient httpClient;
private final String datadogApiKey;
private final String datadogSite;
private final String applicationId;
// In-memory storage for demo (use Redis in production)
private final Map<String, UserSecurityContext> userContexts;
private final Map<String, Integer> ipRequestCounts;
private final List<SecurityRule> securityRules;
private final ScheduledExecutorService scheduler;
public SqreenSecurityManager(String datadogApiKey, String datadogSite, String applicationId) {
this.objectMapper = new ObjectMapper();
this.httpClient = new OkHttpClient();
this.datadogApiKey = datadogApiKey;
this.datadogSite = datadogSite;
this.applicationId = applicationId;
this.userContexts = new ConcurrentHashMap<>();
this.ipRequestCounts = new ConcurrentHashMap<>();
this.securityRules = new ArrayList<>();
this.scheduler = Executors.newScheduledThreadPool(2);
initializeDefaultRules();
startMetricsCollection();
}
/**
* Track security event
*/
public void trackSecurityEvent(SecurityEvent event) {
try {
event.setApplicationId(applicationId);
// Apply security rules
applySecurityRules(event);
// Log event locally
logger.warn("Security Event: {} - {} - {}", 
event.getEventType(), event.getThreatLevel(), event.getRuleName());
// Send to Datadog
sendToDatadog(event);
// Update user context
if (event.getUserId() != null) {
updateUserSecurityContext(event);
}
} catch (Exception e) {
logger.error("Failed to track security event", e);
}
}
/**
* Check request for security violations
*/
public SecurityCheckResult checkRequest(HttpServletRequest request) {
SecurityCheckResult result = new SecurityCheckResult();
result.setRequestId(UUID.randomUUID().toString());
result.setTimestamp(new Date());
try {
String ipAddress = getClientIpAddress(request);
String userAgent = request.getHeader("User-Agent");
String sessionId = request.getSession().getId();
// Check rate limiting
if (isRateLimited(ipAddress)) {
result.setBlocked(true);
result.setBlockReason("Rate limit exceeded");
SecurityEvent rateLimitEvent = new SecurityEvent(
SecurityEvent.EventType.RATE_LIMIT, 
SecurityEvent.ThreatLevel.MEDIUM
);
rateLimitEvent.setIpAddress(ipAddress);
rateLimitEvent.setUserAgent(userAgent);
rateLimitEvent.setBlocked(true);
rateLimitEvent.setRuleName("rate-limit-global");
trackSecurityEvent(rateLimitEvent);
return result;
}
// Check for suspicious patterns
checkSuspiciousPatterns(request, result);
// Check user agent
checkUserAgent(userAgent, result);
// Update metrics
updateRequestMetrics(ipAddress);
} catch (Exception e) {
logger.error("Error during security check", e);
}
return result;
}
/**
* Identify user for security tracking
*/
public void identifyUser(String userId, Map<String, Object> traits) {
try {
UserSecurityContext context = userContexts.computeIfAbsent(userId, 
k -> new UserSecurityContext(userId));
context.setTraits(traits);
context.setLastSeen(new Date());
// Track identification event
SecurityEvent identifyEvent = new SecurityEvent(
SecurityEvent.EventType.SUSPICIOUS_ACTIVITY, 
SecurityEvent.ThreatLevel.LOW
);
identifyEvent.setUserId(userId);
identifyEvent.withContext("traits", traits);
trackSecurityEvent(identifyEvent);
} catch (Exception e) {
logger.error("Failed to identify user", e);
}
}
/**
* Track authentication events
*/
public void trackAuthEvent(String userId, boolean success, String method, 
Map<String, Object> context) {
SecurityEvent authEvent = new SecurityEvent(
SecurityEvent.EventType.LOGIN_ATTEMPT, 
success ? SecurityEvent.ThreatLevel.LOW : SecurityEvent.ThreatLevel.MEDIUM
);
authEvent.setUserId(userId);
authEvent.setBlocked(!success);
authEvent.withContext("auth_method", method);
authEvent.withContext("success", success);
if (context != null) {
authEvent.getContext().putAll(context);
}
trackSecurityEvent(authEvent);
// Update user context
if (success && userId != null) {
UserSecurityContext userContext = userContexts.computeIfAbsent(userId, 
k -> new UserSecurityContext(userId));
userContext.setLastLogin(new Date());
userContext.setFailedLoginAttempts(0);
} else if (userId != null) {
UserSecurityContext userContext = userContexts.get(userId);
if (userContext != null) {
userContext.incrementFailedAttempts();
}
}
}
private void applySecurityRules(SecurityEvent event) {
for (SecurityRule rule : securityRules) {
if (!rule.isEnabled()) continue;
if (evaluateRule(rule, event)) {
event.setRuleName(rule.getName());
switch (rule.getAction()) {
case BLOCK:
event.setBlocked(true);
break;
case ALERT:
// Send alert (could integrate with PagerDuty, Slack, etc.)
sendSecurityAlert(event, rule);
break;
case THROTTLE:
// Implement throttling logic
break;
default:
// Just log
break;
}
// Stop at first matching rule (based on priority)
break;
}
}
}
private boolean evaluateRule(SecurityRule rule, SecurityEvent event) {
switch (rule.getType()) {
case RATE_LIMIT:
return evaluateRateLimitRule(rule, event);
case PATTERN_MATCH:
return evaluatePatternRule(rule, event);
case BEHAVIORAL:
return evaluateBehavioralRule(rule, event);
default:
return false;
}
}
private boolean evaluateRateLimitRule(SecurityRule rule, SecurityEvent event) {
// Implement rate limiting logic
String ip = event.getIpAddress();
if (ip != null) {
int count = ipRequestCounts.getOrDefault(ip, 0);
int limit = (Integer) rule.getConditions().getOrDefault("limit", 100);
return count > limit;
}
return false;
}
private boolean evaluatePatternRule(SecurityRule rule, SecurityEvent event) {
// Implement pattern matching logic
return false;
}
private boolean evaluateBehavioralRule(SecurityRule rule, SecurityEvent event) {
// Implement behavioral analysis
return false;
}
private boolean isRateLimited(String ipAddress) {
int count = ipRequestCounts.getOrDefault(ipAddress, 0);
return count > 1000; // Example limit
}
private void checkSuspiciousPatterns(HttpServletRequest request, SecurityCheckResult result) {
// Check for SQL injection patterns
Enumeration<String> paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
String paramValue = request.getParameter(paramName);
if (containsSqlInjectionPattern(paramValue)) {
result.setBlocked(true);
result.setBlockReason("SQL injection attempt detected");
SecurityEvent sqlEvent = new SecurityEvent(
SecurityEvent.EventType.SQL_INJECTION, 
SecurityEvent.ThreatLevel.HIGH
);
sqlEvent.setIpAddress(getClientIpAddress(request));
sqlEvent.setBlocked(true);
sqlEvent.withRequestData("parameter", paramName);
sqlEvent.withRequestData("value", maskSensitiveData(paramValue));
trackSecurityEvent(sqlEvent);
break;
}
}
}
private void checkUserAgent(String userAgent, SecurityCheckResult result) {
if (userAgent == null) return;
List<String> suspiciousAgents = Arrays.asList(
"sqlmap", "nikto", "metasploit", "nmap"
);
for (String suspicious : suspiciousAgents) {
if (userAgent.toLowerCase().contains(suspicious)) {
result.setBlocked(true);
result.setBlockReason("Suspicious user agent detected");
break;
}
}
}
private boolean containsSqlInjectionPattern(String value) {
if (value == null) return false;
String[] patterns = {
"';", "--", "/*", "*/", "xp_", "union select", "or 1=1", "and 1=1"
};
String lowerValue = value.toLowerCase();
for (String pattern : patterns) {
if (lowerValue.contains(pattern)) {
return true;
}
}
return false;
}
private void updateRequestMetrics(String ipAddress) {
ipRequestCounts.merge(ipAddress, 1, Integer::sum);
}
private void updateUserSecurityContext(SecurityEvent event) {
UserSecurityContext context = userContexts.computeIfAbsent(event.getUserId(), 
k -> new UserSecurityContext(event.getUserId()));
context.getSecurityEvents().add(event);
context.setLastActivity(new Date());
if (event.getThreatLevel() == SecurityEvent.ThreatLevel.HIGH || 
event.getThreatLevel() == SecurityEvent.ThreatLevel.CRITICAL) {
context.incrementHighSeverityEvents();
}
}
private void sendToDatadog(SecurityEvent event) throws IOException {
// Convert event to Datadog log format
Map<String, Object> logEntry = new HashMap<>();
logEntry.put("ddsource", "sqreen-java");
logEntry.put("ddtags", "env:production,version:1.0");
logEntry.put("hostname", System.getenv("HOSTNAME"));
logEntry.put("service", applicationId);
logEntry.put("message", event);
String jsonBody = objectMapper.writeValueAsString(logEntry);
Request request = new Request.Builder()
.url("https://http-intake.logs." + datadogSite + "/api/v2/logs")
.post(RequestBody.create(jsonBody, MediaType.parse("application/json")))
.header("DD-API-KEY", datadogApiKey)
.header("Content-Type", "application/json")
.build();
// Fire and forget - don't block application
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
logger.warn("Failed to send security event to Datadog", e);
}
@Override
public void onResponse(Call call, Response response) {
if (!response.isSuccessful()) {
logger.warn("Datadog API returned error: {}", response.code());
}
response.close();
}
});
}
private void sendSecurityAlert(SecurityEvent event, SecurityRule rule) {
// Implement alerting logic (Slack, PagerDuty, etc.)
logger.error("SECURITY ALERT - Rule: {}, Event: {}, Threat: {}", 
rule.getName(), event.getEventType(), event.getThreatLevel());
}
private void initializeDefaultRules() {
// Rate limiting rule
SecurityRule rateLimitRule = new SecurityRule("rate-limit-global", 
SecurityRule.RuleType.RATE_LIMIT);
rateLimitRule.setDescription("Global rate limiting");
rateLimitRule.setAction(SecurityRule.RuleAction.BLOCK);
rateLimitRule.withCondition("limit", 1000);
rateLimitRule.withCondition("window_seconds", 60);
securityRules.add(rateLimitRule);
// SQL injection rule
SecurityRule sqlInjectionRule = new SecurityRule("sql-injection-detection", 
SecurityRule.RuleType.PATTERN_MATCH);
sqlInjectionRule.setDescription("SQL injection pattern detection");
sqlInjectionRule.setAction(SecurityRule.RuleAction.BLOCK);
sqlInjectionRule.withCondition("patterns", Arrays.asList("';", "union select", "or 1=1"));
securityRules.add(sqlInjectionRule);
// Behavioral rule for failed logins
SecurityRule failedLoginRule = new SecurityRule("failed-login-threshold", 
SecurityRule.RuleType.BEHAVIORAL);
failedLoginRule.setDescription("Multiple failed login attempts");
failedLoginRule.setAction(SecurityRule.RuleAction.ALERT);
failedLoginRule.withCondition("threshold", 5);
failedLoginRule.withCondition("window_minutes", 10);
securityRules.add(failedLoginRule);
}
private void startMetricsCollection() {
scheduler.scheduleAtFixedRate(this::collectMetrics, 0, 1, TimeUnit.MINUTES);
scheduler.scheduleAtFixedRate(this::cleanupOldData, 0, 5, TimeUnit.MINUTES);
}
private void collectMetrics() {
try {
SecurityMetrics metrics = new SecurityMetrics();
metrics.setApplicationId(applicationId);
// Calculate metrics from recent events
// This would aggregate data from your event storage
// Send metrics to Datadog
sendMetricsToDatadog(metrics);
} catch (Exception e) {
logger.error("Failed to collect metrics", e);
}
}
private void cleanupOldData() {
// Clean up old IP counts
// Clean up old user contexts
}
private void sendMetricsToDatadog(SecurityMetrics metrics) {
// Implement metrics sending to Datadog
}
private String getClientIpAddress(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
return request.getRemoteAddr();
}
private String maskSensitiveData(String data) {
if (data == null || data.length() < 8) return "***";
return data.substring(0, 4) + "***" + data.substring(data.length() - 4);
}
public static class SecurityCheckResult {
private String requestId;
private Date timestamp;
private boolean blocked;
private String blockReason;
private Map<String, Object> details;
public SecurityCheckResult() {
this.details = new HashMap<>();
}
// Getters and setters
public String getRequestId() { return requestId; }
public void setRequestId(String requestId) { this.requestId = requestId; }
public Date getTimestamp() { return timestamp; }
public void setTimestamp(Date timestamp) { this.timestamp = timestamp; }
public boolean isBlocked() { return blocked; }
public void setBlocked(boolean blocked) { this.blocked = blocked; }
public String getBlockReason() { return blockReason; }
public void setBlockReason(String blockReason) { this.blockReason = blockReason; }
public Map<String, Object> getDetails() { return details; }
public void setDetails(Map<String, Object> details) { this.details = details; }
}
public static class UserSecurityContext {
private String userId;
private Date lastSeen;
private Date lastLogin;
private int failedLoginAttempts;
private int highSeverityEvents;
private Map<String, Object> traits;
private List<SecurityEvent> securityEvents;
public UserSecurityContext(String userId) {
this.userId = userId;
this.traits = new HashMap<>();
this.securityEvents = new ArrayList<>();
this.lastSeen = new Date();
}
// Getters and setters
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public Date getLastSeen() { return lastSeen; }
public void setLastSeen(Date lastSeen) { this.lastSeen = lastSeen; }
public Date getLastLogin() { return lastLogin; }
public void setLastLogin(Date lastLogin) { this.lastLogin = lastLogin; }
public int getFailedLoginAttempts() { return failedLoginAttempts; }
public void setFailedLoginAttempts(int failedLoginAttempts) { this.failedLoginAttempts = failedLoginAttempts; }
public int getHighSeverityEvents() { return highSeverityEvents; }
public void setHighSeverityEvents(int highSeverityEvents) { this.highSeverityEvents = highSeverityEvents; }
public Map<String, Object> getTraits() { return traits; }
public void setTraits(Map<String, Object> traits) { this.traits = traits; }
public List<SecurityEvent> getSecurityEvents() { return securityEvents; }
public void setSecurityEvents(List<SecurityEvent> securityEvents) { this.securityEvents = securityEvents; }
public void incrementFailedAttempts() {
this.failedLoginAttempts++;
}
public void incrementHighSeverityEvents() {
this.highSeverityEvents++;
}
}
}

Spring Boot Integration

package com.example.sqreen.security;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Component
public class SecurityMonitoringInterceptor implements HandlerInterceptor {
private final SqreenSecurityManager securityManager;
public SecurityMonitoringInterceptor(SqreenSecurityManager securityManager) {
this.securityManager = securityManager;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
Object handler) throws Exception {
// Perform security check
SqreenSecurityManager.SecurityCheckResult checkResult = 
securityManager.checkRequest(request);
if (checkResult.isBlocked()) {
response.setStatus(429); // Too Many Requests or appropriate status
response.getWriter().write("Request blocked for security reasons");
return false;
}
// Add security headers
addSecurityHeaders(response);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
Object handler, Exception ex) throws Exception {
// Track request completion
if (ex != null) {
SecurityEvent errorEvent = new SecurityEvent(
SecurityEvent.EventType.SUSPICIOUS_ACTIVITY, 
SecurityEvent.ThreatLevel.MEDIUM
);
errorEvent.setIpAddress(getClientIpAddress(request));
errorEvent.withContext("exception", ex.getMessage());
errorEvent.withContext("status_code", response.getStatus());
securityManager.trackSecurityEvent(errorEvent);
}
// Identify user if authenticated
identifyAuthenticatedUser(request);
}
private void addSecurityHeaders(HttpServletResponse response) {
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader("X-Frame-Options", "DENY");
response.setHeader("X-XSS-Protection", "1; mode=block");
response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
}
private void identifyAuthenticatedUser(HttpServletRequest request) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated() && 
authentication.getPrincipal() instanceof UserDetails) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
Map<String, Object> traits = new HashMap<>();
traits.put("username", userDetails.getUsername());
traits.put("authorities", userDetails.getAuthorities());
securityManager.identifyUser(userDetails.getUsername(), traits);
}
}
private String getClientIpAddress(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
return request.getRemoteAddr();
}
}

Spring Security Integration

package com.example.sqreen.security;
import org.springframework.security.authentication.event.*;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class SpringSecurityEventListener {
private final SqreenSecurityManager securityManager;
public SpringSecurityEventListener(SqreenSecurityManager securityManager) {
this.securityManager = securityManager;
}
@EventListener
public void handleAuthenticationSuccess(AuthenticationSuccessEvent event) {
String username = event.getAuthentication().getName();
Map<String, Object> context = new HashMap<>();
context.put("authentication_type", event.getAuthentication().getClass().getSimpleName());
securityManager.trackAuthEvent(username, true, "spring_security", context);
}
@EventListener
public void handleAuthenticationFailure(AbstractAuthenticationFailureEvent event) {
String username = event.getAuthentication().getName();
Map<String, Object> context = new HashMap<>();
context.put("failure_type", event.getException().getClass().getSimpleName());
context.put("failure_message", event.getException().getMessage());
securityManager.trackAuthEvent(username, false, "spring_security", context);
}
@EventListener
public void handleAuthorizationEvent(AuthorizationEvent event) {
// Track authorization decisions
SecurityEvent authEvent = new SecurityEvent(
SecurityEvent.EventType.AUTH_BYPASS, 
SecurityEvent.ThreatLevel.MEDIUM
);
// Add authorization context
securityManager.trackSecurityEvent(authEvent);
}
}

REST Controller for Security Management

package com.example.sqreen.security;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
@RestController
@RequestMapping("/api/security")
public class SecurityController {
private final SqreenSecurityManager securityManager;
public SecurityController(SqreenSecurityManager securityManager) {
this.securityManager = securityManager;
}
@PostMapping("/track")
public ResponseEntity<Map<String, Object>> trackSecurityEvent(
@RequestBody SecurityEvent event) {
securityManager.trackSecurityEvent(event);
return ResponseEntity.ok(Map.of(
"success", true,
"message", "Security event tracked"
));
}
@PostMapping("/identify")
public ResponseEntity<Map<String, Object>> identifyUser(
@RequestBody IdentifyUserRequest request) {
securityManager.identifyUser(request.getUserId(), request.getTraits());
return ResponseEntity.ok(Map.of(
"success", true,
"message", "User identified"
));
}
@GetMapping("/metrics")
public ResponseEntity<SecurityMetrics> getSecurityMetrics() {
// Return current security metrics
SecurityMetrics metrics = new SecurityMetrics();
// Populate with actual metrics
return ResponseEntity.ok(metrics);
}
@PostMapping("/test/sql-injection")
public ResponseEntity<Map<String, Object>> testSqlInjectionDetection(
HttpServletRequest request) {
// This endpoint simulates SQL injection for testing
String testParam = request.getParameter("test");
SecurityEvent testEvent = new SecurityEvent(
SecurityEvent.EventType.SQL_INJECTION, 
SecurityEvent.ThreatLevel.HIGH
);
testEvent.setIpAddress(getClientIpAddress(request));
testEvent.withRequestData("test_parameter", testParam);
securityManager.trackSecurityEvent(testEvent);
return ResponseEntity.ok(Map.of(
"success", true,
"message", "SQL injection test completed"
));
}
private String getClientIpAddress(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
return request.getRemoteAddr();
}
// Request DTOs
public static class IdentifyUserRequest {
private String userId;
private Map<String, Object> traits;
// Getters and setters
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public Map<String, Object> getTraits() { return traits; }
public void setTraits(Map<String, Object> traits) { this.traits = traits; }
}
}

Configuration Class

package com.example.sqreen.security;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class SecurityConfig implements WebMvcConfigurer {
@Value("${datadog.api.key:}")
private String datadogApiKey;
@Value("${datadog.site:datadoghq.com}")
private String datadogSite;
@Value("${application.id:my-spring-app}")
private String applicationId;
@Bean
public SqreenSecurityManager sqreenSecurityManager() {
return new SqreenSecurityManager(datadogApiKey, datadogSite, applicationId);
}
@Bean
public SecurityMonitoringInterceptor securityMonitoringInterceptor() {
return new SecurityMonitoringInterceptor(sqreenSecurityManager());
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(securityMonitoringInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/health", "/metrics");
}
}

Best Practices for Production

  1. Proper Agent Configuration: Use Sqreen agent with -javaagent JVM parameter
  2. Data Sampling: Implement sampling for high-traffic applications
  3. Sensitive Data Masking: Never log sensitive information
  4. Performance Monitoring: Monitor the impact of security checks
  5. Alert Tuning: Fine-tune alerts to reduce false positives
  6. Compliance: Ensure compliance with data protection regulations
  7. Incident Response: Integrate with your incident response procedures
  8. Regular Updates: Keep dependencies and security rules updated

Conclusion

Implementing Sqreen/Datadog security in Java provides comprehensive runtime protection and security monitoring. The Java-based approach offers:

  • Real-time Threat Detection: Immediate identification of security threats
  • Behavioral Analysis: Detection of anomalous user behavior
  • Integration with Spring Security: Seamless authentication and authorization monitoring
  • Datadog Integration: Centralized security observability
  • Custom Security Rules: Tailored protection for your application
  • Comprehensive Metrics: Security posture assessment and reporting

By integrating these security measures, you can significantly enhance your application's security posture, detect threats in real-time, and maintain comprehensive security observability through the Datadog platform.

Leave a Reply

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


Macro Nepal Helper