Security Essentials & Best Practices

Security best practices, OWASP Top 10, secure coding practices, and security testing tools.


OWASP Top 10 (2021)

graph TD
    A[OWASP Top 10] --> B[A01: Broken Access Control]
    A --> C[A02: Cryptographic Failures]
    A --> D[A03: Injection]
    A --> E[A04: Insecure Design]
    A --> F[A05: Security Misconfiguration]
    A --> G[A06: Vulnerable Components]
    A --> H[A07: Authentication Failures]
    A --> I[A08: Data Integrity Failures]
    A --> J[A09: Logging Failures]
    A --> K[A10: SSRF]
    
    style A fill:#ff6b6b
    style B fill:#ffe66d
    style C fill:#ffe66d
    style D fill:#ffe66d

A01: Broken Access Control

Vulnerability

1# ❌ Insecure Direct Object Reference (IDOR)
2@app.route('/api/users/<user_id>')
3def get_user(user_id):
4    user = db.query(f"SELECT * FROM users WHERE id = {user_id}")
5    return jsonify(user)
6
7# Attacker can access any user: /api/users/123

Mitigation

 1# ✅ Check authorization
 2@app.route('/api/users/<user_id>')
 3@login_required
 4def get_user(user_id):
 5    current_user = get_current_user()
 6    
 7    # Check if user can access this resource
 8    if current_user.id != user_id and not current_user.is_admin:
 9        abort(403)
10    
11    user = db.query("SELECT * FROM users WHERE id = ?", [user_id])
12    return jsonify(user)
13
14# ✅ Use UUIDs instead of sequential IDs
15import uuid
16
17user_id = str(uuid.uuid4())  # e.g., '550e8400-e29b-41d4-a716-446655440000'

A02: Cryptographic Failures

Vulnerability

1# ❌ Storing passwords in plain text
2user.password = request.form['password']
3db.save(user)
4
5# ❌ Using weak hashing
6import hashlib
7user.password = hashlib.md5(password.encode()).hexdigest()

Mitigation

 1# ✅ Use bcrypt for password hashing
 2import bcrypt
 3
 4# Hash password
 5password = "user_password"
 6hashed = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
 7
 8# Verify password
 9if bcrypt.checkpw(password.encode('utf-8'), hashed):
10    print("Password matches")
11
12# ✅ Use strong encryption for sensitive data
13from cryptography.fernet import Fernet
14
15# Generate key
16key = Fernet.generate_key()
17cipher = Fernet(key)
18
19# Encrypt
20encrypted = cipher.encrypt(b"sensitive data")
21
22# Decrypt
23decrypted = cipher.decrypt(encrypted)

A03: Injection

SQL Injection

1# ❌ Vulnerable to SQL injection
2username = request.form['username']
3query = f"SELECT * FROM users WHERE username = '{username}'"
4db.execute(query)
5
6# Attack: username = "admin' OR '1'='1"
7# Result: SELECT * FROM users WHERE username = 'admin' OR '1'='1'
 1# ✅ Use parameterized queries
 2username = request.form['username']
 3query = "SELECT * FROM users WHERE username = ?"
 4db.execute(query, [username])
 5
 6# ✅ Use ORM
 7from sqlalchemy import select
 8
 9stmt = select(User).where(User.username == username)
10result = session.execute(stmt)

Command Injection

1# ❌ Vulnerable to command injection
2filename = request.form['filename']
3os.system(f'cat {filename}')
4
5# Attack: filename = "file.txt; rm -rf /"
 1# ✅ Use safe alternatives
 2import subprocess
 3
 4filename = request.form['filename']
 5# Validate filename
 6if not re.match(r'^[a-zA-Z0-9_.-]+$', filename):
 7    abort(400)
 8
 9# Use subprocess with list
10result = subprocess.run(['cat', filename], capture_output=True)

XSS (Cross-Site Scripting)

1# ❌ Vulnerable to XSS
2@app.route('/search')
3def search():
4    query = request.args.get('q')
5    return f'<h1>Results for: {query}</h1>'
6
7# Attack: /search?q=<script>alert('XSS')</script>
 1# ✅ Escape user input
 2from markupsafe import escape
 3
 4@app.route('/search')
 5def search():
 6    query = request.args.get('q')
 7    return f'<h1>Results for: {escape(query)}</h1>'
 8
 9# ✅ Use templating engines (auto-escape)
10return render_template('search.html', query=query)
11
12# ✅ Content Security Policy
13@app.after_request
14def set_csp(response):
15    response.headers['Content-Security-Policy'] = "default-src 'self'"
16    return response

A04: Insecure Design

Mitigation

 1# ✅ Implement rate limiting
 2from flask_limiter import Limiter
 3
 4limiter = Limiter(
 5    app,
 6    key_func=lambda: request.remote_addr,
 7    default_limits=["200 per day", "50 per hour"]
 8)
 9
10@app.route('/api/login')
11@limiter.limit("5 per minute")
12def login():
13    # Login logic
14    pass
15
16# ✅ Implement account lockout
17class User:
18    failed_login_attempts = 0
19    locked_until = None
20    
21    def check_login(self, password):
22        if self.locked_until and datetime.now() < self.locked_until:
23            raise AccountLockedException()
24        
25        if self.verify_password(password):
26            self.failed_login_attempts = 0
27            return True
28        else:
29            self.failed_login_attempts += 1
30            if self.failed_login_attempts >= 5:
31                self.locked_until = datetime.now() + timedelta(minutes=15)
32            return False

A05: Security Misconfiguration

Checklist

 1# ✅ Disable debug mode in production
 2DEBUG = False
 3
 4# ✅ Remove default credentials
 5# Change default admin/admin, root/root, etc.
 6
 7# ✅ Disable directory listing
 8# Apache: Options -Indexes
 9# Nginx: autoindex off;
10
11# ✅ Remove unnecessary services
12sudo systemctl disable <service>
13
14# ✅ Keep software updated
15sudo apt update && sudo apt upgrade
16
17# ✅ Configure HTTPS
18# Use Let's Encrypt
19sudo certbot --nginx -d example.com
20
21# ✅ Set secure headers
22@app.after_request
23def set_security_headers(response):
24    response.headers['X-Content-Type-Options'] = 'nosniff'
25    response.headers['X-Frame-Options'] = 'DENY'
26    response.headers['X-XSS-Protection'] = '1; mode=block'
27    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
28    return response

A06: Vulnerable and Outdated Components

Tools

 1# Python
 2pip install safety
 3safety check
 4
 5# Node.js
 6npm audit
 7npm audit fix
 8
 9# Go
10go list -json -m all | nancy sleuth
11
12# Docker
13docker scan myimage:latest
14
15# Snyk
16npm install -g snyk
17snyk test

A07: Identification and Authentication Failures

Best Practices

 1# ✅ Implement MFA
 2from pyotp import TOTP
 3
 4# Generate secret
 5secret = pyotp.random_base32()
 6
 7# Verify TOTP
 8totp = TOTP(secret)
 9if totp.verify(user_code):
10    print("Valid code")
11
12# ✅ Secure password reset
13import secrets
14
15def generate_reset_token():
16    return secrets.token_urlsafe(32)
17
18def send_reset_email(user):
19    token = generate_reset_token()
20    # Store token with expiration
21    user.reset_token = token
22    user.reset_token_expires = datetime.now() + timedelta(hours=1)
23    # Send email with token
24    send_email(user.email, f"Reset link: /reset?token={token}")
25
26# ✅ Implement session timeout
27@app.before_request
28def check_session_timeout():
29    if 'last_activity' in session:
30        last_activity = session['last_activity']
31        if datetime.now() - last_activity > timedelta(minutes=30):
32            session.clear()
33            return redirect('/login')
34    session['last_activity'] = datetime.now()

A08: Software and Data Integrity Failures

Mitigation

 1# ✅ Verify file uploads
 2ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
 3
 4def allowed_file(filename):
 5    return '.' in filename and \
 6           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
 7
 8@app.route('/upload', methods=['POST'])
 9def upload_file():
10    file = request.files['file']
11    
12    if not allowed_file(file.filename):
13        abort(400)
14    
15    # Verify file content (not just extension)
16    import magic
17    mime = magic.from_buffer(file.read(1024), mime=True)
18    if mime not in ['image/png', 'image/jpeg', 'image/gif']:
19        abort(400)
20    
21    # Save with secure filename
22    from werkzeug.utils import secure_filename
23    filename = secure_filename(file.filename)
24    file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
25
26# ✅ Use Subresource Integrity (SRI)
27<script src="https://cdn.example.com/lib.js"
28        integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
29        crossorigin="anonymous"></script>

A09: Security Logging and Monitoring Failures

Implementation

 1import logging
 2from logging.handlers import RotatingFileHandler
 3
 4# Configure logging
 5handler = RotatingFileHandler('security.log', maxBytes=10000000, backupCount=5)
 6handler.setLevel(logging.INFO)
 7
 8formatter = logging.Formatter(
 9    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
10)
11handler.setFormatter(formatter)
12
13security_logger = logging.getLogger('security')
14security_logger.addHandler(handler)
15
16# Log security events
17@app.route('/login', methods=['POST'])
18def login():
19    username = request.form['username']
20    password = request.form['password']
21    
22    user = User.query.filter_by(username=username).first()
23    
24    if user and user.verify_password(password):
25        security_logger.info(f"Successful login: {username} from {request.remote_addr}")
26        return redirect('/dashboard')
27    else:
28        security_logger.warning(f"Failed login attempt: {username} from {request.remote_addr}")
29        return redirect('/login')
30
31# Monitor for suspicious activity
32def detect_brute_force(username, ip_address):
33    """Detect brute force attempts"""
34    recent_attempts = FailedLogin.query.filter(
35        FailedLogin.username == username,
36        FailedLogin.ip_address == ip_address,
37        FailedLogin.timestamp > datetime.now() - timedelta(minutes=5)
38    ).count()
39    
40    if recent_attempts > 5:
41        security_logger.critical(f"Brute force detected: {username} from {ip_address}")
42        # Send alert
43        send_alert(f"Brute force attack detected from {ip_address}")

A10: Server-Side Request Forgery (SSRF)

Vulnerability

1# ❌ Vulnerable to SSRF
2@app.route('/fetch')
3def fetch_url():
4    url = request.args.get('url')
5    response = requests.get(url)
6    return response.content
7
8# Attack: /fetch?url=http://localhost:6379/
9# Can access internal services

Mitigation

 1# ✅ Whitelist allowed domains
 2ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com']
 3
 4def is_safe_url(url):
 5    from urllib.parse import urlparse
 6    parsed = urlparse(url)
 7    return parsed.netloc in ALLOWED_DOMAINS
 8
 9@app.route('/fetch')
10def fetch_url():
11    url = request.args.get('url')
12    
13    if not is_safe_url(url):
14        abort(400)
15    
16    response = requests.get(url, timeout=5)
17    return response.content
18
19# ✅ Block private IP ranges
20import ipaddress
21
22def is_private_ip(hostname):
23    try:
24        ip = ipaddress.ip_address(hostname)
25        return ip.is_private or ip.is_loopback
26    except ValueError:
27        return False

Security Testing Tools

Static Analysis

 1# Python - Bandit
 2pip install bandit
 3bandit -r ./myproject
 4
 5# JavaScript - ESLint Security
 6npm install --save-dev eslint-plugin-security
 7eslint --plugin security .
 8
 9# Go - Gosec
10go install github.com/securego/gosec/v2/cmd/gosec@latest
11gosec ./...

Dynamic Analysis

1# OWASP ZAP
2docker run -t owasp/zap2docker-stable zap-baseline.py -t https://example.com
3
4# Burp Suite
5# GUI tool for web application security testing
6
7# Nikto
8nikto -h https://example.com

Dependency Scanning

1# Python
2pip install safety
3safety check
4
5# Node.js
6npm audit
7
8# Snyk (multi-language)
9snyk test

Security Checklist

 1□ Use HTTPS everywhere
 2□ Implement proper authentication
 3□ Use strong password hashing (bcrypt, Argon2)
 4□ Validate and sanitize all input
 5□ Use parameterized queries (prevent SQL injection)
 6□ Implement CSRF protection
 7□ Set secure HTTP headers
 8□ Implement rate limiting
 9□ Log security events
10□ Keep dependencies updated
11□ Use environment variables for secrets
12□ Implement proper error handling (don't leak info)
13□ Use Content Security Policy
14□ Implement account lockout
15□ Use secure session management
16□ Validate file uploads
17□ Implement proper authorization checks
18□ Use security scanning tools
19□ Conduct regular security audits
20□ Have an incident response plan

Related Snippets