HackBlue

Tools · Python and Bash for security automation · 01 / 20

Setting up Python for security scripting: virtual environments, pinned dependencies, and secrets

Create isolated environments, pin and hash dependencies, keep API keys out of code with environment variables, and structure a small script so it is easy to reuse.

9 min read · practice only with authorization

One environment per project

Installing packages globally mixes versions across projects and can break system tools. Create a virtual environment per project with python3 -m venv, or use uv, which creates environments and installs packages very quickly. Record dependencies in requirements.txt or pyproject.toml so the script runs the same way next month and on a colleague’s machine.

Snippet
python3 -m venv .venv && . .venv/bin/activate
pip install requests
pip freeze > requirements.txt

# hashes make pip refuse tampered or unexpected packages
pip install pip-tools
echo requests > requirements.in
pip-compile --generate-hashes requirements.in
pip install --require-hashes -r requirements.txt

Secrets

Never paste API keys into scripts. Read them from environment variables, a .env file that is listed in .gitignore, or a secret manager. Fail clearly if the variable is missing.

Snippet
import os, sys
api_key = os.environ.get('VT_API_KEY')
if not api_key:
    sys.exit('Set VT_API_KEY in the environment')

Lab: a reusable script skeleton

Create a project folder with a virtual environment, a requirements file, a .gitignore that excludes .venv and .env, and a script with argparse, logging, and a main function. Run it with --help to see the generated usage text. Use this skeleton for every script in this hub.

Snippet
#!/usr/bin/env python3
"""Short description of what the script does."""
import argparse, logging, sys

def parse_args():
    p = argparse.ArgumentParser(description=__doc__)
    p.add_argument('input', help='file to read')
    p.add_argument('-v', '--verbose', action='store_true')
    return p.parse_args()

def main():
    args = parse_args()
    logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO, format='%(levelname)s %(message)s')
    logging.info('reading %s', args.input)
    return 0

if __name__ == '__main__':
    sys.exit(main())

Dependency hygiene

Check package names carefully before installing, because typosquatted packages with similar names exist on PyPI. Run pip-audit periodically to find known vulnerabilities in your dependencies, and prefer well-maintained libraries with many users.

Snippet
pip install pip-audit && pip-audit -r requirements.txt

Diagram

Starting a script project

  1. Environment

    python3 -m venv .venv or uv venv.

  2. Dependencies

    Pin and hash in requirements.txt.

  3. Secrets

    Environment variables; .env in .gitignore.

  4. Skeleton

    argparse, logging, main(); pip-audit regularly.

Check yourself

1. Why use a virtual environment per project?

2. Where should an API key used by a script come from?

3. Which tool reports known vulnerabilities in installed Python dependencies?