Implementation of proper input validation and the use of parameterized statements or stored procedures to prevent SQL injection attacks.
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class VulnerableServlet extends HttpServlet {
private Connection connection;
public void init() throws ServletException {
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost/testDB", "user", "password");
} catch (Exception e) {
throw new ServletException(e);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String id = request.getParameter("id");
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM Users WHERE id = " + id);
// process the result
} catch (SQLException e) {
throw new ServletException(e);
}
}
}
The above code is a simple servlet that connects to a MySQL database and fetches a user based on the provided ID. The ID is taken directly from the request parameter and concatenated into the SQL query. This is a classic example of an SQL injection vulnerability.
The problem arises in the
doGet
method. Here, the
id
is directly taken from the request parameters and used in the SQL query without any validation or sanitization. This allows an attacker to manipulate the SQL query by sending a specially crafted
id
parameter.
For instance, an attacker could send
1 OR 1=1
as the
id
, which would result in the query
SELECT * FROM Users WHERE id = 1 OR 1=1
. Since
1=1
is always true, this would return all users, potentially leaking sensitive information.
Even worse, an attacker could send
1; DROP TABLE Users; --
as the
id
, which would result in the query
SELECT * FROM Users WHERE id = 1; DROP TABLE Users; --
. This would delete the entire Users table from the database.
To fix this vulnerability, never build SQL queries by concatenating user inputs. Instead, use prepared statements or stored procedures, which can safely parameterize inputs.
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class SecureServlet extends HttpServlet {
private Connection connection;
public void init() throws ServletException {
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost/testDB", "user", "password");
} catch (Exception e) {
throw new ServletException(e);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String id = request.getParameter("id");
// Validate and sanitize user input
if (id != null && id.matches("\\\\d+")) {
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM Users WHERE id = ?");
preparedStatement.setInt(1, Integer.parseInt(id));
ResultSet resultSet = preparedStatement.executeQuery();
// process the result
} else {
// handle invalid input
}
} catch (SQLException e) {
throw new ServletException(e);
}
}
}
The above code fixes the SQL Injection vulnerability by using a PreparedStatement instead of a Statement. A PreparedStatement allows us to use placeholders (?) for our parameters, which are then filled in by the PreparedStatement object in a safe manner, preventing SQL Injection attacks.
The user input is also validated and sanitized before it is used in the SQL query. The
matches("\\\\d+")
method checks if the input is a number. If the input is not a number, the code does not execute the SQL query, preventing potential SQL Injection attacks.
The code also implements proper error handling. If a SQLException occurs, it is caught and a ServletException is thrown. This allows the application to handle the error in a controlled manner, preventing potential information leakage to the attacker.