Fix Insufficient Logging & Monitoring in Javalin
Insufficient Logging & Monitoring (OWASP A09:2021) is a gift to attackers. In Javalin, failing to log security-critical events like auth failures, 500-series errors, or input validation drops allows adversaries to brute-force or probe your API in total silence. Silence is the enemy; you need structured, searchable logs to detect and respond to an active breach.
The Vulnerable Pattern
import io.javalin.Javalin;public class App { public static void main(String[] args) { Javalin app = Javalin.create().start(7000);
app.post("/login", ctx -> { String user = ctx.formParam("username"); if ("admin".equals(user) && "password".equals(ctx.formParam("password"))) { ctx.status(200).result("Access Granted"); } else { // VULNERABILITY: No logging on failure. // An attacker can brute-force this without triggering any alerts. ctx.status(401).result("Unauthorized"); } }); app.get("/data", ctx -> { // VULNERABILITY: Generic exception handling might leak info or stay silent throw new RuntimeException("Database connection failed"); }); }
}
The Secure Implementation
To harden Javalin, we implement three layers of visibility. First, we use 'config.requestLogger' to provide a baseline trail of all traffic. Second, we implement a global exception handler via 'app.exception' to ensure that logic failures are logged with full stack traces on the server while returning generic errors to the client to prevent information disclosure. Third, we explicitly log security-relevant events (Audit Logs) such as failed authentication. For production, ensure these logs are piped via SLF4J to a structured appender like Logstash or Graylog for real-time monitoring and automated alerting on 4xx/5xx spikes.
import io.javalin.Javalin; import org.slf4j.Logger; import org.slf4j.LoggerFactory;public class SecureApp { private static final Logger logger = LoggerFactory.getLogger(SecureApp.class);
public static void main(String[] args) { Javalin app = Javalin.create(config -> { // 1. Request Logging: Track every request/response lifecycle config.requestLogger.http((ctx, ms) -> { logger.info("HTTP {} {} - Status: {} in {}ms", ctx.method(), ctx.path(), ctx.status(), ms); }); }).start(7000); // 2. Global Exception Logging: Capture stack traces without leaking them to the client app.exception(Exception.class, (e, ctx) -> { logger.error("Unhandled exception during {} {}: ", ctx.method(), ctx.path(), e); ctx.status(500).result("Internal Server Error"); }); app.post("/login", ctx -> { String user = ctx.formParam("username"); if ("admin".equals(user) && "password".equals(ctx.formParam("password"))) { logger.info("Security Event: Successful login for user '{}' from IP: {}", user, ctx.ip()); ctx.status(200).result("Access Granted"); } else { // 3. Audit Logging: Log failed security attempts for SIEM alerting logger.warn("Security Event: Failed login attempt for user '{}' from IP: {}", user, ctx.ip()); ctx.status(401).result("Unauthorized"); } }); }
}
Your Javalin API
might be exposed to Insufficient Logging & Monitoring
74% of Javalin apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.
Free Tier • No Credit Card • Instant Report
Verified by Ghost Labs Security Team
This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.