The document discusses SQL injection vulnerabilities and how to prevent them. It begins with an example of authenticating a user called "tcon" and shows how appending a single quote to the password leads to a SQL syntax error, revealing that user input is directly embedded in the SQL query without validation or escaping. It then demonstrates how an attacker can bypass authentication by injecting SQL syntax into the password field to alter the query's meaning. Finally, it provides a solution using prepared statements, which separate the SQL query from user input, preventing injection by treating inputs as literal values rather than SQL code.
3. ONLINE BANK
tcon@email.com
password
Log in
Our website is secure, almost never get hacked
Lets assume the
attacker trying to guess
what is the password of
tcon user
Unknown username and password
An error message
prompted
4. ONLINE BANK
tcon@email.com
password’
Log in
Our website is secure, almost never get hacked
By adding a single
quote (‘) at the end of
the password, lets see
the behavior of the
application
An unexpected Error occured
An error message
prompted
The logs show a
SQL Syntax error.
This indicates that
The quote character
Messed something
up
In an unexpected
way
5. SELECT *
FROM users
WHERE email = ‘?’
AND pass = ‘?’ LIMIT 1
This is what the
application code looks
like behind the scenes.
Let’s see how the SQL
code gets built as you
enter your login details
6. ONLINE BANK
tcon@email.com
password’
Log in
Our website is secure, almost never get hacked
An unexpected Error occurred
"'password'' limit 1" LINE 1: ...ers where email
= ‘tcon@email.com' and password =
'password'... ^ : select * from users where
email = ‘tcon@email.com' and password =
'password'' limit 1.Unable to login this user
due to unexpected error.
7. Unexpected Error
SELECT *
FROM users
WHERE email = ‘tcon@email.com’
AND pass = ‘password’’ LIMIT 1
This is what the
application code looks
like behind the scenes.
Let’s see how the SQL
code gets built as you
enter your login details
8. Authentication Bypass 1
ONLINE BANK
Email Address
Password
Log in
Our website is secure, almost never get hacked
Enter the following credentials and click “Log
In”:
Email:
tcon@email.com
Password:
‘ or 1=1--
9. Authentication Bypass 1
ONLINE BANK
tcon@email.com
‘ or 1=1--
Log in
Our website is secure, almost never get hacked
Rendering login page.
Checking supplied authentication details for
tcon@email.com.
Finding user in database.
Authentication details confirmed,
establishing session for this user.
10. Authentication Bypass 1
ONLINE BANK
Bank Accounts
Account Available Balance Present Balance
Checking PHP 80,260.56 PHP 80,260.56
SavingsPHP 95,895.96 PHP 95.895.96
Transfer Funds!
11. Authentication Bypass 1
SELECT *
FROM users
WHERE email = ‘tcon@email.com’
AND pass = ‘1 or 1=1--’ LIMIT 1
The - - characters you entered cause the database
to ignore the rest of the SQL statement, allowing
you to be authenticated without having to supply
the real password
13. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
Connection connection = pool.getConnection();
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(sql);
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
14. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
Connection connection = pool.getConnection();
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(sql);
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
To validate user credentials, the
request.getParamenter() method is
first called to extract tcon’s email
(from HTTP request parameter)
which is assigned to the email
variable
15. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
Connection connection = pool.getConnection();
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(sql);
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
Similarly, the
request.getParameter() method is
called to extract tcon’s password
value and assigned to password
variable
16. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
Connection connection = pool.getConnection();
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(sql);
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
The string variable sql is then declared
which represents the SQL query used to
authenticate tcon’s credentials.
Note, the tcon’s email and password value
are concatenated to build the final query
17. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
Connection connection = pool.getConnection();
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(sql);
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
The SQL query in defined in sql string variable is then executed by invoking
the executeQuery method. This method executes our query against the
backend SQL server and returns a result as ResultSet object which is on
result.next through the if/else block
Finally, should tcon’s credentials match the loggedIn variable is set to true
and tcon is redirected to its profile page.
19. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
Connection connection = pool.getConnection();
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(sql);
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
20. Code walkthrough
//string email = request.getParameter("Email");
//String password = request.getParameter("password");
String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
//Connection connection = pool.getConnection();
//Statement statement = connection.createStatement();
//ResultSet result = statement.executeQuery(sql);
//if (result.next()) {
//LoggedIn = true;
// #Successfully logged in and redirect to user profile page
//} else {
// #Authentication failure - Redirect to login page
//}
Now that we have simplified our authentication logic code, lets analyze the
SQL query generated by Bank to validate tcon’s authentication credentials
21. Understanding Injection
Step 1: Enter the password tcon@email.com and watch the code widow.
Notice how the single quote (‘) you appended is interpreted by the SQL server
as a string delimiter.
However, when the query is processed, the last quote does not have a
closing/matching (‘) character, which cause a SQL Syntax Error, resulting in
the HTTP 500 Internal server Error
Step 2: Now try logging with a password followed by two single quotes e.g.
password’’ Interestingly the application does nor error in this case
25. Action:1
ONLINE BANK
Our website is secure, almost never get hacked
500 Internal Server Error.
The server encounter an internal error or misconfiguration and was
unable to complete your request. Please contact your handsome
administrator and inform them of the time the error occurred, and
anything might have done that may have caused the error.
Also by them a coffee or burger so that they will fix your problem
28. Bypassing Authentication
At this point we know that injecting characters interpreted by the database
server is known as SQL injection
How ever, it’s not just (‘) characters that can be injected, entire strings can be
injected, entire strings can be injected. What if this could be used to alter the
purpose of the SQL Statement Entirely?
29. Bypassing Authentication
ONLINE BANK
tcon@email.com
Password ‘ 1 or 1=1)#
Log in
Our website is secure, almost never get hacked
Note in MySQL the (#) character is used for
code comments. Keep an eye on the code,
everything to the right of the (#) character is
commented our, including the extra (‘) and )
character.
Note: the provided SQL payload is the one
must use
31. Bypassing Authentication
ONLINE BANK
Bank Accounts
Account Available Balance Present Balance
Checking PHP 80,260.56 PHP 80,260.56
SavingsPHP 95,895.96 PHP 95.895.96
Transfer Funds!
32. Authentication was bypass,
but why?
The credentials we provide on the login resulted in the following SQL
statement:
SELECT *
WHERE (email = ‘tcon@email.com’)
AND password = ‘ ‘ OR 1=1)#
Because the statement is both syntactically valid and OR 1=1 always return
true, the authentication mechanism was bypassed. Let’s now analyze the
Vulnerability from a code perspective
33. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
Connection connection = pool.getConnection();
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(sql);
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
To quickly recap, the
request.getParameter() method is
first called to extract tcon’s
username and password values
which are assigned to the email
and password variable
34. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
Connection connection = pool.getConnection();
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(sql);
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
The string variable sql is then declared, which
represents the SQL query used to authenticate
tcon’s credentials
35. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
Connection connection = pool.getConnection();
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(sql);
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
Notice that tcon’s email and password input (retrieved using the
request.getParameter() method) are concatenated within the variabl sql ,
without any input validation checks.
36. Input Validation
Because there is no input validation (i.e. no checking of legal characters,
minimum/maximum string lengths, or removal of “malicious” characters)
TCON has the ability to inject raw SQL syntax within the username and
password input fields to alter the meaning of the underlying SQL query
responsible for authentication, resulting in a bypass of the application’s
authentication mechanism
A SQL Injection Attack!
38. Remediation
Prepared Statements (A.K.A parameterized queries) are the best mechanism for
preventing SQL injection attacks
Prepared statements are used to abstract SQL statement syntax from input
parameters. Statement templates are first defined at the application layer, and the
parameters are then passed to them
In java this can be achieved using PreparedStatement class for sending SQL
statements to the backend database
Aside from a better security posture against SQL injection attacks, prepared
statements offer improved code quality from a legibility and maintainability
perspective due to separation of SQL logic from inputs
39. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
//String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
String sql = “select * from users where email = ? and password = ?”;
Connection connection = pool.getConnection();
//Statement statement = connection.createStatement();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//ResultSet result = statement.executeQuery(sql);
preparedStatement.setString(1, email);
preparedStatement.setString(2, password);
ResultSet result = preparedStatement.executeQuery();
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
In our modified code example, we first declared the authentication query
string and assign it to the sql string variable. Notice that the email and
password variable have now been replace with (?) symbol which act as a
place holder for java’s PreparedStatement class.
40. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
//String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
String sql = “select * from users where email = ? and password = ?”;
Connection connection = pool.getConnection();
//Statement statement = connection.createStatement();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//ResultSet result = statement.executeQuery(sql);
preparedStatement.setString(1, email);
preparedStatement.setString(2, password);
ResultSet result = preparedStatement.executeQuery();
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
The sql string variable is then passed as an argument to the
preparedStatement() method, which precompiles the SQL query and creates
a PreparedStatement object for sending parameteized SQL statements to
the back end SQL Server
41. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
//String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
String sql = “select * from users where email = ? and password = ?”;
Connection connection = pool.getConnection();
//Statement statement = connection.createStatement();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//ResultSet result = statement.executeQuery(sql);
preparedStatement.setString(1, email);
preparedStatement.setString(2, password);
ResultSet result = preparedStatement.executeQuery();
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
Similarly, the setString() method is then called to pass the password
parameter value to our prepared statement.
42. Code walkthrough
string email = request.getParameter("Email");
String password = request.getParameter("password");
//String sql = "select * from users where (email='" + email +"' and password='" + password +"')";
String sql = “select * from users where email = ? and password = ?”;
Connection connection = pool.getConnection();
//Statement statement = connection.createStatement();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//ResultSet result = statement.executeQuery(sql);
preparedStatement.setString(1, email);
preparedStatement.setString(2, password);
ResultSet result = preparedStatement.executeQuery();
if (result.next()) {
LoggedIn = true;
#Successfully logged in and redirect to user profile page
} else {
#Authentication failure - Redirect to login page
}
Finally, we execute our authentication query by invoking the
preparedStatement.executeQuery() method. The SQL used by prepareStatement is
precompiled ensuring that all parameters sent to underlying database are treated as literal
value and not SQL statement/query language, ensuring that no SQL code can be injected
using an untrusted parameter.
Ultimately, the security payoff with using prepared statements is that the database will
ensure that parameters are automatically escaped
43. END OF THE SLIDE
HACKTHENORTH.ORG
References:
Hackplaining
OWASP Foundation
CodeBashing
Editor's Notes
The Application crash unexpected! What does it mean?
The quote is inserted directly into the SQL string , and terminates the query early, this is what caused
The syntax error we saw in the logs. This behavior indicates that the application might be vulnerable
To SQL injection
And we are in! we successfully gained access to the application having to guess the password, using
SQL Injection.
And we are in! we successfully gained access to the application having to guess the password, using
SQL Injection.