Security Essentials & Best Practices

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


OWASP Top 10 (2021)


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