An Auditor’s Checklist for Securing Python Backends on OVH
Environment Hardening: OVH Instance Configuration
Securing a Python backend on OVH begins with a hardened instance. This involves minimizing the attack surface by disabling unnecessary services, configuring a strict firewall, and ensuring all system packages are up-to-date. For OVH Public Cloud instances, this typically means starting with a clean OS image (e.g., Ubuntu LTS) and applying security best practices from the outset.
Firewall Rules with UFW
Uncomplicated Firewall (UFW) is a user-friendly frontend for `iptables`. We’ll configure it to allow only essential inbound traffic. SSH (port 22) for management, HTTP/HTTPS (ports 80/443) for web traffic, and potentially a specific port for application-level communication if not using a reverse proxy.
Initial UFW Setup
Start by enabling UFW and setting default policies to deny all incoming connections and allow all outgoing.
sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw enable
Allowing Essential Ports
Allow SSH, HTTP, and HTTPS. If your Python application listens on a specific port (e.g., 8000 for a development server or a custom API port), add that as well. For production, it’s highly recommended to use a reverse proxy like Nginx or HAProxy, which would then listen on 80/443 and forward traffic to your Python application.
sudo ufw allow ssh sudo ufw allow http sudo ufw allow https # Example: Allow custom application port if not using a reverse proxy # sudo ufw allow 8000/tcp
Verifying Firewall Status
Confirm that the rules are active and correctly applied.
sudo ufw status verbose
System Package Updates and Security Patches
Regularly updating system packages is critical to patch known vulnerabilities. Automate this process where possible, or establish a strict schedule for manual updates.
sudo apt update && sudo apt upgrade -y sudo apt autoremove -y sudo apt clean
Consider setting up unattended upgrades for critical security patches:
sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades
SSH Security Best Practices
Secure SSH access to prevent brute-force attacks and unauthorized logins. This involves disabling root login, using key-based authentication, and changing the default port (though this is often debated, as it can lead to obscurity rather than true security if not managed carefully).
Disable Root SSH Login
Edit the SSH daemon configuration file.
# /etc/ssh/sshd_config PermitRootLogin no
Enforce Key-Based Authentication
Ensure that password authentication is disabled and only public key authentication is allowed. This requires users to generate an SSH key pair and place their public key on the server.
# /etc/ssh/sshd_config PasswordAuthentication no PubkeyAuthentication yes
Restart SSH Service
Apply the changes by restarting the SSH service.
sudo systemctl restart sshd
Application Deployment and Runtime Security
The security of the Python application itself is paramount. This section covers secure deployment practices and runtime configurations.
Virtual Environments and Dependency Management
Always use virtual environments (like `venv` or `conda`) to isolate project dependencies. This prevents conflicts and ensures that only explicitly installed packages are available. Regularly audit and update dependencies to patch known vulnerabilities.
Creating and Activating a Virtual Environment
# Install venv if not present sudo apt install python3-venv # Create a virtual environment python3 -m venv /opt/my_python_app/venv # Activate the environment source /opt/my_python_app/venv/bin/activate # Install dependencies from requirements.txt pip install -r requirements.txt # Deactivate when done deactivate
Dependency Auditing with `pip-audit`
`pip-audit` is a powerful tool for checking installed Python packages against known vulnerabilities. Integrate this into your CI/CD pipeline and perform regular manual checks.
# Install pip-audit pip install pip-audit # Audit current environment pip-audit
Running Python Applications Securely
Avoid running Python applications directly with `python app.py`. Use a production-grade WSGI/ASGI server and run the application under a non-privileged user. For web applications, a reverse proxy is essential.
Using Gunicorn with a Non-Privileged User
Gunicorn is a popular WSGI HTTP Server for Python. Create a dedicated user for your application.
# Create a system user for the application sudo useradd -r -s /bin/false myappuser # Set ownership for application directory sudo chown -R myappuser:myappuser /opt/my_python_app
Configure Gunicorn to run as this user. A systemd service file is the standard way to manage this on modern Linux systems.
# /etc/systemd/system/my_python_app.service [Unit] Description=Gunicorn instance to serve my_python_app After=network.target [Service] User=myappuser Group=myappuser WorkingDirectory=/opt/my_python_app ExecStart=/opt/my_python_app/venv/bin/gunicorn --workers 3 --bind unix:/opt/my_python_app/my_python_app.sock my_python_app.wsgi:application [Install] [Install] WantedBy=multi-user.target
Start and enable the service:
sudo systemctl start my_python_app sudo systemctl enable my_python_app sudo systemctl status my_python_app
Reverse Proxy with Nginx
Nginx acts as a reverse proxy, handling SSL termination, load balancing, and serving static files efficiently. It forwards dynamic requests to your Gunicorn application via a Unix socket or TCP port.
Nginx Configuration for Gunicorn
# /etc/nginx/sites-available/my_python_app
server {
listen 80;
server_name your_domain.com www.your_domain.com;
location /static/ {
alias /opt/my_python_app/static/;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:/opt/my_python_app/my_python_app.sock;
}
}
Enable the site and test Nginx configuration:
sudo ln -s /etc/nginx/sites-available/my_python_app /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl restart nginx
SSL/TLS Configuration with Let’s Encrypt
Secure your application with HTTPS. Certbot with Let’s Encrypt provides free SSL certificates and automates renewal.
# Install Certbot and Nginx plugin sudo apt install certbot python3-certbot-nginx # Obtain and install certificate sudo certbot --nginx -d your_domain.com -d www.your_domain.com # Verify auto-renewal sudo certbot renew --dry-run
Application-Level Security Considerations
Beyond infrastructure and deployment, the Python code itself must be secured against common web vulnerabilities.
Input Validation and Sanitization
All user-supplied input (from forms, URL parameters, API requests) must be rigorously validated and sanitized to prevent injection attacks (SQL injection, XSS, command injection).
SQL Injection Prevention
Use parameterized queries or ORMs (like SQLAlchemy or Django’s ORM) that handle this automatically. Never construct SQL queries by concatenating strings.
# Using SQLAlchemy (example)
from sqlalchemy import text
# Vulnerable: String formatting
# query = text(f"SELECT * FROM users WHERE username = '{user_input}'")
# Secure: Parameterized query
query = text("SELECT * FROM users WHERE username = :username")
result = session.execute(query, {"username": user_input})
Cross-Site Scripting (XSS) Prevention
When rendering user-provided content in HTML, always escape it. Most web frameworks provide templating engines that do this by default (e.g., Jinja2, Django templates). Ensure autoescaping is enabled.
# Using Jinja2 (example)
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('my_template.html')
# User-provided content
user_comment = "<script>alert('XSS')</script>"
# Rendered HTML will automatically escape user_comment
rendered_html = template.render(comment=user_comment)
# rendered_html will contain: <p>Your comment: <script>alert('XSS')</script></p>
Authentication and Authorization
Implement strong authentication mechanisms. Use secure password hashing (e.g., bcrypt, Argon2) and enforce secure password policies. Authorization checks must be performed on every request to ensure users only access resources they are permitted to.
Secure Session Management
Use secure, HTTP-only, and same-site cookies for session management. Regenerate session IDs upon login and logout to prevent session fixation. Store sensitive session data server-side.
Logging and Monitoring
Comprehensive logging and proactive monitoring are essential for detecting and responding to security incidents.
Application Logging
Configure your Python application to log security-relevant events: authentication attempts (success/failure), authorization failures, critical errors, and sensitive data access. Use the standard `logging` module and consider structured logging (e.g., JSON format) for easier parsing by log aggregation tools.
import logging
import json
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def log_security_event(event_type, user_id, details=None):
log_entry = {
"timestamp": datetime.datetime.utcnow().isoformat() + "Z",
"event_type": event_type,
"user_id": user_id,
"details": details or {}
}
logger.info(json.dumps(log_entry))
# Example usage
log_security_event("authentication_success", "user123", {"ip_address": "192.168.1.100"})
log_security_event("authorization_failure", "user456", {"resource": "/admin", "action": "GET"})
System and Nginx Logs
Ensure system logs (`/var/log/auth.log`, `/var/log/syslog`) and Nginx access/error logs (`/var/log/nginx/access.log`, `/var/log/nginx/error.log`) are collected and monitored. Tools like `fail2ban` can be configured to parse SSH and Nginx logs to automatically block malicious IPs.
# Install fail2ban sudo apt install fail2ban # Configure fail2ban for SSH and Nginx (example) # Create /etc/fail2ban/jail.local and add sections for sshd and nginx-http-auth # Example for sshd: # [sshd] # enabled = true # port = ssh # filter = sshd # logpath = /var/log/auth.log # maxretry = 3 # bantime = 1h # Example for Nginx (if using basic auth) # [nginx-http-auth] # enabled = true # port = http,https # filter = nginx-http-auth # logpath = /var/log/nginx/error.log # maxretry = 2 # bantime = 1h sudo systemctl restart fail2ban
Regular Audits and Penetration Testing
An auditor’s checklist is not a one-time task. Regular security audits, code reviews, and periodic penetration tests are crucial to identify and remediate vulnerabilities that may have been introduced or discovered over time.
Code Review Checklist
- Input validation and sanitization for all external data.
- Secure handling of secrets and credentials (no hardcoding).
- Proper error handling that doesn’t leak sensitive information.
- Authentication and authorization checks on all sensitive endpoints.
- Use of secure cryptographic practices.
- Dependency vulnerability checks.
- Rate limiting on sensitive operations (login, password reset).
Penetration Testing
Engage with security professionals to perform black-box or grey-box penetration tests against your deployed application and infrastructure. This provides an external perspective on your security posture.