Security & Compliance
Security best practices, secrets management, compliance frameworks, and infrastructure hardening
Security: Everyone's Responsibility
Security isn't just the security team's job, it's everyone's responsibility. One misconfigured S3 bucket, one leaked API key, one SQL injection vulnerability can compromise your entire system. Modern security is about defense in depth: layers of protection at the network, infrastructure, application, and data levels. This lesson covers security fundamentals, secrets management (never hardcode credentials!), authentication and authorization, infrastructure hardening, compliance frameworks (SOC 2, GDPR, HIPAA), and practical tools for securing your systems. Security is hard, but ignoring it is catastrophic.
Security Fundamentals, CIA Triad
Security starts with three core principles: Confidentiality, Integrity, and Availability.
Confidentiality
Goal: Keep data private
- Encryption (at rest, in transit)
- Access controls (who can see what)
- Authentication (who are you)
- Strong passwords, MFA
- Data classification
Integrity
Goal: Keep data accurate
- Checksums, hashing
- Digital signatures
- Audit logs (who changed what)
- Input validation
- Version control
Availability
Goal: Keep systems accessible
- Redundancy (no single point of failure)
- DDoS protection
- Rate limiting
- Backups and disaster recovery
- High availability architecture
Defense in Depth (Layered Security)
- Input validation, output encoding
- OWASP Top 10 protection
- Secure coding practices
- Application firewalls (WAF)
- Encryption at rest (AES-256)
- Encryption in transit (TLS 1.3)
- Data loss prevention (DLP)
- Secrets management (Vault, Secrets Manager)
- Multi-factor authentication (MFA)
- OAuth 2.0 / OpenID Connect
- Role-based access control (RBAC)
- Least privilege principle
- OS hardening (CIS benchmarks)
- Patch management
- Container security (scan images)
- Kubernetes security policies
- Firewalls, security groups
- Network segmentation (VPCs, subnets)
- DDoS protection (CloudFlare, AWS Shield)
- VPN, private networks
- IDS/IPS (intrusion detection/prevention)
- Web application firewall (WAF)
- Rate limiting, throttling
- API gateway security
- Data center access controls
- Hardware security modules (HSM)
- Physical device security
Secrets Management, Never Hardcode Credentials
Secrets (API keys, passwords, tokens) must NEVER be in code or config files. Use dedicated secret managers.
❌ BAD: Hardcoded Secrets
# config.py DATABASE_URL = "postgres://user:pass123@db:5432/mydb" API_KEY = "sk-proj-abc123xyz" AWS_SECRET = "wJalrXUtnFEMI/K7MDENG"
Problems:
• Committed to Git (leaked!)
• Hard to rotate
• Same secrets for all environments
• Visible to everyone with code access
✅ GOOD: Secret Manager
# config.py
import boto3
client = boto3.client('secretsmanager')
secret = client.get_secret_value(
SecretId='prod/db'
)Benefits:
• Never in code/version control
• Easy rotation
• Environment-specific
• Audit trail (who accessed when)
Secret Management Tools
| Tool | Best For | Features | Pricing |
|---|---|---|---|
| HashiCorp Vault | Multi-cloud, dynamic secrets | Dynamic secrets, encryption as service, PKI | Free (open-source), Enterprise $$$ |
| AWS Secrets Manager | AWS workloads | Auto rotation, RDS integration, versioning | $0.40/secret/month + API calls |
| AWS SSM Parameter Store | Simple AWS secrets | Free tier, KMS encryption, hierarchical | Free (standard), $0.05/param (advanced) |
| GCP Secret Manager | GCP workloads | Versioning, IAM integration, replication | $0.06/secret/month |
| Azure Key Vault | Azure workloads | Keys, secrets, certificates, HSM-backed | $0.03/10k operations |
| Doppler | Developer-friendly, SaaS | Multi-environment, integrations, GUI | Free tier, $36+/user/month |
AWS Secrets Manager (Python)
# Install
pip install boto3
# Retrieve secrets from AWS Secrets Manager
import boto3
import json
from botocore.exceptions import ClientError
def get_secret(secret_name, region_name="us-east-1"):
"""Retrieve secret from AWS Secrets Manager"""
# Create client
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
try:
get_secret_value_response = client.get_secret_value(
SecretId=secret_name
)
except ClientError as e:
# Handle errors
if e.response['Error']['Code'] == 'ResourceNotFoundException':
print(f"Secret {secret_name} not found")
elif e.response['Error']['Code'] == 'InvalidRequestException':
print(f"Invalid request for {secret_name}")
elif e.response['Error']['Code'] == 'InvalidParameterException':
print(f"Invalid parameter for {secret_name}")
raise e
else:
# Decrypt and return secret
if 'SecretString' in get_secret_value_response:
secret = get_secret_value_response['SecretString']
return json.loads(secret)
else:
# Binary secret
return get_secret_value_response['SecretBinary']
# Usage
db_credentials = get_secret("prod/database")
DATABASE_URL = f"postgresql://{db_credentials['username']}:{db_credentials['password']}@{db_credentials['host']}:5432/{db_credentials['dbname']}"
api_keys = get_secret("prod/api-keys")
STRIPE_SECRET_KEY = api_keys['stripe_secret']
SENDGRID_API_KEY = api_keys['sendgrid_api_key']HashiCorp Vault (Python)
# Install
pip install hvac
# Vault client in Python
import hvac
import os
# Initialize Vault client
client = hvac.Client(
url='https://vault.example.com:8200',
token=os.environ['VAULT_TOKEN'] # Never hardcode!
)
# Check if authenticated
if not client.is_authenticated():
raise Exception("Not authenticated to Vault")
# Read secret (KV v2)
secret = client.secrets.kv.v2.read_secret_version(
path='prod/database',
mount_point='secret'
)
db_credentials = secret['data']['data']
DATABASE_URL = f"postgresql://{db_credentials['username']}:{db_credentials['password']}@{db_credentials['host']}:5432/{db_credentials['dbname']}"
# Write secret
client.secrets.kv.v2.create_or_update_secret(
path='prod/api-keys',
secret=dict(
stripe_key='sk_live_...',
sendgrid_key='SG...'
),
mount_point='secret'
)
# Dynamic database credentials (Vault generates temporary creds)
db_creds = client.secrets.database.generate_credentials(
name='postgres-role'
)
# Credentials auto-expire after TTLEnvironment Variables (Simplest Approach)
# .env file (NEVER commit to Git!)
# .env DATABASE_URL=postgresql://user:pass@localhost:5432/mydb STRIPE_SECRET_KEY=sk_test_... SENDGRID_API_KEY=SG...
# Python: Load from environment
import os
from dotenv import load_dotenv
# Load .env file (development only!)
load_dotenv()
# Access secrets
DATABASE_URL = os.getenv('DATABASE_URL')
STRIPE_SECRET_KEY = os.getenv('STRIPE_SECRET_KEY')
if not DATABASE_URL:
raise ValueError("DATABASE_URL not set")
# In production: set env vars in deployment platform
# (Heroku, Railway, AWS ECS, Kubernetes Secrets, etc.)• NEVER commit secrets to Git (add .env to .gitignore)
• Use different secrets for dev/staging/prod
• Rotate secrets regularly (automated if possible)
• Use short-lived credentials when available
• Audit secret access (who accessed what, when)
• Encrypt secrets at rest (KMS, HSM)
• Principle of least privilege (only grant necessary access)
Authentication & Authorization
Authentication: Who are you? Authorization: What are you allowed to do?
Authentication Methods
| Method | How It Works | Use Case |
|---|---|---|
| Password + MFA | Password + one-time code (TOTP, SMS) | Traditional user login, admin access |
| OAuth 2.0 | Delegated authorization (Login with Google) | Third-party login, API access delegation |
| JWT (JSON Web Token) | Stateless tokens with signed claims | API authentication, microservices |
| API Keys | Long-lived secret tokens | Service-to-service, simple APIs |
| mTLS (Mutual TLS) | Client and server both present certificates | Service mesh, high-security environments |
| SSO (Single Sign-On) | One login for multiple apps (SAML, OIDC) | Enterprise, employee access |
JWT Authentication (Python)
# Install
pip install pyjwt flask
# JWT authentication in Flask
from flask import Flask, request, jsonify
import jwt
import datetime
from functools import wraps
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key' # Use env var in production!
def token_required(f):
"""Decorator to require valid JWT token"""
@wraps(f)
def decorated(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({'message': 'Token is missing'}), 401
try:
# Remove 'Bearer ' prefix
if token.startswith('Bearer '):
token = token[7:]
# Decode token
data = jwt.decode(
token,
app.config['SECRET_KEY'],
algorithms=["HS256"]
)
current_user = data['user_id']
except jwt.ExpiredSignatureError:
return jsonify({'message': 'Token has expired'}), 401
except jwt.InvalidTokenError:
return jsonify({'message': 'Invalid token'}), 401
return f(current_user, *args, **kwargs)
return decorated
@app.route('/login', methods=['POST'])
def login():
"""Login endpoint - returns JWT"""
auth = request.json
# Validate credentials (check database)
if auth.get('username') == 'admin' and auth.get('password') == 'password':
# Create token
token = jwt.encode({
'user_id': 123,
'username': 'admin',
'exp': datetime.datetime.now(timezone.utc) + datetime.timedelta(hours=24)
}, app.config['SECRET_KEY'], algorithm="HS256")
return jsonify({'token': token})
return jsonify({'message': 'Invalid credentials'}), 401
@app.route('/protected', methods=['GET'])
@token_required
def protected(current_user):
"""Protected endpoint - requires valid token"""
return jsonify({'message': f'Hello user {current_user}!'})
# Usage:
# 1. POST /login with username/password → get token
# 2. GET /protected with header: Authorization: Bearer <token>Role-Based Access Control (RBAC)
# RBAC in Python
from enum import Enum
from functools import wraps
from flask import jsonify
class Role(Enum):
ADMIN = "admin"
USER = "user"
VIEWER = "viewer"
class Permission(Enum):
READ = "read"
WRITE = "write"
DELETE = "delete"
ADMIN = "admin"
# Define role permissions
ROLE_PERMISSIONS = {
Role.ADMIN: [Permission.READ, Permission.WRITE, Permission.DELETE, Permission.ADMIN],
Role.USER: [Permission.READ, Permission.WRITE],
Role.VIEWER: [Permission.READ]
}
def require_permission(permission):
"""Decorator to check user has required permission"""
def decorator(f):
@wraps(f)
def decorated_function(current_user, *args, **kwargs):
# Get user role from database/token
user_role = get_user_role(current_user)
# Check permission
if permission not in ROLE_PERMISSIONS.get(user_role, []):
return jsonify({'message': 'Insufficient permissions'}), 403
return f(current_user, *args, **kwargs)
return decorated_function
return decorator
@app.route('/users/<user_id>', methods=['DELETE'])
@token_required
@require_permission(Permission.DELETE)
def delete_user(current_user, user_id):
"""Only admins can delete users"""
# Delete user logic
return jsonify({'message': f'User {user_id} deleted'})
@app.route('/reports', methods=['GET'])
@token_required
@require_permission(Permission.READ)
def view_reports(current_user):
"""All authenticated users can view reports"""
return jsonify({'reports': []})• Always use HTTPS (encrypt credentials in transit)
• Implement MFA for privileged accounts
• Use strong password policies (min length, complexity)
• Hash passwords with bcrypt/argon2 (NEVER store plaintext)
• Implement account lockout after failed attempts
• Use short-lived tokens (15 min - 1 hour)
• Rotate API keys regularly
• Log all authentication events
Application Security, OWASP Top 10
The OWASP Top 10 lists the most critical web application security risks. Protect against these.
| # | Vulnerability | Description | Prevention |
|---|---|---|---|
| 1 | Broken Access Control | Users can access unauthorized resources | Enforce access controls server-side, deny by default |
| 2 | Cryptographic Failures | Sensitive data exposed due to weak crypto | Encrypt data at rest/transit, use strong algorithms |
| 3 | Injection | SQL/NoSQL/OS command injection | Use parameterized queries, validate input |
| 4 | Insecure Design | Missing security controls in design | Threat modeling, secure design patterns |
| 5 | Security Misconfiguration | Default configs, unnecessary features enabled | Harden configs, disable unused features |
| 6 | Vulnerable Components | Using libraries with known vulnerabilities | Keep dependencies updated, scan for CVEs |
| 7 | Authentication Failures | Weak auth, session management issues | MFA, secure session management, rate limiting |
| 8 | Data Integrity Failures | Assuming data hasn't been tampered with | Digital signatures, integrity checks |
| 9 | Logging Failures | Insufficient logging, no monitoring | Log security events, monitor for anomalies |
| 10 | SSRF | Server-side request forgery | Validate/sanitize URLs, use allowlists |
SQL Injection Prevention (Python)
# ❌ VULNERABLE to SQL injection
import sqlite3
def get_user_vulnerable(email):
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
# NEVER DO THIS - allows SQL injection!
query = f"SELECT * FROM users WHERE email = '{email}'"
cursor.execute(query)
return cursor.fetchone()
# Attacker input: ' OR '1'='1
# Query becomes: SELECT * FROM users WHERE email = '' OR '1'='1'
# Returns ALL users!# ✅ SAFE: Parameterized queries
import sqlite3
def get_user_safe(email):
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
# Use parameterized query - prevents injection
query = "SELECT * FROM users WHERE email = ?"
cursor.execute(query, (email,))
return cursor.fetchone()
# With ORMs (even safer)
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
email = Column(String)
name = Column(String)
# Safe by default
user = session.query(User).filter_by(email=email).first()XSS (Cross-Site Scripting) Prevention
# Flask: Auto-escaping with Jinja2
from flask import Flask, render_template, request
from markupsafe import escape
app = Flask(__name__)
@app.route('/search')
def search():
query = request.args.get('q', '')
# Jinja2 auto-escapes by default
return render_template('search.html', query=query)
# template: search.html
# {{ query }} - automatically escaped
# Attacker input: <script>alert('XSS')</script>
# Rendered as: <script>alert('XSS')</script>
# Browser shows the text, doesn't execute
# If you need to allow some HTML (dangerous!)
from bleach import clean
def sanitize_html(html):
"""Allow only safe HTML tags"""
return clean(
html,
tags=['p', 'br', 'strong', 'em', 'a'],
attributes={'a': ['href']},
strip=True
)Input Validation
# Validate all user input
from flask import Flask, request, jsonify
from pydantic import BaseModel, EmailStr, field_validator
from typing import Optional
app = Flask(__name__)
class UserCreate(BaseModel):
email: EmailStr # Validates email format
username: str
age: Optional[int] = None
@field_validator('username')
@classmethod
def username_alphanumeric(cls, v):
if not v.isalnum():
raise ValueError('Username must be alphanumeric')
if len(v) < 3 or len(v) > 20:
raise ValueError('Username must be 3-20 characters')
return v
@field_validator('age')
@classmethod
def age_range(cls, v):
if v is not None and (v < 13 or v > 120):
raise ValueError('Age must be between 13 and 120')
return v
@app.route('/users', methods=['POST'])
def create_user():
try:
# Validate input
user_data = UserCreate(**request.json)
# Use validated data
create_user_in_db(user_data)
return jsonify({'message': 'User created'}), 201
except ValueError as e:
return jsonify({'error': str(e)}), 400□ Use parameterized queries (prevent SQL injection)
□ Escape output (prevent XSS)
□ Validate all input (allowlist, not denylist)
□ Use HTTPS everywhere
□ Set secure HTTP headers (CSP, X-Frame-Options, etc.)
□ Implement CSRF protection
□ Keep dependencies updated (scan for CVEs)
□ Use security headers (helmet.js for Node, flask-talisman for Flask)
□ Rate limiting on APIs
□ Regular security audits and pen testing
Infrastructure Hardening
Hardening reduces attack surface by disabling unnecessary services, applying patches, and following security benchmarks.
OS Hardening (Linux)
# Basic Linux hardening steps
# 1. Keep system updated sudo apt update && sudo apt upgrade -y # 2. Disable unused services sudo systemctl disable bluetooth sudo systemctl disable cups # Printer service # 3. Configure firewall (UFW) sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow 22/tcp # SSH sudo ufw allow 80/tcp # HTTP sudo ufw allow 443/tcp # HTTPS sudo ufw enable # 4. Secure SSH # Edit /etc/ssh/sshd_config: PermitRootLogin no PasswordAuthentication no # Use keys only Port 2222 # Change default port (optional) MaxAuthTries 3 # 5. Fail2ban (block brute-force attacks) sudo apt install fail2ban sudo systemctl enable fail2ban # 6. Disable unused network protocols sudo sysctl -w net.ipv4.conf.all.send_redirects=0 sudo sysctl -w net.ipv4.conf.all.accept_redirects=0 # 7. Set file permissions chmod 600 /home/user/.ssh/authorized_keys chmod 700 /home/user/.ssh # 8. Remove unnecessary packages sudo apt autoremove
Container Security
# Secure Dockerfile
# Use specific version tags, not 'latest' FROM python:3.11.6-slim # Create non-root user RUN useradd -m -u 1000 appuser # Set working directory WORKDIR /app # Copy dependencies first (layer caching) COPY requirements.txt . # Install dependencies RUN pip install --no-cache-dir -r requirements.txt # Copy application code COPY . . # Change ownership to non-root user RUN chown -R appuser:appuser /app # Switch to non-root user USER appuser # Run as non-root CMD ["python", "app.py"]
# Scan Docker images for vulnerabilities
# Using Trivy docker run aquasec/trivy image myapp:latest # Using Docker Scout docker scout cves myapp:latest # Using Snyk snyk container test myapp:latest
Kubernetes Security
# Secure Pod configuration
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:1.0.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities:
drop:
- ALL
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "250m"
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}□ Apply CIS Benchmarks for your OS
□ Automated patch management
□ Run containers as non-root
□ Use read-only filesystems where possible
□ Scan images for vulnerabilities before deployment
□ Network segmentation (VPCs, subnets)
□ Principle of least privilege (IAM roles)
□ Enable audit logging
□ Regular security assessments
Compliance Frameworks
Compliance frameworks define security and privacy requirements for specific industries or use cases.
| Framework | Purpose | Key Requirements | Who Needs It |
|---|---|---|---|
| SOC 2 | Trust service criteria for SaaS | Security, availability, confidentiality, privacy controls | B2B SaaS companies selling to enterprises |
| ISO 27001 | Information security management | ISMS, risk assessment, controls implementation | Global enterprises, European market |
| GDPR | EU data privacy regulation | Consent, data portability, right to deletion, breach notification | Companies with EU customers/employees |
| HIPAA | US healthcare data protection | PHI encryption, access controls, audit trails, BAAs | Healthcare providers, health tech companies |
| PCI-DSS | Payment card security | Secure cardholder data, encryption, access control, monitoring | Companies that process/store credit cards |
| FedRAMP | US federal cloud security | NIST controls, continuous monitoring, authorization | Cloud providers serving US government |
SOC 2 Trust Service Criteria
Security (Required)
Access controls, MFA, encryption, firewalls, IDS/IPS
Availability
Uptime monitoring, redundancy, disaster recovery, backups
Processing Integrity
Data accuracy, completeness, authorized processing
Confidentiality
Encrypt sensitive data, NDA, data classification
Privacy
Privacy policy, consent, data retention, deletion on request
GDPR Key Requirements
GDPR Principles:
1. LAWFULNESS - Legal basis for processing (consent, contract, etc.)
2. PURPOSE LIMITATION - Collect data for specific purposes
3. DATA MINIMIZATION - Only collect what's necessary
4. ACCURACY - Keep data accurate and up-to-date
5. STORAGE LIMITATION - Don't keep data longer than needed
6. INTEGRITY & CONFIDENTIALITY - Protect against unauthorized access
7. ACCOUNTABILITY - Demonstrate compliance
User Rights:
• Right to access (get copy of their data)
• Right to rectification (correct inaccurate data)
• Right to erasure ("right to be forgotten")
• Right to data portability (export data)
• Right to object to processing
• Right to restrict processing
Requirements:
• Obtain explicit consent before processing
• Data breach notification (72 hours)
• Privacy by design and default
• Data Protection Impact Assessment (DPIA) for high-risk processing
• Appoint Data Protection Officer (DPO) if applicable
• Cross-border data transfer protectionsGDPR Implementation (Python Example)
# GDPR data subject rights implementation
from flask import Flask, request, jsonify, send_file
import json
from datetime import datetime, timezone
app = Flask(__name__)
@app.route('/user/<user_id>/data', methods=['GET'])
def export_user_data(user_id):
"""Right to data portability - export all user data"""
# Gather all user data from databases
user_data = {
'user_info': get_user_info(user_id),
'orders': get_user_orders(user_id),
'preferences': get_user_preferences(user_id),
'activity_log': get_user_activity(user_id),
'exported_at': datetime.now(timezone.utc).isoformat()
}
# Create JSON file
filename = f"user_data_{user_id}.json"
with open(filename, 'w') as f:
json.dump(user_data, f, indent=2)
# Return file for download
return send_file(filename, as_attachment=True)
@app.route('/user/<user_id>', methods=['DELETE'])
def delete_user_data(user_id):
"""Right to erasure - delete all user data"""
# Verify user consent/request
if not verify_deletion_request(user_id):
return jsonify({'error': 'Invalid deletion request'}), 403
# Delete from all systems
delete_user_from_database(user_id)
delete_user_from_analytics(user_id)
delete_user_from_email_list(user_id)
# Anonymize logs (can't delete for compliance)
anonymize_user_logs(user_id)
# Log deletion (audit trail)
log_gdpr_deletion(user_id, datetime.now(timezone.utc))
return jsonify({'message': 'User data deleted'}), 200
@app.route('/user/<user_id>/consent', methods=['POST'])
def update_consent(user_id):
"""Record user consent for data processing"""
consent_data = {
'user_id': user_id,
'marketing_emails': request.json.get('marketing_emails', False),
'analytics': request.json.get('analytics', False),
'third_party_sharing': request.json.get('third_party_sharing', False),
'timestamp': datetime.now(timezone.utc).isoformat(),
'ip_address': request.remote_addr
}
# Store consent record
save_consent_record(consent_data)
return jsonify({'message': 'Consent updated'}), 200• Start early - compliance takes months
• Document everything (policies, procedures, evidence)
• Use compliance automation tools (Vanta, Drata, Secureframe)
• Regular internal audits
• Employee security training
• Engage legal counsel for regulations
• Consider compliance-as-a-service for SOC 2/ISO
Security Scanning & Testing
Automate security testing to catch vulnerabilities before they reach production.
| Type | What It Scans | Tools |
|---|---|---|
| SAST | Source code for vulnerabilities | SonarQube, Semgrep, Bandit (Python) |
| DAST | Running application (black-box testing) | OWASP ZAP, Burp Suite |
| SCA | Dependencies for known CVEs | Snyk, Dependabot, Safety (Python) |
| Container Scanning | Docker images for vulnerabilities | Trivy, Clair, Docker Scout, Snyk |
| Secret Scanning | Code/commits for leaked secrets | GitGuardian, TruffleHog, GitHub Secret Scanning |
| IaC Scanning | Terraform/CloudFormation misconfigs | Checkov, tfsec, Terraform Sentinel |
Python Security Scanning
# Bandit - SAST for Python
# Install pip install bandit # Scan project bandit -r . -f json -o bandit-report.json # Common issues Bandit finds: # - Hardcoded passwords # - SQL injection vulnerabilities # - Use of insecure random (random vs secrets) # - Insecure deserialization (pickle) # - Shell injection vulnerabilities
# Safety - check dependencies for CVEs
# Install pip install safety # Check current environment safety check # Check requirements.txt safety check -r requirements.txt # Generate report safety check --json > safety-report.json # In CI/CD safety check --exit-code 1 # Fail build if vulnerabilities found
CI/CD Security Integration
# GitHub Actions - security scanning
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# SAST - Python code scanning
- name: Bandit Security Scan
run: |
pip install bandit
bandit -r . -f json -o bandit-report.json
# SCA - Dependency vulnerabilities
- name: Safety Check
run: |
pip install safety
safety check --json > safety-report.json
# Secret scanning
- name: TruffleHog Secret Scan
uses: trufflesecurity/trufflehog@main
with:
path: ./
# Container scanning
- name: Build Docker image
run: docker build -t myapp:$GITHUB_SHA .
- name: Trivy Container Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:$GITHUB_SHA
severity: 'CRITICAL,HIGH'
exit-code: '1' # Fail if vulnerabilities found
# IaC scanning (Trivy config scan - successor to tfsec)
- name: Terraform Security Scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'config'
scan-ref: '.'
severity: 'CRITICAL,HIGH'
exit-code: '1'• Run SAST on every commit (find issues early)
• Scan dependencies daily (new CVEs published constantly)
• Block deployment if critical vulnerabilities found
• Regular pen testing (annually or after major changes)
• Bug bounty program for production systems
• Security champions in each team
• Threat modeling for new features
Key Takeaways
- CIA Triad: Confidentiality, Integrity, Availability, foundation of security
- Defense in Depth: Layered security across network, infra, app, data levels
- Secrets Management: NEVER hardcode credentials, use Vault, AWS Secrets Manager
- Authentication: MFA everywhere, JWT for APIs, OAuth for third-party login
- Authorization: RBAC, principle of least privilege, server-side enforcement
- OWASP Top 10: Injection, broken auth, XSS, access control, protect against these
- Infrastructure Hardening: Patch management, disable unused services, non-root containers
- Compliance: SOC 2 (SaaS), GDPR (EU data), HIPAA (healthcare), PCI-DSS (payments)
- Security Scanning: SAST, SCA, container scanning in CI/CD pipeline
- Security is Continuous: Not a one-time project, requires ongoing vigilance
Security Implementation Roadmap
Phase 1: Foundation (Month 1-2)
- Set up secrets management (AWS Secrets Manager or Vault)
- Implement MFA for all admin accounts
- Enable HTTPS everywhere
- Basic input validation and parameterized queries
- Set up basic monitoring and logging
Phase 2: Hardening (Month 3-4)
- OS and container hardening
- Implement RBAC and authorization
- Security scanning in CI/CD (SAST, SCA, container scanning)
- Regular dependency updates
- Network segmentation (VPCs, security groups)
Phase 3: Compliance (Month 5-8)
- Document security policies and procedures
- Implement compliance controls (SOC 2, GDPR, etc.)
- Employee security training
- Third-party vendor assessments
- Engage auditors for SOC 2 Type II
Phase 4: Maturity (Ongoing)
- Regular penetration testing
- Bug bounty program
- Advanced threat detection (SIEM)
- Incident response drills
- Continuous compliance monitoring
- Security champions program