Security Architecture¶
This document outlines the security architecture and best practices implemented in the NSGG Backend.
Authentication¶
JWT Authentication¶
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
}
Password Security¶
- Password hashing using Argon2
- Password validation rules
- Password reset flow
- Account lockout after failed attempts
Authorization¶
Role-Based Access Control (RBAC)¶
from rest_framework.permissions import BasePermission
class IsProductManager(BasePermission):
def has_permission(self, request, view):
return request.user.groups.filter(name='Product Managers').exists()
class ProductViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated, IsProductManager]
Object-Level Permissions¶
class OrderPermission(BasePermission):
def has_object_permission(self, request, view, obj):
return obj.user == request.user or request.user.is_staff
API Security¶
Rate Limiting¶
# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
Input Validation¶
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['name', 'price', 'description']
def validate_price(self, value):
if value <= 0:
raise serializers.ValidationError("Price must be greater than zero")
return value
Data Protection¶
Encryption at Rest¶
# models.py
from django_cryptography.fields import encrypt
class User(AbstractUser):
tax_number = encrypt(models.CharField(max_length=50))
phone = encrypt(models.CharField(max_length=15))
Secure File Storage¶
# settings.py
AWS_S3_OBJECT_PARAMETERS = {
'ServerSideEncryption': 'AES256',
}
AWS_S3_BUCKET_ACL = 'private'
AWS_DEFAULT_ACL = 'private'
Network Security¶
SSL/TLS Configuration¶
server {
listen 443 ssl http2;
server_name api.nsgg.com;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000" always;
}
CORS Configuration¶
# settings.py
CORS_ALLOWED_ORIGINS = [
"https://nsgg.com",
"https://admin.nsgg.com",
]
CORS_ALLOW_METHODS = [
'GET',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS'
]
Audit Logging¶
Request Logging¶
# middleware.py
class AuditLogMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
log_entry = {
'user': request.user.id if request.user.is_authenticated else None,
'path': request.path,
'method': request.method,
'ip': request.META.get('REMOTE_ADDR'),
'timestamp': timezone.now()
}
AuditLog.objects.create(**log_entry)
response = self.get_response(request)
return response
Data Change Tracking¶
from django_audit_fields import AuditModelMixin
class Order(AuditModelMixin, models.Model):
created_by = models.ForeignKey(User, on_delete=models.PROTECT)
modified_by = models.ForeignKey(User, on_delete=models.PROTECT)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
Security Headers¶
Django Security Middleware¶
# settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
# ...
]
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_HSTS_SECONDS = 31536000
SECURE_SSL_REDIRECT = True
Custom Security Headers¶
# middleware.py
class SecurityHeadersMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response['Content-Security-Policy'] = "default-src 'self'"
response['X-Frame-Options'] = 'DENY'
response['X-Content-Type-Options'] = 'nosniff'
return response
Payment Security¶
Stripe Integration¶
# services.py
import stripe
from django.conf import settings
stripe.api_key = settings.STRIPE_SECRET_KEY
def create_payment_intent(amount, currency='usd'):
try:
intent = stripe.PaymentIntent.create(
amount=amount,
currency=currency,
automatic_payment_methods={'enabled': True}
)
return intent
except stripe.error.StripeError as e:
log_stripe_error(e)
raise PaymentError("Payment processing failed")
Error Handling¶
Secure Error Responses¶
# views.py
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is not None:
response.data = {
'error': {
'code': response.status_code,
'message': response.data.get('detail', str(exc)),
}
}
return response
Security Testing¶
Automated Security Testing¶
# tests/test_security.py
class SecurityTests(TestCase):
def test_password_complexity(self):
weak_password = "password123"
with self.assertRaises(ValidationError):
validate_password(weak_password)
def test_sql_injection_prevention(self):
malicious_input = "'; DROP TABLE users; --"
response = self.client.get(f"/api/users/?search={malicious_input}")
self.assertEqual(response.status_code, 400)
Incident Response¶
Security Event Monitoring¶
# monitoring.py
from sentry_sdk import capture_message
def log_security_event(event_type, details):
capture_message(
f"Security Event: {event_type}",
level="error",
extra={
"event_type": event_type,
"details": details,
"timestamp": timezone.now()
}
)
Compliance¶
GDPR Compliance¶
# views.py
class UserDataExportView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
user_data = {
'personal_info': UserSerializer(request.user).data,
'orders': OrderSerializer(request.user.orders.all(), many=True).data,
'addresses': AddressSerializer(request.user.addresses.all(), many=True).data
}
return Response(user_data)
class UserDataDeletionView(APIView):
permission_classes = [IsAuthenticated]
def delete(self, request):
user = request.user
user.is_active = False
user.email = f"deleted_{user.id}@deleted.com"
user.save()
return Response(status=204)