Writing Secure Python Scripts for IT Tasks
Sponsor message — This article is made possible by Dargslan.com, a publisher of practical, no-fluff IT & developer workbooks.
Why Dargslan.com?
If you prefer doing over endless theory, Dargslan’s titles are built for you. Every workbook focuses on skills you can apply the same day—server hardening, Linux one-liners, PowerShell for admins, Python automation, cloud basics, and more.
Writing Secure Python Scripts for IT Tasks
In today's interconnected digital landscape, IT professionals face an unprecedented challenge: balancing automation efficiency with robust security measures. Every script executed in production environments becomes a potential entry point for attackers, making security not just a best practice but an absolute necessity. The consequences of insecure automation range from data breaches and compliance violations to complete system compromises that can cripple entire organizations.
Secure scripting represents the practice of writing code that protects sensitive information, validates inputs, manages credentials safely, and operates with appropriate permissions while accomplishing its intended automation tasks. This discipline combines traditional security principles with modern development practices, creating a framework where functionality and protection work in harmony. Throughout this exploration, we'll examine multiple perspectives—from defensive coding techniques to credential management, from input validation to logging practices—that collectively form a comprehensive security approach.
By engaging with this material, you'll gain practical knowledge about implementing security controls directly into your Python automation workflows. You'll discover concrete techniques for protecting credentials, validating user inputs, managing file permissions, implementing proper error handling, and establishing audit trails. These aren't theoretical concepts but actionable strategies that you can implement immediately in your IT environment, transforming your scripts from functional tools into hardened, production-ready solutions.
Understanding the Security Landscape for Python Automation
Python has become the lingua franca of IT automation, powering everything from simple file operations to complex infrastructure orchestration. This widespread adoption brings tremendous benefits but also creates significant security implications. When scripts interact with databases, APIs, cloud services, and system resources, each interaction represents a trust boundary that must be carefully managed.
The security challenges in Python scripting differ fundamentally from traditional application development. Scripts often run with elevated privileges, access sensitive credentials, and execute in environments where traditional security controls may be limited. Unlike web applications with multiple layers of defense, a single compromised script can provide direct access to critical systems. Understanding these unique characteristics helps frame the security measures we must implement.
"The most dangerous scripts are those that work perfectly in production for months before someone discovers they've been logging passwords in plaintext or running with unnecessary root privileges."
Common Vulnerability Patterns in IT Scripts
Several vulnerability patterns appear repeatedly in IT automation scripts. Command injection occurs when scripts construct system commands using unsanitized input, allowing attackers to execute arbitrary commands. Hardcoded credentials embedded directly in script files create permanent security weaknesses that persist across version control systems. Insufficient input validation allows malicious data to flow through scripts unchecked, potentially corrupting systems or data.
Path traversal vulnerabilities emerge when scripts handle file operations without properly validating paths, enabling access to files outside intended directories. Insecure deserialization can execute malicious code when scripts process untrusted serialized objects. Each of these patterns represents a specific failure mode that secure coding practices must address systematically.
| Vulnerability Type | Risk Level | Common Occurrence | Primary Impact |
|---|---|---|---|
| Command Injection | Critical | System administration scripts | Arbitrary code execution |
| Hardcoded Credentials | High | Database connection scripts | Unauthorized access |
| Path Traversal | High | File management utilities | Unauthorized file access |
| Insecure Deserialization | Critical | Data processing scripts | Remote code execution |
| Insufficient Logging | Medium | All script types | Undetected breaches |
| Excessive Privileges | High | System maintenance scripts | Privilege escalation |
Credential Management and Secret Handling
Proper credential management stands as the cornerstone of secure scripting. Passwords, API keys, tokens, and certificates must never appear in script files, configuration files committed to version control, or log outputs. The challenge lies in making credentials accessible to scripts while keeping them protected from unauthorized access.
Environment Variables and Configuration Management
Environment variables provide a basic but effective mechanism for separating credentials from code. Scripts retrieve sensitive values at runtime from the environment rather than having them embedded in source files. This approach prevents credentials from appearing in version control and allows different values for development, staging, and production environments.
import os
import sys
def get_database_credentials():
"""Retrieve database credentials from environment variables."""
db_host = os.getenv('DB_HOST')
db_user = os.getenv('DB_USER')
db_password = os.getenv('DB_PASSWORD')
if not all([db_host, db_user, db_password]):
sys.stderr.write("Error: Required database credentials not found in environment\n")
sys.exit(1)
return {
'host': db_host,
'user': db_user,
'password': db_password
}
For more sophisticated requirements, dedicated secret management solutions like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault provide centralized credential storage with access controls, audit logging, and automatic rotation capabilities. These systems integrate with Python through official libraries, enabling scripts to request credentials dynamically with proper authentication.
Implementing Secret Management Services
Modern secret management services offer significant advantages over simple environment variables. They provide encryption at rest and in transit, detailed access logs, fine-grained permission controls, and automated credential rotation. Integration requires initial setup but delivers enterprise-grade security for production environments.
import boto3
from botocore.exceptions import ClientError
def get_secret_from_aws(secret_name, region_name='us-east-1'):
"""Retrieve secret from AWS Secrets Manager."""
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
try:
response = client.get_secret_value(SecretId=secret_name)
return response['SecretString']
except ClientError as e:
if e.response['Error']['Code'] == 'ResourceNotFoundException':
raise ValueError(f"Secret {secret_name} not found")
elif e.response['Error']['Code'] == 'InvalidRequestException':
raise ValueError(f"Invalid request for secret {secret_name}")
elif e.response['Error']['Code'] == 'InvalidParameterException':
raise ValueError(f"Invalid parameter for secret {secret_name}")
else:
raise
"Every hardcoded password represents a future incident waiting to happen. The question isn't if it will be exploited, but when and by whom."
Input Validation and Sanitization
Scripts that accept external input—whether from command-line arguments, configuration files, API responses, or user prompts—must treat all input as potentially malicious until proven otherwise. Input validation verifies that data conforms to expected formats, types, and ranges before processing. This defensive approach prevents injection attacks, data corruption, and unexpected behavior.
Validating Command-Line Arguments
Command-line arguments represent a primary input vector for IT scripts. Using the argparse module with type checking and validation provides structured input handling. Custom validation functions can enforce business rules and security constraints beyond basic type checking.
import argparse
import re
import sys
def validate_hostname(hostname):
"""Validate hostname format."""
pattern = r'^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$'
if not re.match(pattern, hostname):
raise argparse.ArgumentTypeError(f"Invalid hostname format: {hostname}")
return hostname
def validate_port(port):
"""Validate port number."""
try:
port_num = int(port)
if not 1 <= port_num <= 65535:
raise ValueError
return port_num
except ValueError:
raise argparse.ArgumentTypeError(f"Port must be between 1 and 65535: {port}")
def setup_argument_parser():
"""Configure argument parser with validation."""
parser = argparse.ArgumentParser(description='Secure server configuration script')
parser.add_argument(
'--hostname',
type=validate_hostname,
required=True,
help='Target hostname'
)
parser.add_argument(
'--port',
type=validate_port,
default=22,
help='Target port (1-65535)'
)
return parser
Preventing Command Injection
Command injection vulnerabilities arise when scripts construct shell commands by concatenating strings that include user input. Attackers can inject additional commands through metacharacters like semicolons, pipes, and backticks. The solution involves avoiding shell invocation entirely or using parameterized interfaces that separate commands from data.
import subprocess
import shlex
def unsafe_command_execution(filename):
"""UNSAFE: Vulnerable to command injection."""
command = f"cat {filename}"
subprocess.run(command, shell=True) # DANGEROUS
def safe_command_execution(filename):
"""SAFE: Protected against command injection."""
# Validate filename first
if not is_valid_filename(filename):
raise ValueError("Invalid filename")
# Use list format without shell=True
subprocess.run(['cat', filename], check=True)
def is_valid_filename(filename):
"""Validate filename contains no dangerous characters."""
dangerous_chars = [';', '&', '|', '`', '$', '(', ')', '<', '>', '\n']
return not any(char in filename for char in dangerous_chars)
"Input validation isn't about distrusting users—it's about protecting systems from the inevitable mistakes and malicious actors that will eventually interact with your scripts."
File System Security and Permission Management
Scripts that create, modify, or delete files must implement appropriate permission controls to prevent unauthorized access. File permissions, ownership settings, and directory structures form critical security boundaries that scripts must respect and enforce.
Setting Secure File Permissions
When creating files containing sensitive information, scripts should set restrictive permissions immediately upon creation. The default umask may allow overly permissive access. Explicitly setting permissions ensures consistent security regardless of environment configuration.
import os
import stat
import tempfile
def create_secure_config_file(config_data, filepath):
"""Create configuration file with secure permissions."""
# Create file with restrictive permissions (0o600 = owner read/write only)
fd = os.open(
filepath,
os.O_WRONLY | os.O_CREAT | os.O_EXCL,
mode=0o600
)
try:
with os.fdopen(fd, 'w') as f:
f.write(config_data)
except Exception:
# Clean up on failure
os.unlink(filepath)
raise
# Verify permissions were set correctly
file_stat = os.stat(filepath)
if file_stat.st_mode & 0o777 != 0o600:
os.unlink(filepath)
raise RuntimeError("Failed to set secure file permissions")
def create_secure_temp_file():
"""Create temporary file with secure settings."""
fd, path = tempfile.mkstemp(
prefix='secure_',
suffix='.tmp',
dir='/var/secure_temp'
)
# Set restrictive permissions
os.chmod(path, 0o600)
return fd, path
Path Traversal Prevention
Path traversal attacks exploit insufficient validation of file paths, allowing access to files outside intended directories. Scripts must validate and sanitize all path inputs, resolve symbolic links, and verify final paths remain within authorized boundaries.
import os
from pathlib import Path
def safe_path_join(base_directory, user_provided_path):
"""Safely join paths preventing traversal attacks."""
# Convert to absolute paths
base = Path(base_directory).resolve()
# Join and resolve the full path
full_path = (base / user_provided_path).resolve()
# Verify the resolved path is within base directory
try:
full_path.relative_to(base)
except ValueError:
raise ValueError(f"Path traversal attempt detected: {user_provided_path}")
return full_path
def read_file_safely(base_dir, filename):
"""Read file with path traversal protection."""
try:
safe_path = safe_path_join(base_dir, filename)
# Additional check: ensure it's a file, not a directory
if not safe_path.is_file():
raise ValueError(f"Not a valid file: {filename}")
with open(safe_path, 'r') as f:
return f.read()
except (ValueError, OSError) as e:
raise RuntimeError(f"Failed to read file safely: {e}")
Error Handling and Logging Security
Proper error handling prevents information leakage while maintaining operational visibility. Scripts must log sufficient information for troubleshooting and security monitoring without exposing sensitive data. This balance requires careful consideration of what information appears in error messages and log files.
Implementing Secure Logging Practices
Logging frameworks should filter sensitive information automatically while capturing security-relevant events. Structured logging with appropriate severity levels enables effective monitoring without creating security vulnerabilities through excessive disclosure.
import logging
import re
from logging.handlers import RotatingFileHandler
class SensitiveDataFilter(logging.Filter):
"""Filter sensitive information from log messages."""
def __init__(self):
super().__init__()
# Patterns for sensitive data
self.patterns = [
(re.compile(r'password["\']?\s*[:=]\s*["\']?([^"\'\s]+)', re.IGNORECASE), 'password=***'),
(re.compile(r'token["\']?\s*[:=]\s*["\']?([^"\'\s]+)', re.IGNORECASE), 'token=***'),
(re.compile(r'api[_-]?key["\']?\s*[:=]\s*["\']?([^"\'\s]+)', re.IGNORECASE), 'api_key=***'),
(re.compile(r'\b\d{16}\b'), '****-****-****-****'), # Credit card numbers
]
def filter(self, record):
"""Remove sensitive data from log record."""
message = record.getMessage()
for pattern, replacement in self.patterns:
message = pattern.sub(replacement, message)
record.msg = message
record.args = ()
return True
def setup_secure_logging(log_file='/var/log/secure_script.log'):
"""Configure secure logging with sensitive data filtering."""
logger = logging.getLogger('secure_script')
logger.setLevel(logging.INFO)
# Rotating file handler with size limits
handler = RotatingFileHandler(
log_file,
maxBytes=10485760, # 10MB
backupCount=5
)
# Set secure permissions on log file
os.chmod(log_file, 0o640)
# Add sensitive data filter
handler.addFilter(SensitiveDataFilter())
# Structured format
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(funcName)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
Security-Focused Exception Handling
Exception handling must distinguish between expected operational errors and security-relevant events. Generic error messages prevent information leakage to potential attackers while detailed logging captures context for security teams.
import logging
import sys
logger = logging.getLogger('secure_script')
def handle_database_operation():
"""Execute database operation with secure error handling."""
try:
# Database operation code
result = execute_query()
return result
except ConnectionError as e:
# Log detailed error internally
logger.error(f"Database connection failed: {e}", exc_info=True)
# Return generic message to user
raise RuntimeError("Database operation failed. Please contact support.")
except AuthenticationError as e:
# Security event - log with high priority
logger.critical(f"Authentication failure: {e}", exc_info=True)
# Don't reveal authentication details
raise RuntimeError("Authentication failed")
except Exception as e:
# Unexpected error - log everything
logger.exception(f"Unexpected error in database operation: {e}")
raise RuntimeError("An unexpected error occurred")
"Logs should tell you everything you need to know about what happened, except the specific details that would help an attacker exploit your system."
Secure Communication and API Interactions
Scripts frequently interact with external systems through APIs, requiring secure communication channels and proper authentication. Transport layer security, certificate validation, and authentication token management form essential components of secure API interactions.
Implementing HTTPS with Certificate Verification
All network communications should use encrypted channels with proper certificate verification. Disabling certificate verification—even temporarily for testing—creates severe security vulnerabilities that often persist into production.
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
def create_secure_session():
"""Create requests session with security best practices."""
session = requests.Session()
# Configure retry strategy
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
method_whitelist=["HEAD", "GET", "OPTIONS"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
# Enforce certificate verification
session.verify = True
# Set secure timeout
session.timeout = 30
return session
def make_secure_api_call(url, api_token):
"""Make API call with security controls."""
session = create_secure_session()
headers = {
'Authorization': f'Bearer {api_token}',
'User-Agent': 'SecureScript/1.0',
'Accept': 'application/json'
}
try:
response = session.get(
url,
headers=headers,
timeout=30,
verify=True # Explicitly enforce certificate verification
)
response.raise_for_status()
return response.json()
except requests.exceptions.SSLError as e:
logger.error(f"SSL certificate verification failed: {e}")
raise RuntimeError("Secure connection could not be established")
except requests.exceptions.Timeout:
logger.error(f"Request timeout for URL: {url}")
raise RuntimeError("Request timed out")
except requests.exceptions.RequestException as e:
logger.error(f"API request failed: {e}")
raise RuntimeError("API request failed")
Principle of Least Privilege
Scripts should operate with the minimum permissions necessary to accomplish their tasks. Running with excessive privileges—particularly root or administrator access—amplifies the impact of any security vulnerability. Implementing least privilege requires careful analysis of required permissions and appropriate privilege management.
Privilege Dropping and Separation
When scripts require elevated privileges for specific operations, they should drop privileges immediately after completing those operations. This time-limited elevation reduces the window of exposure.
import os
import pwd
import grp
def drop_privileges(user_name='scriptuser', group_name='scriptgroup'):
"""Drop root privileges to specified user and group."""
if os.getuid() != 0:
# Not running as root, nothing to drop
return
try:
# Get user and group information
user_info = pwd.getpwnam(user_name)
group_info = grp.getgrnam(group_name)
# Remove supplementary groups
os.setgroups([])
# Set GID and UID
os.setgid(group_info.gr_gid)
os.setuid(user_info.pw_uid)
# Verify privileges were dropped
if os.getuid() == 0 or os.getgid() == 0:
raise RuntimeError("Failed to drop privileges")
logger.info(f"Privileges dropped to {user_name}:{group_name}")
except KeyError as e:
raise RuntimeError(f"User or group not found: {e}")
except OSError as e:
raise RuntimeError(f"Failed to drop privileges: {e}")
def perform_privileged_operation():
"""Example of privilege management pattern."""
# Verify running as root
if os.getuid() != 0:
raise RuntimeError("This operation requires root privileges")
try:
# Perform operation requiring root
configure_system_service()
finally:
# Always drop privileges after privileged operation
drop_privileges()
"Every script running with root privileges is a potential system compromise waiting to happen. Question every elevated permission and implement alternatives wherever possible."
Dependency Management and Supply Chain Security
Third-party libraries and dependencies introduce code from external sources into your environment. Each dependency represents a trust decision and potential security risk. Managing dependencies securely requires verification, monitoring, and regular updates.
Dependency Verification and Pinning
Version pinning ensures consistent, reproducible builds while preventing unexpected updates that might introduce vulnerabilities or breaking changes. Requirements files should specify exact versions with cryptographic hashes for verification.
# requirements.txt with pinned versions and hashes
requests==2.31.0 \
--hash=sha256:942c5a758f98d7479f9cc49e422a8b8e4e3e4f1a5a6f5e7f5e7f5e7f5e7f5e7f
cryptography==41.0.7 \
--hash=sha256:8b5c3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
pyyaml==6.0.1 \
--hash=sha256:7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b
Vulnerability Scanning and Monitoring
Regular dependency scanning identifies known vulnerabilities in third-party libraries. Tools like pip-audit, Safety, or Snyk check dependencies against vulnerability databases and provide remediation guidance.
#!/usr/bin/env python3
"""Script to check dependencies for known vulnerabilities."""
import subprocess
import sys
import json
def check_dependencies():
"""Run security audit on installed packages."""
try:
# Run pip-audit
result = subprocess.run(
['pip-audit', '--format', 'json'],
capture_output=True,
text=True,
check=False
)
if result.returncode != 0:
vulnerabilities = json.loads(result.stdout)
print("⚠️ Security vulnerabilities found in dependencies:")
for vuln in vulnerabilities.get('vulnerabilities', []):
print(f"\n📦 Package: {vuln['package']}")
print(f" Version: {vuln['version']}")
print(f" Vulnerability: {vuln['id']}")
print(f" Fixed in: {vuln.get('fixed_version', 'No fix available')}")
return False
else:
print("✅ No known vulnerabilities found in dependencies")
return True
except FileNotFoundError:
print("❌ pip-audit not installed. Install with: pip install pip-audit")
return False
except json.JSONDecodeError:
print("❌ Failed to parse vulnerability report")
return False
if __name__ == '__main__':
if not check_dependencies():
sys.exit(1)
| Security Control | Implementation Method | Priority | Verification Method |
|---|---|---|---|
| Credential Protection | Environment variables or secret management service | Critical | Code review for hardcoded secrets |
| Input Validation | Type checking and format validation | Critical | Fuzzing and boundary testing |
| Command Injection Prevention | Parameterized commands without shell | Critical | Static analysis and penetration testing |
| File Permission Management | Explicit permission setting (0o600/0o640) | High | File system audit |
| Secure Logging | Filtered logging with sensitive data removal | High | Log review and testing |
| HTTPS Certificate Verification | Enforce verify=True in requests | Critical | Network traffic analysis |
| Least Privilege | Privilege dropping and role separation | High | Process monitoring |
| Dependency Security | Version pinning and vulnerability scanning | Medium | Automated scanning in CI/CD |
Code Review and Security Testing
Security measures implemented in code require validation through systematic review and testing. Combining automated tools with manual review provides comprehensive coverage that catches both common vulnerabilities and subtle logic flaws.
Static Analysis and Linting
Static analysis tools examine code without executing it, identifying potential security issues, code quality problems, and adherence to best practices. Tools like Bandit, Pylint, and mypy catch different categories of issues and should be integrated into development workflows.
#!/usr/bin/env python3
"""Security-focused code analysis script."""
import subprocess
import sys
def run_security_checks():
"""Execute multiple security analysis tools."""
checks = [
{
'name': 'Bandit Security Scanner',
'command': ['bandit', '-r', '.', '-f', 'json'],
'severity_threshold': 'MEDIUM'
},
{
'name': 'Pylint Security Checks',
'command': ['pylint', '--disable=all', '--enable=security', '.'],
'severity_threshold': None
}
]
all_passed = True
for check in checks:
print(f"\n🔍 Running {check['name']}...")
try:
result = subprocess.run(
check['command'],
capture_output=True,
text=True,
check=False
)
if result.returncode != 0:
print(f"❌ {check['name']} found issues:")
print(result.stdout)
all_passed = False
else:
print(f"✅ {check['name']} passed")
except FileNotFoundError:
print(f"⚠️ {check['name']} not installed")
all_passed = False
return all_passed
if __name__ == '__main__':
if not run_security_checks():
sys.exit(1)
Penetration Testing for Scripts
Testing scripts with malicious inputs validates security controls under attack conditions. This includes fuzzing inputs, attempting injection attacks, testing authentication bypasses, and verifying error handling under adversarial conditions.
"Automated security tools catch the obvious vulnerabilities. Manual review and testing catch the subtle logic flaws that automated tools miss. Both are essential."
Deployment and Operational Security
Security doesn't end when code is written—deployment practices and operational procedures significantly impact overall security posture. Scripts must be deployed with appropriate controls, monitored during execution, and maintained throughout their lifecycle.
Secure Deployment Practices
Deployment processes should include integrity verification, secure transfer mechanisms, and appropriate access controls. Scripts deployed to production systems require the same rigor as any production application.
- ✅ Code signing verifies script integrity and authenticity
- ✅ Encrypted transfer protects scripts during deployment
- ✅ Version control integration maintains audit trail
- ✅ Automated deployment pipelines reduce human error
- ✅ Rollback capabilities enable rapid response to issues
Runtime Monitoring and Alerting
Production scripts require monitoring to detect security incidents, performance issues, and operational failures. Structured logging combined with centralized log management enables effective monitoring and incident response.
import logging
import time
from functools import wraps
def monitor_execution(func):
"""Decorator to monitor script execution with security context."""
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
logger = logging.getLogger(func.__name__)
logger.info(f"Script execution started", extra={
'function': func.__name__,
'user': os.getenv('USER'),
'hostname': os.uname().nodename
})
try:
result = func(*args, **kwargs)
execution_time = time.time() - start_time
logger.info(f"Script execution completed successfully", extra={
'function': func.__name__,
'execution_time': execution_time
})
return result
except Exception as e:
execution_time = time.time() - start_time
logger.error(f"Script execution failed", extra={
'function': func.__name__,
'execution_time': execution_time,
'error_type': type(e).__name__
}, exc_info=True)
raise
return wrapper
@monitor_execution
def main():
"""Main script function with monitoring."""
# Script logic here
pass
Compliance and Regulatory Considerations
Many organizations operate under regulatory frameworks that impose specific security requirements on automation scripts. Understanding applicable regulations and implementing required controls ensures compliance while enhancing overall security.
Common Regulatory Requirements
Frameworks like PCI DSS, HIPAA, GDPR, and SOC 2 include provisions affecting script security. These typically mandate encryption of sensitive data, access controls, audit logging, and regular security assessments.
- 🔒 Data encryption for sensitive information at rest and in transit
- 🔒 Access logging with tamper-evident audit trails
- 🔒 Authentication mechanisms for script execution
- 🔒 Data retention policies for logs and outputs
- 🔒 Regular security assessments and vulnerability management
Security Documentation and Knowledge Transfer
Comprehensive documentation ensures security controls remain effective as teams change and systems evolve. Documentation should cover security decisions, threat models, required configurations, and operational procedures.
Essential Security Documentation
Security documentation serves multiple audiences and purposes. Developers need technical implementation details, operators require configuration guidance, and auditors need compliance evidence. Structured documentation addresses all these needs systematically.
"""
Script: secure_backup.py
Purpose: Automated secure backup with encryption
SECURITY CONSIDERATIONS:
- Credentials: Retrieved from AWS Secrets Manager
- Encryption: AES-256 encryption for backup files
- Access Control: Requires 'backup_operator' role
- Logging: All operations logged to centralized SIEM
- Network: Communicates only with approved backup storage
THREAT MODEL:
- Threat: Unauthorized access to backup files
Mitigation: Encryption at rest, strict file permissions
- Threat: Credential exposure
Mitigation: Secret management service, no hardcoded credentials
- Threat: Data exfiltration during transfer
Mitigation: TLS encryption for all network transfers
COMPLIANCE REQUIREMENTS:
- PCI DSS 3.4: Encryption of cardholder data
- SOC 2: Audit logging of all backup operations
DEPENDENCIES:
- boto3==1.28.0 (AWS SDK)
- cryptography==41.0.7 (Encryption library)
All dependencies verified for known vulnerabilities
CONFIGURATION:
Environment Variables Required:
- AWS_REGION: AWS region for Secrets Manager
- BACKUP_BUCKET: S3 bucket for backup storage
- ENCRYPTION_KEY_ID: KMS key ID for encryption
File Permissions:
- Script: 0o750 (owner: backup_operator)
- Log files: 0o640 (owner: backup_operator)
- Backup files: 0o600 (owner: backup_operator)
TESTING:
- Unit tests: tests/test_secure_backup.py
- Security tests: tests/security/test_backup_security.py
- Penetration tests: Conducted quarterly
INCIDENT RESPONSE:
Contact: security@example.com
Escalation: Follow incident response playbook IRP-001
"""
How do I securely store API keys and passwords in Python scripts?
Never hardcode credentials in scripts. Use environment variables for simple cases or dedicated secret management services like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault for production environments. These services provide encryption, access controls, audit logging, and credential rotation capabilities that environment variables alone cannot offer.
What's the most important security control to implement first?
Credential protection should be your first priority. Hardcoded credentials create immediate, exploitable vulnerabilities that persist in version control history. Once credentials are secured, focus on input validation to prevent injection attacks, then implement proper logging and monitoring to detect security incidents.
How can I prevent command injection vulnerabilities in system administration scripts?
Avoid using shell=True in subprocess calls. Instead, pass commands as lists where the command and arguments are separate elements. Validate and sanitize all inputs before using them in any system command. Use parameterized interfaces provided by libraries rather than constructing commands through string concatenation.
Should my scripts run with root privileges?
Only when absolutely necessary, and only for the minimum duration required. If root access is needed for specific operations, drop privileges immediately afterward. Consider alternative approaches like sudo for specific commands, capability-based permissions, or running scripts as dedicated service accounts with only required permissions.
How do I know if my Python dependencies have security vulnerabilities?
Use automated scanning tools like pip-audit, Safety, or Snyk integrated into your development workflow and CI/CD pipeline. Pin dependency versions with cryptographic hashes in requirements files. Subscribe to security advisories for your critical dependencies and establish a process for promptly applying security updates.
What logging information is safe to include without creating security risks?
Log events, timestamps, user identifiers, operation types, and outcomes. Never log passwords, API keys, tokens, credit card numbers, or other sensitive data. Implement log filtering to automatically remove sensitive patterns. Use structured logging with appropriate severity levels to enable effective monitoring without exposing sensitive information.