Back to skills

Security Patterns

Comprehensive OWASP security guidelines, secure coding patterns, vulnerability prevention strategies, and remediation best practices for building secure applications

9 stars
0 votes
0 copies
0 views
Added 12/19/2025
developmentpythonrustgoshellsqlflaskawstestingdebugginggit

Works with

cliapi

Install via CLI

$openskills install bejranonda/LLM-Autonomous-Agent-Plugin-for-Claude
Download Zip
Files
SKILL.md
---
name: security-patterns
description: Comprehensive OWASP security guidelines, secure coding patterns, vulnerability prevention strategies, and remediation best practices for building secure applications
version: 1.0.0
---

## Security Patterns Skill

Provides comprehensive security knowledge based on OWASP Top 10, secure coding practices, common vulnerability patterns, and proven remediation strategies.

## Core Philosophy: Secure by Default

**Security is not optional**. Every line of code should be written with security in mind. This skill provides the knowledge to:
- Prevent vulnerabilities before they occur
- Detect security issues early
- Remediate problems effectively
- Build security into the development process

## OWASP Top 10 (2021) - Deep Dive

### A01: Broken Access Control

**What It Is**: Failures that allow users to act outside their intended permissions.

**Common Vulnerabilities**:
```python
# ❌ INSECURE: No authorization check
@app.route('/api/user/<int:user_id>/profile')
def get_profile(user_id):
    user = User.query.get(user_id)
    return jsonify(user.to_dict())

# ✅ SECURE: Proper authorization
@app.route('/api/user/<int:user_id>/profile')
@require_auth
def get_profile(user_id):
    # Check if current user can access this profile
    if current_user.id != user_id and not current_user.is_admin:
        abort(403)  # Forbidden

    user = User.query.get_or_404(user_id)
    return jsonify(user.to_dict())
```

**Prevention Strategies**:
1. **Deny by Default**: Require explicit permission grants
2. **Principle of Least Privilege**: Grant minimum necessary permissions
3. **Verify on Server**: Never trust client-side access control
4. **Use Mature Frameworks**: Leverage battle-tested authorization libraries
5. **Log Access Failures**: Monitor for unauthorized access attempts

**Testing**:
```python
def test_authorization():
    """Test that users can only access their own data."""
    # Create two users
    user1 = create_user()
    user2 = create_user()

    # User1 tries to access User2's data
    response = client.get(
        f'/api/user/{user2.id}/profile',
        headers={'Authorization': f'Bearer {user1.token}'}
    )

    assert response.status_code == 403  # Should be forbidden
```

### A02: Cryptographic Failures

**What It Is**: Failures related to cryptography that expose sensitive data.

**Secure Patterns**:

**Password Hashing**:
```python
# ❌ INSECURE: Weak hashing
import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest()

# ✅ SECURE: Strong password hashing
import bcrypt

def hash_password(password: str) -> str:
    salt = bcrypt.gensalt(rounds=12)  # Cost factor 12
    return bcrypt.hashpw(password.encode('utf-8'), salt).decode('utf-8')

def verify_password(password: str, hashed: str) -> bool:
    return bcrypt.checkpw(password.encode('utf-8'), hashed.encode('utf-8'))
```

**Encryption**:
```python
# ✅ SECURE: AES-256 encryption
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2
import base64

def generate_encryption_key(password: str, salt: bytes) -> bytes:
    """Generate encryption key from password."""
    kdf = PBKDF2(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=100000,
    )
    return base64.urlsafe_b64encode(kdf.derive(password.encode()))

def encrypt_data(data: str, key: bytes) -> str:
    """Encrypt data using Fernet (AES-128-CBC + HMAC)."""
    f = Fernet(key)
    return f.encrypt(data.encode()).decode()

def decrypt_data(encrypted: str, key: bytes) -> str:
    """Decrypt data."""
    f = Fernet(key)
    return f.decrypt(encrypted.encode()).decode()
```

**Secure Random**:
```python
# ❌ INSECURE: Predictable random
import random
token = str(random.randint(100000, 999999))

# ✅ SECURE: Cryptographically secure random
import secrets

def generate_secure_token(length: int = 32) -> str:
    """Generate cryptographically secure token."""
    return secrets.token_urlsafe(length)

def generate_reset_token() -> str:
    """Generate password reset token."""
    return secrets.token_hex(32)  # 64 character hex string
```

**Secret Management**:
```python
# ❌ INSECURE: Hardcoded secrets
API_KEY = "sk_live_abcdef123456"
DB_PASSWORD = "mysecretpassword"

# ✅ SECURE: Environment variables
import os
from dotenv import load_dotenv

load_dotenv()  # Load from .env file

API_KEY = os.environ.get('API_KEY')
DB_PASSWORD = os.environ.get('DB_PASSWORD')

if not API_KEY:
    raise ValueError("API_KEY environment variable not set")
```

### A03: Injection

**SQL Injection Prevention**:
```python
# ❌ INSECURE: String concatenation
def get_user_by_username(username):
    query = f"SELECT * FROM users WHERE username = '{username}'"
    return db.execute(query)

# ✅ SECURE: Parameterized queries
def get_user_by_username(username):
    query = "SELECT * FROM users WHERE username = %s"
    return db.execute(query, (username,))

# ✅ SECURE: ORM usage
def get_user_by_username(username):
    return User.query.filter_by(username=username).first()
```

**Command Injection Prevention**:
```python
# ❌ INSECURE: Shell command with user input
import os
def ping_host(hostname):
    os.system(f"ping -c 4 {hostname}")

# ✅ SECURE: Subprocess with list arguments
import subprocess
def ping_host(hostname):
    # Validate hostname
    if not re.match(r'^[a-zA-Z0-9.-]+$', hostname):
        raise ValueError("Invalid hostname")

    result = subprocess.run(
        ['ping', '-c', '4', hostname],
        capture_output=True,
        text=True,
        timeout=10
    )
    return result.stdout
```

**NoSQL Injection Prevention**:
```python
# ❌ INSECURE: Direct query construction
def find_user(user_id):
    query = {"_id": user_id}  # If user_id is dict, can inject
    return db.users.find_one(query)

# ✅ SECURE: Type validation
def find_user(user_id):
    # Ensure user_id is a string
    if not isinstance(user_id, str):
        raise TypeError("user_id must be string")

    from bson.objectid import ObjectId
    try:
        query = {"_id": ObjectId(user_id)}
    except:
        return None

    return db.users.find_one(query)
```

**Template Injection Prevention**:
```python
# ❌ INSECURE: Rendering user input as template
from flask import render_template_string
def render_page(template_str):
    return render_template_string(template_str)

# ✅ SECURE: Render with automatic escaping
from flask import render_template
def render_page(data):
    return render_template('page.html', data=data)
# In template: {{ data|e }} or use autoescaping
```

### A04: Insecure Design

**Secure Design Patterns**:

**Rate Limiting**:
```python
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(
    app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route('/api/login', methods=['POST'])
@limiter.limit("5 per minute")  # Prevent brute force
def login():
    # Login logic
    pass
```

**Business Logic Protection**:
```python
# ✅ SECURE: Prevent business logic flaws
class EcommerceCart:
    def apply_discount(self, code: str) -> bool:
        """Apply discount code with proper validation."""
        # Validate discount hasn't been used
        if self.discount_used:
            raise ValueError("Discount already applied")

        # Validate discount code
        discount = DiscountCode.query.filter_by(
            code=code,
            active=True
        ).first()

        if not discount:
            return False

        # Check expiration
        if discount.expires_at < datetime.now():
            return False

        # Check usage limit
        if discount.usage_count >= discount.max_uses:
            return False

        # Check minimum purchase amount
        if self.total < discount.min_purchase:
            return False

        # Apply discount
        self.discount_amount = min(
            self.total * discount.percentage / 100,
            discount.max_discount_amount
        )
        self.discount_used = True
        discount.usage_count += 1

        return True
```

### A05: Security Misconfiguration

**Secure Configuration Checklist**:

**Security Headers**:
```python
from flask import Flask
from flask_talisman import Talisman

app = Flask(__name__)

# Force HTTPS and set security headers
Talisman(app,
    force_https=True,
    strict_transport_security=True,
    strict_transport_security_max_age=31536000,
    content_security_policy={
        'default-src': "'self'",
        'script-src': ["'self'", "'unsafe-inline'"],
        'style-src': ["'self'", "'unsafe-inline'"],
        'img-src': ["'self'", "data:", "https:"],
    },
    content_security_policy_nonce_in=['script-src'],
    referrer_policy='strict-origin-when-cross-origin',
    feature_policy={
        'geolocation': "'none'",
        'microphone': "'none'",
        'camera': "'none'",
    }
)

@app.after_request
def set_security_headers(response):
    """Set additional security headers."""
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-XSS-Protection'] = '1; mode=block'
    response.headers['Permissions-Policy'] = 'geolocation=(), microphone=(), camera=()'
    return response
```

**CORS Configuration**:
```python
# ❌ INSECURE: Wildcard CORS
from flask_cors import CORS
CORS(app, origins="*")  # Allows any origin

# ✅ SECURE: Specific origins
CORS(app,
    origins=["https://yourdomain.com", "https://app.yourdomain.com"],
    methods=["GET", "POST"],
    allow_headers=["Content-Type", "Authorization"],
    max_age=3600,
    supports_credentials=True
)
```

**Error Handling**:
```python
# ❌ INSECURE: Verbose error messages
@app.errorhandler(Exception)
def handle_error(error):
    return jsonify({
        "error": str(error),
        "traceback": traceback.format_exc()
    }), 500

# ✅ SECURE: Generic error messages
@app.errorhandler(Exception)
def handle_error(error):
    # Log full error for debugging
    app.logger.error(f"Error: {error}", exc_info=True)

    # Return generic message to user
    return jsonify({
        "error": "An internal error occurred",
        "request_id": generate_request_id()
    }), 500
```

### A06: Vulnerable Components

**Dependency Management**:
```python
# requirements.txt - Pin versions
flask==2.3.0
requests==2.31.0
cryptography==41.0.0

# Use pip-audit or safety
$ pip-audit  # Check for vulnerabilities
$ safety check  # Alternative tool
```

**Automated Scanning**:
```yaml
# .github/workflows/security.yml
name: Security Scan

on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run pip-audit
        run: |
          pip install pip-audit
          pip-audit -r requirements.txt
```

### A07: Authentication Failures

**Secure Authentication Pattern**:
```python
from werkzeug.security import check_password_hash
import secrets
from datetime import datetime, timedelta

class SecureAuth:
    # Password policy
    MIN_PASSWORD_LENGTH = 12
    REQUIRE_UPPERCASE = True
    REQUIRE_LOWERCASE = True
    REQUIRE_DIGIT = True
    REQUIRE_SPECIAL = True

    # Account lockout
    MAX_LOGIN_ATTEMPTS = 5
    LOCKOUT_DURATION = timedelta(minutes=15)

    # Session security
    SESSION_TIMEOUT = timedelta(hours=2)
    SESSION_ABSOLUTE_TIMEOUT = timedelta(hours=8)

    @staticmethod
    def validate_password_strength(password: str) -> Tuple[bool, str]:
        """Validate password meets security requirements."""
        if len(password) < SecureAuth.MIN_PASSWORD_LENGTH:
            return False, f"Password must be at least {SecureAuth.MIN_PASSWORD_LENGTH} characters"

        if SecureAuth.REQUIRE_UPPERCASE and not any(c.isupper() for c in password):
            return False, "Password must contain uppercase letter"

        if SecureAuth.REQUIRE_LOWERCASE and not any(c.islower() for c in password):
            return False, "Password must contain lowercase letter"

        if SecureAuth.REQUIRE_DIGIT and not any(c.isdigit() for c in password):
            return False, "Password must contain digit"

        if SecureAuth.REQUIRE_SPECIAL and not any(c in "!@#$%^&*" for c in password):
            return False, "Password must contain special character"

        return True, "Password meets requirements"

    @staticmethod
    def login(username: str, password: str) -> dict:
        """Secure login implementation."""
        user = User.query.filter_by(username=username).first()

        # Timing attack prevention: always hash even if user doesn't exist
        if not user:
            check_password_hash("$2b$12$dummy", password)
            return {"success": False, "message": "Invalid credentials"}

        # Check if account is locked
        if user.locked_until and user.locked_until > datetime.now():
            return {"success": False, "message": "Account temporarily locked"}

        # Verify password
        if not check_password_hash(user.password_hash, password):
            user.failed_login_attempts += 1

            # Lock account after max attempts
            if user.failed_login_attempts >= SecureAuth.MAX_LOGIN_ATTEMPTS:
                user.locked_until = datetime.now() + SecureAuth.LOCKOUT_DURATION

            db.session.commit()
            return {"success": False, "message": "Invalid credentials"}

        # Reset failed attempts on successful login
        user.failed_login_attempts = 0
        user.last_login = datetime.now()
        db.session.commit()

        # Create session
        session_token = secrets.token_urlsafe(32)
        session = UserSession(
            user_id=user.id,
            token=session_token,
            expires_at=datetime.now() + SecureAuth.SESSION_TIMEOUT,
            absolute_expires_at=datetime.now() + SecureAuth.SESSION_ABSOLUTE_TIMEOUT
        )
        db.session.add(session)
        db.session.commit()

        return {
            "success": True,
            "token": session_token,
            "expires_in": int(SecureAuth.SESSION_TIMEOUT.total_seconds())
        }
```

**Multi-Factor Authentication**:
```python
import pyotp

class MFAManager:
    @staticmethod
    def generate_secret() -> str:
        """Generate TOTP secret for user."""
        return pyotp.random_base32()

    @staticmethod
    def get_totp_uri(secret: str, username: str, issuer: str) -> str:
        """Generate QR code URI for TOTP app."""
        totp = pyotp.TOTP(secret)
        return totp.provisioning_uri(
            name=username,
            issuer_name=issuer
        )

    @staticmethod
    def verify_totp(secret: str, token: str, window: int = 1) -> bool:
        """Verify TOTP token with tolerance window."""
        totp = pyotp.TOTP(secret)
        return totp.verify(token, valid_window=window)

    @staticmethod
    def generate_backup_codes(count: int = 10) -> List[str]:
        """Generate one-time backup codes."""
        return [secrets.token_hex(4) for _ in range(count)]
```

### A08: Software and Data Integrity Failures

**Secure Deserialization**:
```python
# ❌ INSECURE: pickle allows code execution
import pickle
def load_data(data):
    return pickle.loads(data)

# ✅ SECURE: Use JSON or safer formats
import json
def load_data(data):
    return json.loads(data)

# If you must use pickle, sign the data
import hmac
import hashlib

def secure_pickle_dumps(obj, secret_key):
    """Pickle with HMAC signature."""
    pickled = pickle.dumps(obj)
    signature = hmac.new(secret_key, pickled, hashlib.sha256).hexdigest()
    return signature.encode() + b':' + pickled

def secure_pickle_loads(data, secret_key):
    """Verify signature before unpickling."""
    signature, pickled = data.split(b':', 1)
    expected_signature = hmac.new(secret_key, pickled, hashlib.sha256).hexdigest().encode()

    if not hmac.compare_digest(signature, expected_signature):
        raise ValueError("Invalid signature")

    return pickle.loads(pickled)
```

### A09: Logging and Monitoring

**Secure Logging Pattern**:
```python
import logging
from logging.handlers import RotatingFileHandler
import json

# Configure security event logging
security_logger = logging.getLogger('security')
security_logger.setLevel(logging.INFO)

handler = RotatingFileHandler(
    'logs/security.log',
    maxBytes=10485760,  # 10MB
    backupCount=10
)

formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
security_logger.addHandler(handler)

def log_security_event(event_type: str, user_id: str, details: dict):
    """Log security-relevant events."""
    event = {
        "event_type": event_type,
        "user_id": user_id,
        "timestamp": datetime.now().isoformat(),
        "details": details,
        "ip_address": request.remote_addr if request else None
    }

    security_logger.info(json.dumps(event))

# Usage
log_security_event("LOGIN_SUCCESS", user.id, {"username": user.username})
log_security_event("ACCESS_DENIED", user.id, {"resource": "/admin/users"})
log_security_event("PASSWORD_CHANGE", user.id, {})
```

### A10: Server-Side Request Forgery (SSRF)

**SSRF Prevention**:
```python
import requests
from urllib.parse import urlparse

ALLOWED_PROTOCOLS = ['http', 'https']
BLOCKED_IPS = [
    '127.0.0.0/8',    # Loopback
    '10.0.0.0/8',     # Private
    '172.16.0.0/12',  # Private
    '192.168.0.0/16', # Private
    '169.254.0.0/16', # Link-local
]

def is_safe_url(url: str) -> bool:
    """Validate URL is safe from SSRF."""
    parsed = urlparse(url)

    # Check protocol
    if parsed.scheme not in ALLOWED_PROTOCOLS:
        return False

    # Check for localhost/internal IPs
    hostname = parsed.hostname
    if not hostname:
        return False

    if hostname in ['localhost', '127.0.0.1', '0.0.0.0']:
        return False

    # Resolve and check IP
    import socket
    try:
        ip = socket.gethostbyname(hostname)

        import ipaddress
        ip_obj = ipaddress.ip_address(ip)

        # Check if private/internal
        if ip_obj.is_private or ip_obj.is_loopback:
            return False

    except:
        return False

    return True

def fetch_url(url: str) -> str:
    """Safely fetch URL content."""
    if not is_safe_url(url):
        raise ValueError("URL not allowed")

    response = requests.get(
        url,
        timeout=5,
        allow_redirects=False  # Prevent redirect to internal URLs
    )

    return response.text
```

## Secure Coding Checklist

### Input Validation
- [ ] All user input is validated
- [ ] Whitelist validation where possible
- [ ] Length limits enforced
- [ ] Type checking implemented
- [ ] Special characters handled

### Authentication
- [ ] Strong password policy enforced
- [ ] Multi-factor authentication available
- [ ] Account lockout after failed attempts
- [ ] Secure password reset process
- [ ] Session timeout configured

### Authorization
- [ ] All endpoints require authorization
- [ ] Principle of least privilege applied
- [ ] Authorization checked on server-side
- [ ] No IDOR vulnerabilities
- [ ] Admin functions protected

### Cryptography
- [ ] Strong algorithms used (AES-256, SHA-256)
- [ ] No hardcoded secrets
- [ ] Secure random for tokens
- [ ] TLS/HTTPS enforced
- [ ] Passwords hashed with bcrypt/argon2

### Data Protection
- [ ] Sensitive data encrypted at rest
- [ ] Sensitive data encrypted in transit
- [ ] PII properly handled
- [ ] Data retention policies implemented
- [ ] Secure deletion procedures

### Error Handling
- [ ] Generic error messages to users
- [ ] Detailed errors logged securely
- [ ] No stack traces exposed
- [ ] Sensitive data not in logs
- [ ] Error monitoring implemented

### Logging & Monitoring
- [ ] Security events logged
- [ ] Log tampering prevented
- [ ] Anomaly detection configured
- [ ] Alerting for critical events
- [ ] Regular log review

This skill provides the foundation for writing secure code and identifying vulnerabilities effectively.

Comments (0)

No comments yet. Be the first to comment!