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:
Short-Lived Access Tokens
Lifetime: 15 minutes
Reduces exposure window
Forces regular refresh
Token Rotation
New refresh token on each refresh
Old token immediately blacklisted
Prevents token reuse attacks
Token Blacklisting
Logout blacklists refresh token
Stored in Redis + database
Checked on every token use
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)
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:
Access Control: Only owner can view
Logging: PII never logged
Encryption: Encrypted in transit and at rest
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:
.envfile (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:
Right to Access: Users can download their data
Right to Deletion: Account deletion removes all PII
Right to Rectification: Users can update their data
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
Principle of Least Privilege: Users only access their own data
Defense in Depth: Multiple security layers
Secure by Default: Secure settings out of the box
Regular Updates: Dependencies updated regularly
Security Audits: Regular code reviews
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
Detect: Monitor logs for suspicious activity
Contain: Disable affected accounts/services
Investigate: Analyze logs, identify breach source
Remediate: Patch vulnerability, rotate secrets
Notify: Inform affected users (GDPR requirement)
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
Review Deployment Architecture
Explore Database Design
Read API Documentation
Social OAuth Security
Google OAuth Flow:
Frontend receives ID token from Google
Backend verifies token with Google’s API
Validates token signature and expiration
Extracts verified email
Creates/links user account
Issues JWT tokens
Security Measures:
Server-side token verification
No client secrets exposed
Email auto-verified (trusted provider)
Profile picture securely stored
Implementation: