Alex Makumbi
08 January 2017
In this article I create a simple form to demonstrate insecure interaction between a java based component
and the outside world explaining why the form poses as a vulnerability to the overall application.
Finally, I show how to properly secure the application.
A simple Java application is created with a simple login form that retrieves username and password using
two JTextFields and storing them into the Derby Database.
import java.sql.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class JavaAppInsecureSQL implements ActionListener {
private static String dbURL = "jdbc:derby://localhost:1527/derbyDB;create=true;";
private static String tableName = "appusers";
// jdbc Connection
private static Connection conn = null;
private static Statement stmt = null;
JPanel totalGUI = new JPanel();
JPanel textPanel, panelForTextFields, completionPanel;
JLabel titleLabel, usernameLabel, passwordLabel, userLabel, passLabel;
JTextField usernameField, loginField;
JButton loginButton;
String usernameText;
String passwordText;
public JPanel createContentPane(){
// create bottom panel as a base for pane content
totalGUI.setLayout(null);
titleLabel = new JLabel("Login Screen");
titleLabel.setLocation(0,0);
titleLabel.setSize(290, 30);
titleLabel.setHorizontalAlignment(0);
totalGUI.add(titleLabel);
// Creation of a Panel to contain the JLabels
textPanel = new JPanel();
textPanel.setLayout(null);
textPanel.setLocation(10, 35);
textPanel.setSize(70, 80);
totalGUI.add(textPanel);
// Username Label
usernameLabel = new JLabel("Username");
usernameLabel.setLocation(0, 0);
usernameLabel.setSize(70, 40);
usernameLabel.setHorizontalAlignment(4);
textPanel.add(usernameLabel);
// Login Label
passwordLabel = new JLabel("Password");
passwordLabel.setLocation(0, 40);
passwordLabel.setSize(70, 40);
passwordLabel.setHorizontalAlignment(4);
textPanel.add(passwordLabel);
// TextFields Panel Container
panelForTextFields = new JPanel();
panelForTextFields.setLayout(null);
panelForTextFields.setLocation(110, 40);
panelForTextFields.setSize(100, 70);
totalGUI.add(panelForTextFields);
// Username Textfield
usernameField = new JTextField(8);
usernameField.setLocation(0, 0);
usernameField.setSize(100, 30);
panelForTextFields.add(usernameField);
// Login Textfield
loginField = new JTextField(8);
loginField.setLocation(0, 40);
loginField.setSize(100, 30);
panelForTextFields.add(loginField);
// Creation of a Panel to contain the completion JLabels
completionPanel = new JPanel();
completionPanel.setLayout(null);
completionPanel.setLocation(240, 35);
completionPanel.setSize(70, 80);
totalGUI.add(completionPanel);
// Username Label
userLabel = new JLabel("Wrong");
userLabel.setForeground(Color.red);
userLabel.setLocation(0, 0);
userLabel.setSize(70, 40);
completionPanel.add(userLabel);
// Login Label
passLabel = new JLabel("Wrong");
passLabel.setForeground(Color.red);
passLabel.setLocation(0, 40);
passLabel.setSize(70, 40);
completionPanel.add(passLabel);
// Button for Logging in
loginButton = new JButton("Login");
loginButton.setLocation(130, 120);
loginButton.setSize(80, 30);
loginButton.addActionListener(this);
totalGUI.add(loginButton);
totalGUI.setOpaque(true);
return totalGUI;
}
public void actionPerformed(ActionEvent e) {
if(e.getSource() == loginButton)
{
if(!(usernameField.getText().trim().isEmpty()))
{
usernameText = usernameField.getText().trim();
userLabel.setForeground(Color.green);
userLabel.setText("Correct!");
}
else
{
userLabel.setForeground(Color.red);
userLabel.setText("Wrong!");
}
if(!(loginField.getText().trim().isEmpty()))
{
passwordText = loginField.getText().trim();
passLabel.setForeground(Color.green);
passLabel.setText("Correct!");
}
else
{
passLabel.setForeground(Color.red);
passLabel.setText("Wrong!");
}
if((userLabel.getForeground() == Color.green)
&& (passLabel.getForeground() == Color.green))
{
try{
titleLabel.setText("Storing into database....");
// insert user information into database
insertUserInfor(usernameText, passwordText);
titleLabel.setText("Information stored!");
}
catch(Exception ex){
ex.getStackTrace();
}finally{
loginButton.setEnabled(false);
// shutdown database connections
shutdown();
}
}
}
}
private static void createAndShowGUI() {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame(" Insecure SQL Java App ");
JavaAppInsecureSQL demo = new JavaAppInsecureSQL();
frame.setContentPane(demo.createContentPane());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(310, 200);
frame.setVisible(true);
}
public static void main(String[] args){
//Event-dispatching thread:
//creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createConnection()
{
try
{
Class.forName("org.apache.derby.jdbc.ClientDriver").newInstance();
//Get a connection
conn = DriverManager.getConnection(dbURL);
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException except)
{
except.printStackTrace();
}
}
private static void insertUserInfor(String userName, String userPass)
{
try
{
createConnection();
stmt = conn.createStatement();
stmt.execute("insert into " + tableName + " values ('" +
userName + "', '" + userPass + "')");
stmt.close();
}
catch (SQLException sqlExcept)
{
sqlExcept.printStackTrace();
}
}
private static void shutdown()
{
try
{
if (stmt != null)
{
stmt.close();
}
if (conn != null)
{
DriverManager.getConnection(dbURL + ";shutdown=true");
conn.close();
}
}
catch (SQLException sqlExcept)
{
sqlExcept.getStackTrace();
}
}
}
Once login button is pressed, the username and password is sent to be stored in the database.
Note: The simple login application does not take into account other software vulnerabilities.
We’re only demonstrating SQL Injection vulnerability.
The application works great, but when we take a look under the hood we find that this application is vulnerable
to SQL Injection attacks because SQL statements are not prepared and are dynamically storing data directly into
the database.
private static void insertUserInfor(String userName, String userPass)
{
try
{
createConnection();
stmt = conn.createStatement();
stmt.execute("insert into " + tableName + " values ('" +
userName + "', '" + userPass + "')");
stmt.close();
}
catch (SQLException sqlExcept)
{
sqlExcept.printStackTrace();
}
}
SQL Injection attacks are the most prevent attacks to an application and inflicts the most damage exposing
sensitive data. Injection attacks attempt to break into application databases by injecting malicious code,
oftentimes in a form of SQL statements. They are often injected through form fields similar to the small
application I created, but are also injected through uploads, 3rd party APIs, configuration files, input files etc.
The code snippet above shows the application retrieving username and password directly placing it into the SQL
statement without validating or sanitizing the data. Essentially, the developer trusts the user not to
inject malicious code. The user should never be trusted. Below I demonstrate how to properly prepare SQL statements
in java.
private static void insertUserInfor(String userName, String userPass)
{
try
{
// establish connection
createConnection();
String qTxt = "INSERT INTO " + tableName + "VALUES (?,?)";
prepStmt = conn.prepareStatement(qTxt);
prepStmt.setString(1, userName);
prepStmt.setString(1, userPass);
prepStmt.close();
}
catch (SQLException sqlExcept)
{
sqlExcept.printStackTrace();
}
}
When SQL statements are prepared information received from the user is placed in a prepared statement and not directly
into the SQL query, thus mitigating dynamic queries.
In this demonstration, I have alluded to mention another measure to take in order to mitigate SQL Injection attacks,
which is to validate and sanitize data retrieved from the user. As we’ve mentioned, the application cannot trust data
from the outside world. Information must be validated for content, length, format, and other factors before use.