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)