Common Python Security Problems

Stan Georgian
ITNEXT
Published in
6 min readMay 25, 2020

--

Python is increasingly becoming one of the most popular programming languages among developers. The relatively low number of Python security issues and its user-friendliness give it an edge over other languages. For this reason, it powers some of the largest sites on the web, including YouTube, Dropbox, Reddit, Quora, Spotify, and Instagram.

According to a 2019 survey by StackOverflow, 41.7 percent of developers cited Python as their favourite development technology. The survey also highlighted that Python has risen in the ranks, surpassing C#, PHP, and now Java, in terms of developer preference.

However, as with any other technology, developers should focus on heightening security levels in their application. This post puts together some of the most common Python security concerns and guides developers on how to fix them.

Input Injection

Injection attacks are arguably the most common vulnerabilities in any development environment. In Python, these attacks can take different forms, including:

  • Module injection — It occurs when a malicious Python module or package file is imported into a directory.
  • SQL injection — It involves introducing malicious input that is executed in the context of SQL statements.
  • Command injection — It occurs when calling a process, subprocess, or os.system. Specially crafted variables can contain malicious values, which introduce vulnerabilities when called in local commands.

Here is a sample code snippet where a simple malicious module is imported and can lead to undesired consequences upon execution.

Raw

There are several ways to mitigate injection attacks. These include:

  • Sanitizing all input
  • Correct input escaping for the shlex module in shell
  • Maintaining secure access permissions on package files and directories in the search path. This ensures unprivileged users have no write access to the files.

Misusing Import Functionalities

Python is very flexible when it comes to imports. However, this flexibility comes at a cost in terms of security.

When using a relative import in Python, a malicious module found in the system path can be smuggled into your codebase. This is dangerous because the import statements can execute code in the malicious module, thereby creating a security hole.

If a simple statement like __import__(“os”).system(“uname -a”) contains such malicious values, then the supplied command can be used to compromise data or processes in an application.

To avoid abusing import functionalities, the input function should only be used when data passed to the script’s stdin is trusted. Otherwise, treat all data as raw, untrusted input.

The good thing is that Python 3 does not support implicit relative imports. Instead, it treats import functions as raw input, which completely fixes the vulnerability. For this reason, Python 2 users should consider upgrading to Python 3. However, this does not mean Python 3 users should be careless with their import statements.

Using Outdated Dependencies

A significant portion of developers introduce vulnerabilities into their Python applications by using unpatched dependencies. Outdated Python dependencies usually open up loopholes, most of which are fixed in subsequent releases. For this reason, it is vital to keep your dependencies updated. Otherwise, your code will remain insecure.

For instance, in this Python-based animation engine for explanatory math videos, there were a number of security vulnerabilities in the unpatched Pillow dependency, as shown:

The three Python vulnerabilities identified in the dependency were:

  • CVE-2019–19911 — DoS vulnerability associated with uncontrolled resource consumption. It affects all Pillow versions below 6.2.2.
  • CVE-2020–5313 — Buffer overflow in Pillow, leading to an out-of-bounds read. This vulnerability also affects all versions below 6.2.2.
  • CVE-2019–16865 — Denial of Service attack in Pillow when processing specially crafted image files.It affects all versions below 6.2.0.

The three vulnerabilities can be fixed by updating the pillow dependency to a patched version. In this case, any version above 6.2.2 will fix the three flaws.

The best way to keep your dependencies up to date is by using WhiteSource Renovate, a free dependency update tool that can found on the GitHub marketplace. When you integrate Renovate into a project or workflow, it scans all the repositories.

Here is a snapshot showing debugging level logs for this GitHub Python project.

After scanning your codebase, Renovate initiates a pull request for every outdated dependency, as shown below:

Once you merge the pull request invoked by Renovate, the vulnerable Pillow dependency is updated to version 7, thus making the application or repository secure.

Notice the green mark on the dependency alerts section; which security issues in the pillow dependency are now fixed.

Assert Statements

It is wrong to use assert statements to control your application logic or program execution. It can lead to retrieval of wrong results, introduce security risks, or even worse, program failure.

Consider the Python code snippet below.

Raw

When you run this Python program in optimized mode, the assertstatement is ignored. As such, any user, including those who are not members of the superuser group, can successfully get the list of clients.

This means whatever protection was wired into the code is removed, leaving the application vulnerable to attacks.

Instead of relying on the assert statement to guard code against illegal access, we can rewrite a more secure program as follows:

Raw

The assert mechanism should only be used for communication with other developers. For instance, when performing unit or integration tests.

Insecure Deserialization

There are a number of techniques for reading external files and loading their content into (de/serializing) Python objects. Pickle is one such powerful serialization technique that is inherently risky, especially when an attacker tampers with serialized data.

Data from external sources is never secure. As a rule of thumb, never unpickle or parse data from an untrusted source into Python objects. This is because an attacker can use a subprocess module to execute arbitrary commands during pickling.

Additionally, YAML files from user input can leave your application open to attacks. To avoid this, use PyYAML safe_loadfunction (yaml.safe_load) to handle YAML serialization.

Here is a simple custom code that can be used to find all unsafe yaml.load functions in your codebase.

Raw

Wrap Up

Software security should always be at the forefront of every Python development project. While this post is meant to increase awareness of security vulnerabilities, it is not a complete roundup of all security vulnerabilities in the Python ecosystem.

However, it covers the most prevalent issues that introduce security gaps in Python applications. So, follow the above practices to create a solid foundation for building secure Python applications.

--

--