Top Java Security Vulnerabilities and How to Prevent Them in Modern Java
Most Java security breaches stem from preventable coding mistakes. Follow secure coding practices, validate inputs, and keep dependencies updated to reduce risk.
Join the DZone community and get the full member experience.
Join For FreeWith the increasing number of security threats, organizations have invested heavily in cybersecurity initiatives to protect their applications, infrastructure, and sensitive data. Security vulnerabilities are rarely introduced intentionally. Most of them creep into applications through shortcuts, overlooked edge cases, outdated libraries, or some bad coding habits.
Modern Java has significantly improved its security capabilities, but no framework or JVM version can completely protect an application from insecure coding practices. As developers, we still need to understand where vulnerabilities originate and how to prevent them before they reach production.
In this article, I am trying to summarize some of the most common Java security vulnerabilities and practical techniques used to prevent them. These are the same security best practices and lessons learned that I frequently share with new team members joining my team. I am sharing them here in the hope that they can serve as a practical handbook for Java developers looking to build more secure applications.
1. SQL Injection
SQL Injection remains one of the oldest and most dangerous vulnerabilities. It occurs when user input is directly concatenated into SQL statements.
Consider the following example:
String query =
"SELECT * FROM users WHERE username = '" +
username + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
If an attacker enters, the query can be manipulated to return unintended results.
admin' OR '1'='1
Prevention
Always use parameterized queries.
String query =
"SELECT * FROM users WHERE username = ?";
PreparedStatement stmt =
connection.prepareStatement(query);
stmt.setString(1, username);
ResultSet rs = stmt.executeQuery();
Prepared statements separate data from executable SQL, eliminating injection opportunities.
2. Hardcoded Secrets
One of the most common findings during security reviews is hardcoded credentials.
private static final String API_KEY =
"abcd123456789";
This may seem harmless during development, but once committed to source control, secrets often remain exposed indefinitely.
Prevention
Store secrets externally.
String apiKey =
System.getenv("PAYMENT_API_KEY");
Better alternatives are to include it in AWS Secrets Manager, Azure Key Vault, HashiCorp Vault, or Kubernetes Secrets. Secrets should never live inside source code repositories.
3. Insecure Deserialization
Java serialization has been responsible for numerous security incidents.
Example:
ObjectInputStream input =
new ObjectInputStream(request.getInputStream());
Object obj = input.readObject();
The danger is that attackers can craft malicious serialized objects that execute unexpected code during deserialization.
Prevention
Avoid Java serialization whenever possible. Prefer formats such as JSON, XML (with secure parsing), or Protocol Buffers.
Example using Jackson:
ObjectMapper mapper =
new ObjectMapper();
User user =
mapper.readValue(json, User.class);
Using structured formats reduces attack surfaces significantly.
4. Cross-Site Scripting (XSS)
Although often associated with front-end applications, backend services can accidentally enable XSS vulnerabilities when user-generated content is returned without sanitization.
Example:
String comment =
request.getParameter("comment");
response.getWriter().write(comment);
If the user submits, the browser executes the script.
<script>alert('Hacked')</script>
Prevention
Always encode output.
Using Spring:
String safeComment =
HtmlUtils.htmlEscape(comment);
Additionally, validate inputs, sanitize rich text, and implement Content Security Policies (CSP).
5. Path Traversal Attacks
File download functionality often introduces path traversal vulnerabilities.
Example:
String file =
request.getParameter("file");
Path path =
Paths.get("/documents/" + file);
An attacker could submit and potentially access sensitive files.
../../../etc/passwd
Prevention
Normalize and validate paths.
Path base =
Paths.get("/documents");
Path resolved =
base.resolve(file).normalize();
if (!resolved.startsWith(base)) {
throw new SecurityException(
"Invalid file path");
}
Never trust file names coming directly from user input
6. Weak Password Storage
Storing passwords improperly remains surprisingly common.
Bad practice:
String passwordHash =
DigestUtils.md5Hex(password);
MD5 and SHA-1 are no longer considered secure for password storage.
Prevention
Use adaptive hashing algorithms.
Example with BCrypt:
BCryptPasswordEncoder encoder =
new BCryptPasswordEncoder();
String hash =
encoder.encode(password);
BCrypt automatically includes salting and work-factor adjustments.
Other strong alternatives include Argon2, PBKDF2 or SCrypt
7. Dependency Vulnerabilities
Modern Java applications often contain more third-party code than custom code.
A secure application can still become vulnerable because of outdated dependencies.
Prevention
Integrate dependency scanning into CI/CD pipelines.
Example Maven plugin:
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
</plugin>
Additionally, tools such as Snyk can automatically identify known vulnerabilities. We have been using Snyk for the last couple of years, and it is effective.
Regular dependency updates should be part of every release cycle.
8. Improper Logging of Sensitive Data
Developers often log information for troubleshooting without considering security implications.
Example:
logger.info(
"Login request received for user={} password={}",
username,
password);
This exposes credentials inside log files.
Prevention
Mask or exclude sensitive information.
logger.info(
"Login request received for user={}",
username);
Never log passwords, access tokens, credit card information, Personal health information (PHI), or PII information. This is especially important in regulated industries such as healthcare, like ours.
9. Insufficient Authentication and Authorization
Authentication verifies identity, and authorization determines access. Many applications perform authentication correctly but fail to enforce authorization consistently.
Example:
@GetMapping("/admin/users")
public List<User> getUsers() {
return userService.findAll();
}
Without authorization checks, any authenticated user might gain access.
Prevention
Use role-based security.
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/users")
public List<User> getUsers() {
return userService.findAll();
}
Security should be enforced at every layer, not just the UI.
10. Lack of Input Validation
Many vulnerabilities originate from accepting unexpected input.
Example:
String age =
request.getParameter("age");
int userAge =
Integer.parseInt(age);
Invalid input can cause exceptions or unexpected behavior.
Prevention
Validate all external input.
@Min(18)
@Max(120)
private Integer age;
Bean Validation provides a simple and consistent approach for validating request payloads. Never assume user input is safe.
Final Thoughts
Security is not a feature that can be added at the end of a project. It needs to be part of the development process from the very beginning.
The vulnerabilities discussed here are not theoretical. They are among the most common findings during security assessments, penetration tests, and production incident investigations.
Fortunately, modern Java provides mature frameworks, libraries, and tools that make secure development significantly easier than it was a decade ago.
The key is building security awareness into everyday development practices:
- Use parameterized queries
- Protect secrets properly
- Validate all inputs
- Keep dependencies updated
- Apply strong authentication and authorization
- Log responsibly
- Continuously scan for vulnerabilities
Security is ultimately about reducing risk. Small improvements applied consistently across a codebase can prevent incidents that would otherwise become expensive lessons later.
Opinions expressed by DZone contributors are their own.
Comments