SQL injection - Code
Need
Implementation of proper input validation and the use of parameterized statements or stored procedures to prevent SQL injection attacks.
Context
- Usage of Scala for building scalable and functional applications
- Usage of Anorm for type-safe database access in Scala
- Usage of play.api.db.DB for database operations in Play Framework
- Usage of the Play.current object in the Play framework for accessing the current application instance
Description
Non compliant code
import anorm._
import play.api.db.DB
import play.api.Play.current
def getUser(username: String, password: String): User = {
DB.withConnection { implicit connection =>
SQL("SELECT * FROM users WHERE username = " + username + " AND password = " + password)
.as(User.parser.singleOpt)
}
}
In the above Scala code, the getUser
function is vulnerable to SQL Injection. The function takes a username
and password
as parameters and directly interpolates them into a SQL query string.
The query is intended to select a user from the users
table where the username
and password
match the provided parameters. However, because the parameters are directly concatenated into the query string, an attacker could potentially manipulate the SQL query by providing specially crafted input.
For example, if an attacker provides "admin'; --"
as the username
and any random string as the password
, the SQL query becomes:
SELECT * FROM users WHERE username = 'admin'; -- AND password = 'random_string'
The --
in SQL starts a comment, causing the rest of the query to be ignored. This would effectively log in the attacker as the 'admin' user, regardless of the password
provided.
This vulnerability can lead to unauthorized access, data leakage, data loss, or even denial of service in severe cases.
Steps
- Use parameterized statements or prepared statements to construct SQL queries.
- Validate and sanitize user input before using it in SQL queries.
- Avoid concatenating user input directly into SQL queries.
- Use an ORM (Object-Relational Mapping) framework to handle database interactions.
- Implement proper authentication and authorization mechanisms to prevent unauthorized access to sensitive data.
Compliant code
import anorm._
import play.api.db.DB
import play.api.Play.current
def getUser(username: String, password: String): User = {
DB.withConnection { implicit connection =>
SQL("SELECT * FROM users WHERE username = {username} AND password = {password}")
.on('username -> username, 'password -> password)
.as(User.parser.singleOpt)
}
}
The original code was vulnerable to SQL Injection attacks because it was directly concatenating user input into the SQL query. This could allow an attacker to manipulate the SQL query by providing specially crafted input.
The fixed code uses parameterized queries to prevent SQL Injection. In a parameterized query, placeholders are used instead of directly embedding user input in the query. The user input is then supplied as parameters that replace the placeholders in the query. This ensures that the user input is properly escaped, and cannot be used to inject malicious SQL code.
In the fixed code, {username}
and {password}
are placeholders in the SQL query. The actual values of username
and password
are supplied with the .on('username -> username, 'password -> password)
method. This ensures that the username
and password
values are properly escaped and cannot be used to manipulate the SQL query.
This fix also assumes that the username
and password
values are validated and sanitized before the getUser
method is called. It's important to always validate and sanitize user input to prevent other types of attacks.
In addition to using parameterized queries, it's also recommended to use an ORM (Object-Relational Mapping) framework to handle database interactions, and to implement proper authentication and authorization mechanisms to prevent unauthorized access to sensitive data.