Cleaning Up Python Security: Hands-on Bandit and Safety in DevSecOps

Python tutorial - IT technology blog
Python tutorial - IT technology blog

Don’t Let the “If It Works, It’s Fine” Mindset Deceive You

When I first started dabbling in web development, I used to think that as long as the code produced the right results, I was done. Solid logic, a smooth UI, green test cases – that was enough to deploy with confidence. But reality is different. A single eval() function used carelessly for string processing, or an “ancient” library forgotten without updates, is enough for a hacker to turn your server into their own playground.

My project grew from an initial 200 lines to over 2,000 lines in just a few months. At this stage, manually scrutinizing every line of code for security flaws is impossible. That’s why you need to integrate DevSecOps thinking into your workflow from the start. Instead of scrambling to patch things after being attacked, scanning for vulnerabilities automatically while coding will help you sleep much better.

Why Is Python Source Code an Easy Target?

Python is famous for being easy to learn, but this very flexibility often makes developers complacent. The problems usually lie in these three blind spots:

  • Logic Black Holes: Carelessly hardcoding API keys, using exec() for flexible script execution, or calling system commands via subprocess without filtering input.
  • Third-party Libraries (Dependencies): We often pip install anything we find online. Are you sure that library doesn’t contain a CVE vulnerability that’s been public for ages?
  • Configuration Mistakes: Forgetting to turn off DEBUG = True in production is a classic error, exposing all environment information when an error occurs.

Bandit: A “Microscope” for Source Code Flaws

Bandit is a static analysis tool (SAST) specifically designed for Python. It doesn’t execute code but scans source files to find unsafe programming patterns based on a predefined set of rules.

Quick Installation

pip install bandit

Applying it in Practice

Suppose your app.py file contains a few “time bombs” like this:

import subprocess
import yaml

# Injection Risk: Hackers can insert additional commands like '&& rm -rf /'
def run_ping(ip):
    subprocess.run(f"ping -c 4 {ip}", shell=True)

# Using load() instead of safe_load() is vulnerable to remote code execution
def load_config(data):
    return yaml.load(data)

# Never leave passwords here!
DB_PASSWORD = "admin123"

When running the command bandit -r app.py, the tool will immediately flag these issues. Bandit categorizes errors by severity: Low, Medium, and High. For example, it will specifically point out that using shell=True is extremely dangerous. A hacker only needs to enter 127.0.0.1; cat /etc/passwd into the input field to steal your system data.

Safety: A Shield Against Malicious Libraries

While Bandit examines the code you write, Safety checks what you “borrow” from the community. Most Python projects today rely on dozens of libraries from PyPI. Safety cross-references your requirements.txt file with a security vulnerability database (like PyUp.io) to provide early warnings.

Installation

pip install safety

Quick Dependency Scan

You only need to run a single command:

safety check -r requirements.txt

The results will be very specific. For instance, if you’re using Django==2.2.1, Safety will immediately report that this version is affected by CVE-2019-14234 and require you to upgrade to 2.2.2 or higher for safety. This is a vital step before packaging your application into Docker or pushing it to a server.

Integrating Security into the Automation Pipeline

Don’t wait until you’re free to run security scans. Integrate them directly into Git Hooks or your CI/CD pipeline. Every time you commit, the system will automatically scan. If a “High” severity error is detected, the commit will be blocked until you fix it.

A practical workflow I often use through a simple script file:

# run_security_scan.sh
echo "--- Scanning for source code flaws (Bandit) ---"
bandit -r . -x ./venv

echo "\n--- Checking libraries (Safety) ---"
safety check

Nipping Vulnerabilities in the Bud

Tools are just assistants; your security mindset is what matters most. After several times of being “called out” by Bandit, I’ve drawn three non-negotiable principles:

  1. Always distrust all input: Treat every piece of user-submitted data as malicious. Use whitelists for filtering instead of trying to block individual characters.
  2. Choose secure defaults: Prioritize yaml.safe_load() over yaml.load(). When using subprocess, pass parameters as a list instead of using shell=True.
  3. Isolate Secrets: Never commit passwords to GitHub. Use a .env file combined with python-dotenv to manage environment variables.

Conclusion

Security is not the exclusive privilege of security experts. With Bandit and Safety, anyone can build a basic yet incredibly solid defense. Try scanning your project today. You might just discover some “silly” vulnerabilities that have been lurking for a long time. Good luck building systems that are both fast and impenetrable.

Share: