What is SQL Injection?
SQL injection (SQLi) involves adding malicious code to a database query to gain unauthorized access to a web application’s database. Threat actors employ SQL injection techniques to manipulate SQL code, intending to execute malicious code that can help them gain access to sensitive data or compromise the database server.
A successful SQL injection attack can potentially expose any data stored by the database, including intellectual property, administrative credentials, and customer data. Threat actors can use SQL injection to target any SQL database, such as MySQL, SQL Server, and Oracle. Typically, SQL injection attacks target web applications using a database on the back end.
SQL injection is a common security exploit. Threat actors employ this technique frequently, using automated tools to increase the number of attacks they can launch and the scope of the attack. SQL injection is ranked #3 in the OWASP Top 10 lists of web application vulnerabilities.
In this article:
- How Do SQL Injection Attacks Work?
- Example of SQL Injection in Python
- 4 Tips for Preventing SQL Injection in Python
- DAST Testing for Python Applications with Bright Security
How Do SQL Injection Attacks Work?
Threat actors launch SQL injection attacks by first identifying vulnerable user inputs in a web application or page employing user input directly within an SQL query. This vulnerability allows actors to create and send input content (malicious payload) that executes malicious SQL commands in the database.
Web applications and sites typically store all data in SQL databases. SQL is a query language that manages the data stored in a relational database. SQL commands perform actions on data, allowing access, deletion, and modification. It may also allow running operating system commands. As a result, successful SQL injection attacks can lead to critical consequences.
Threat actors launch SQL injection attacks to gain unauthorized access to data and then steal it, modify it, delete it, or perform malicious actions on the breached database. For example, SQL injection can grant access to user credentials, allowing actors to impersonate database users. If the user is a database administrator, the actor gains access to all database privileges.
SQL enables authorized users to choose data and output it from the database. SQL injection vulnerabilities can allow threat actors to gain unauthorized access to all data in the database server. Threat actors can use the privileged obtained through SQL injection to modify data, add new data to the database, delete records, and drop tables.
Example of SQL Injection in Python
The following example shows a SQL injection vulnerability in a Flask application. It is based on code provided by SecureFlag.
The application defines a route for the URL /login and requests credentials from the user:
username = request.values.get('username')
password = request.values.get('password')
Next, the application connects to a database running on the localhost:
db = pymysql.connect("localhost")
cursor = db.cursor()
This part of the application is vulnerable to SQL injection. The app runs a SQL query in which it insecurely concatenates the username and password fields:
cursor.execute("SELECT * FROM users WHERE username = '%s' AND password = '%s'" % (username, password))
If the query returns a matching record, the application logs the user in:
cord = cursor.fetchone()
session['logged_user'] = username
Because the application accepts user inputs and processes them with no validation as part of the SQL query, it is possible for the attacker to switch context and override the authentication mechanism.
For example, the attacker could inject the following string into the username field:
john' OR 'a'='a';--
The following query is then submitted to the database:
SELECT * FROM users WHERE username = 'john' OR 'a'='a';-- AND password = '';
Because the ‘a’=’a’ statement is always true, the expression allows the attacker to login with the username john, if it exists, or with the first entry in the user table. The characters ;– comment out the rest of the SQL query, causing the application to ignore the password field.
See examples of real-life attacks in our guide to SQL injection attacks
4 Tips for Preventing SQL Injection in Python
The most important way to prevent SQL injection is to avoid vulnerable code and insecure coding practices. Here are a few ways to do that—they will be effective against SQL injection and many other vulnerabilities that can affect your Python code.
1. Insecure Packages
When you import a module into a Python application, the interpreter runs the code. This means you should be careful when importing modules.
The PyPi package index is a great resource, but there is no verification that all the code in libraries listed there is secure. Many malicious packages exist on PyPi, some of them attempt to trick users by adopting the names of well known libraries with small misspellings.
If you are unsure of the authenticity and contents of the outer packaging, investigate further, and if you are still unsure about its origin or security status, don’t use it.
2. Identifying Vulnerabilities
The first step in preventing vulnerabilities is to create a checklist of security best practices and review it before releasing your code or promoting it to a test environment. You should adhere to these best practices at the development stage, and automatically verify them at the testing stage. Ideally, you should adopt automated tools that scan your code at all stages of the software development lifecycle (SDLC).
3. Use Linters and Static Analysis Tools
Linters are tools that provide automated recommendations about good coding practices. They are a simple form of static application security testing (SAST) tools, which analyze source code during the development phase of a project. Linters can be used manually in the editor, as part of a local development process, or as part of an automated testing process.
There are several linters in Python, including:
- Pylint—Python’s de facto linter, which emphasizes bad code practices, some of which can lead to vulnerabilities. However, it does not provide extensive security recommendations.
- Bandit—you can use this tool to discover common security issues in Python code. Bandit processes each file, creates an AST node, and runs the appropriate plugin to test it.
- Python IDEs like PyCharm and Wingware have these tools and others will be built in, as well as plugins for text editors that can provide security guidance.
Related content: Read our guide to SAST
4. Use Dynamic Application Security Testing
Dynamic Analysis Security Testing Tool, or DAST Testing, is an application security solution that helps web developers discover specific vulnerabilities while running in staging or production environments.
DAST testing can find a wide range of vulnerabilities, including I/O validation issues that can make applications vulnerable to SQL injection attacks. The major benefit of DAST testing is that it can validate that a vulnerability is really exploitable—meaning that attackers can actually perform a successful SQL injection attack. DAST testing can also help identify misconfigurations and errors that could lead to a SQL injection attack.
DAST Testing for Python Applications with Bright Security
Bright Security helps automate the detection and remediation of many vulnerabilities including SQLi, early in the development process, across web applications and APIs.
By shifting DAST scans left, and integrating them into the SDLC, developers and application security professionals can detect vulnerabilities early, and remediate them before they appear in production. Bright Security completes scans in minutes and achieves zero false positives, by automatically validating every vulnerability. This allows developers to adopt the solution and use it throughout the development lifecycle.
Scan any web app, or REST, SOAP and GraphQL APIs to prevent SQL injection vulnerabilities – try Bright Security free.