Security Architecture

Comprehensive security measures implemented in ModestWear API.

Overview

ModestWear implements defense-in-depth security with multiple layers:

  • Authentication: JWT tokens with rotation

  • Authorization: Role-based access control

  • Data Protection: Encryption at rest and in transit

  • Input Validation: Comprehensive sanitization

  • Rate Limiting: Prevent abuse

  • Monitoring: Security event logging


Authentication Security

JWT Token Management

Token Structure:

{
  "token_type": "access",
  "exp": 1705757700,
  "iat": 1705756800,
  "jti": "unique-token-id",
  "user_id": 15
}

Security Features:

  1. Short-Lived Access Tokens

    • Lifetime: 15 minutes

    • Reduces exposure window

    • Forces regular refresh

  2. Token Rotation

    • New refresh token on each refresh

    • Old token immediately blacklisted

    • Prevents token reuse attacks

  3. Token Blacklisting

    • Logout blacklists refresh token

    • Stored in Redis + database

    • Checked on every token use

  4. Secure Algorithm

    • HS256 (HMAC-SHA256)

    • Secret key never exposed

    • Signature verification on every request

Implementation:

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=14),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
}

Password Security

Hashing:

  • Algorithm: PBKDF2-SHA256

  • Iterations: 390,000 (Django 4.2 default)

  • Salt: Unique per password

  • Never stored in plaintext

Validation Rules:

AUTH_PASSWORD_VALIDATORS = [
    'UserAttributeSimilarityValidator',  # Not similar to username/email
    'MinimumLengthValidator',            # Min 8 characters
    'CommonPasswordValidator',           # Not in common password list
    'NumericPasswordValidator',          # Not entirely numeric
]

Example Rejected Passwords:

  • password123 (too common)

  • 12345678 (entirely numeric)

  • sarah@example.com (similar to email)

Account Lockout

Brute Force Protection:

# After 5 failed attempts
cache.set(f'account_lockout:{email}', True, timeout=900)  # 15 minutes

Features:

  • Tracks failed login attempts (Redis)

  • 5 attempts = 15-minute lockout

  • Counter resets on successful login

  • Logs all failed attempts with IP

Implementation:

failed_attempts = cache.get(f'failed_logins:{email}', 0) + 1
cache.set(f'failed_logins:{email}', failed_attempts, timeout=1800)

if failed_attempts >= 5:
    cache.set(f'account_lockout:{email}', True, timeout=900)
    logger.warning(f'Account locked: {email} from IP: {ip_address}')

Email Verification

Token Generation:

token_data = {
    'user_id': user.id,
    'email': user.email,
    'timestamp': int(time.time())
}
token = base64.urlsafe_b64encode(
    signing.dumps(token_data).encode()
).decode()

Security Features:

  • Signed with SECRET_KEY

  • Expires after 72 hours

  • One-time use (deleted after verification)

  • Tamper-proof (signature validation)

Social OAuth Security

Google OAuth Flow:

  1. Frontend receives ID token from Google

  2. Backend verifies token with Google’s API

  3. Validates token signature and expiration

  4. Extracts verified email

  5. Creates/links user account

  6. Issues JWT tokens

Security Measures:

  • Server-side token verification

  • No client secrets exposed

  • Email auto-verified (trusted provider)

  • Profile picture securely stored

Implementation:

from google.oauth2 import id_token
from google.auth.transport import requests

# Verify token with Google
idinfo = id_token.verify_oauth2_token(
    token, 
    requests.Request(), 
    settings.GOOGLE_CLIENT_ID
)

# Extract verified data
email = idinfo['email']
email_verified = idinfo['email_verified']

Authorization & Access Control

Permission Levels

Level

Description

Access

Anonymous

Unauthenticated users

Public products, categories

Authenticated

Logged-in users

Cart, wishlist, orders, profile

Staff

Admin users

Admin panel, all data

Superuser

System admins

Full system access

Endpoint Protection

Public Endpoints:

@permission_classes([AllowAny])
def product_list(request):
    # Anyone can view products

Authenticated Endpoints:

@permission_classes([IsAuthenticated])
def cart_view(request):
    # Only logged-in users

Owner-Only Access:

def order_detail(request, order_id):
    order = Order.objects.get(id=order_id)
    if order.user != request.user:
        return Response(status=403)  # Forbidden

CORS Configuration

Allowed Origins:

CORS_ALLOWED_ORIGINS = [
    'https://modestwear-app.vercel.app',  # Production frontend
    'https://modestwear.onrender.com',    # Backend
]

Security Headers:

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_HEADERS = [
    'authorization',
    'content-type',
    'x-csrftoken',
]

CSRF Protection

Token-Based:

CSRF_COOKIE_SECURE = True  # HTTPS only
CSRF_COOKIE_HTTPONLY = True  # No JavaScript access
CSRF_COOKIE_SAMESITE = 'Strict'

Trusted Origins:

CSRF_TRUSTED_ORIGINS = [
    'https://modestwear-app.vercel.app',
    'https://modestwear.onrender.com',
]

Data Protection

Encryption in Transit

HTTPS/TLS:

  • All traffic encrypted (TLS 1.2+)

  • Automatic SSL certificates (Render/Vercel)

  • HSTS enabled (force HTTPS)

Django Settings:

SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000  # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

Encryption at Rest

Database:

  • Neon PostgreSQL: Encrypted at rest (AES-256)

  • Automatic backups encrypted

Media Files:

  • Cloudinary: Encrypted storage

  • Secure URLs with signed tokens

Sensitive Data:

# Passwords
password = make_password(raw_password)  # PBKDF2-SHA256

# API Keys
SECRET_KEY = os.getenv('SECRET_KEY')  # Never in code

PII Protection

Personal Identifiable Information:

  • Email addresses

  • Phone numbers

  • Delivery addresses

  • Payment information

Protection Measures:

  1. Access Control: Only owner can view

  2. Logging: PII never logged

  3. Encryption: Encrypted in transit and at rest

  4. Retention: Deleted on account deletion

Example:

# Never log PII
logger.info(f'User logged in: {user.id}')  # ✓ User ID only
logger.info(f'User logged in: {user.email}')  # ✗ Email exposed

Input Validation & Sanitization

Request Validation

Django REST Framework Serializers:

class ProductSerializer(serializers.ModelSerializer):
    name = serializers.CharField(max_length=255)
    price = serializers.DecimalField(max_digits=6, decimal_places=2, min_value=0)
    
    def validate_price(self, value):
        if value < 0:
            raise serializers.ValidationError("Price cannot be negative")
        return value

SQL Injection Prevention

Django ORM:

  • Parameterized queries (automatic)

  • No raw SQL without parameters

  • Input sanitization

Safe:

Product.objects.filter(name=user_input)  # ✓ Parameterized

Unsafe:

Product.objects.raw(f"SELECT * FROM products WHERE name='{user_input}'")  # ✗ SQL injection

XSS Prevention

Output Escaping:

  • Django templates auto-escape HTML

  • API returns JSON (not HTML)

  • Content-Type headers enforced

Security Headers:

SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'

File Upload Security

Image Validation:

def validate_image(file):
    # Check file size
    if file.size > 5 * 1024 * 1024:  # 5MB
        raise ValidationError("File too large")
    
    # Check file type
    if not file.content_type.startswith('image/'):
        raise ValidationError("Invalid file type")
    
    # Verify image integrity
    try:
        Image.open(file)
    except:
        raise ValidationError("Corrupted image")

Cloudinary Security:

  • Automatic malware scanning

  • Format validation

  • Size limits enforced


Rate Limiting

Throttling Configuration

Anonymous Users:

class AnonRateThrottle(BaseThrottle):
    rate = '100/hour'  # 100 requests per hour

Authenticated Users:

class UserRateThrottle(BaseThrottle):
    rate = '1000/hour'  # 1000 requests per hour

Sensitive Endpoints:

class LoginRateThrottle(BaseThrottle):
    rate = '5/minute'  # 5 login attempts per minute

Implementation

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/hour',
        'user': '1000/hour',
        'login': '5/minute',
    }
}

Security Monitoring

Logging

Security Events Logged:

  • Failed login attempts

  • Account lockouts

  • Token blacklisting

  • Permission denials

  • Suspicious activity

Implementation:

import logging
logger = logging.getLogger(__name__)

# Failed login
logger.warning(f'Failed login: {email} from IP: {ip_address}')

# Account lockout
logger.warning(f'Account locked: {email} from IP: {ip_address}')

# Permission denied
logger.warning(f'Unauthorized access attempt: User {user.id} to Order {order.id}')

Audit Trail

Database Logging:

class AuditLog(models.Model):
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
    action = models.CharField(max_length=50)
    resource = models.CharField(max_length=100)
    ip_address = models.GenericIPAddressField()
    timestamp = models.DateTimeField(auto_now_add=True)

Tracked Actions:

  • User registration

  • Login/logout

  • Password changes

  • Order creation

  • Admin actions


Vulnerability Prevention

Common Vulnerabilities

Vulnerability

Prevention

SQL Injection

Django ORM parameterized queries

XSS

Output escaping, Content-Type headers

CSRF

CSRF tokens, SameSite cookies

Clickjacking

X-Frame-Options: DENY

MITM

HTTPS/TLS, HSTS

Brute Force

Account lockout, rate limiting

Session Hijacking

Secure cookies, token rotation

File Upload

Type validation, size limits, scanning

Security Headers

# Prevent clickjacking
X_FRAME_OPTIONS = 'DENY'

# Prevent MIME sniffing
SECURE_CONTENT_TYPE_NOSNIFF = True

# Enable XSS filter
SECURE_BROWSER_XSS_FILTER = True

# Force HTTPS
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000

Secrets Management

Environment Variables

Never in Code:

# ✗ Bad
SECRET_KEY = 'hardcoded-secret-key'

# ✓ Good
SECRET_KEY = os.getenv('SECRET_KEY')

Storage:

  • Development: .env file (gitignored)

  • Production: Render environment variables

  • Never committed to Git

Sensitive Data

Protected:

  • SECRET_KEY

  • DATABASE_URL

  • Email passwords

  • API keys (Cloudinary, Google, etc.)

  • JWT signing keys

Access Control:

  • Only admins can view

  • Encrypted in Render dashboard

  • Rotated regularly


Compliance & Best Practices

GDPR Compliance

User Rights:

  1. Right to Access: Users can download their data

  2. Right to Deletion: Account deletion removes all PII

  3. Right to Rectification: Users can update their data

  4. Data Portability: Export in JSON format

Implementation:

# Data export
def export_user_data(user):
    return {
        'profile': UserSerializer(user).data,
        'orders': OrderSerializer(user.orders.all(), many=True).data,
        'wishlist': WishlistSerializer(user.wishlist.all(), many=True).data,
    }

# Account deletion
def delete_account(user):
    user.orders.all().update(user=None)  # Anonymize orders
    user.delete()  # Delete user and related data

Security Best Practices

  1. Principle of Least Privilege: Users only access their own data

  2. Defense in Depth: Multiple security layers

  3. Secure by Default: Secure settings out of the box

  4. Regular Updates: Dependencies updated regularly

  5. Security Audits: Regular code reviews

  6. Incident Response: Plan for security breaches


Security Checklist

Development

  • No secrets in code

  • Input validation on all endpoints

  • Output escaping enabled

  • HTTPS in development (optional)

  • Security headers configured

Pre-Production

  • All dependencies updated

  • Security audit completed

  • Penetration testing done

  • Rate limiting configured

  • Logging enabled

Production

  • HTTPS enforced

  • HSTS enabled

  • Secrets in environment variables

  • Database encrypted

  • Backups configured

  • Monitoring enabled

  • Incident response plan ready


Incident Response

Security Breach Protocol

  1. Detect: Monitor logs for suspicious activity

  2. Contain: Disable affected accounts/services

  3. Investigate: Analyze logs, identify breach source

  4. Remediate: Patch vulnerability, rotate secrets

  5. Notify: Inform affected users (GDPR requirement)

  6. Review: Post-mortem, improve security

Contact

Security Issues: security@modestwear.com

Responsible Disclosure:

  • Report vulnerabilities privately

  • Allow 90 days for fix before public disclosure

  • Acknowledgment in security hall of fame


Next Steps