# Security Guide This guide covers security best practices for Django MCP servers. ## Fundamental Principle: "Agent as User" Django MCP treats AI agents as authenticated users. Every MCP tool invocation: 1. **Authenticates** the agent (via bearer token, OAuth2, etc.) 2. **Binds** the agent to a Django User or AnonymousUser 3. **Enforces** all DRF permission checks 4. **Audits** the invocation **You cannot bypass DRF's permission system in Django MCP.** ## Authentication ### Default: No Authentication By default, Django MCP runs without authentication. Only use this for: - Local development - Private networks with network-level security - Testing and prototyping ### Production: OAuth2/OIDC For production, implement token-based authentication: #### 1. Install OAuth2 Toolkit ```bash uv add django-oauth-toolkit ``` #### 2. Configure Custom Authentication ```python # settings.py INSTALLED_APPS = [ ... 'oauth2_provider', 'rest_framework', ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', ], } ``` #### 3. Create Agent Clients ```bash python manage.py creatersakey python manage.py create_oauth2_client \ --name "claude-agent" \ --client-type confidential \ --grant-type client-credentials ``` #### 4. Agents Use Token ```bash curl -X POST https://your-api.com/o/token/ \ -d "client_id=YOUR_CLIENT_ID" \ -d "client_secret=YOUR_CLIENT_SECRET" \ -d "grant_type=client_credentials" # Returns: {"access_token": "...", "token_type": "Bearer"} ``` #### 5. MCP Server Validates Token ```python # mcp_server.py from drf_mcp.auth import MCPTokenAuthentication # Token validation happens automatically in ViewInvoker # when DRF permission checks run ``` ### OIDC / OpenID Connect For federated identity (e.g., corporate SSO): ```bash uv add django-oidc-auth ``` ```python # settings.py REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'oidc_auth.authentication.BearerTokenAuthentication', ], } OIDC_RP_CLIENT_ID = "your-client-id" OIDC_RP_CLIENT_SECRET = "your-client-secret" OIDC_OP_AUTHORIZATION_ENDPOINT = "https://your-identity-provider.com/oauth/authorize" OIDC_OP_TOKEN_ENDPOINT = "https://your-identity-provider.com/oauth/token" OIDC_OP_USER_ENDPOINT = "https://your-identity-provider.com/oauth/userinfo" ``` ## Permission Classes Your DRF permission classes automatically apply to MCP tools: ```python from rest_framework.permissions import ( IsAuthenticated, IsAdminUser, BasePermission, ) class IsTeamMember(BasePermission): """Only team members can access""" def has_permission(self, request, view): return request.user.groups.filter(name='team').exists() class CustomerViewSet(viewsets.ModelViewSet): serializer_class = CustomerSerializer permission_classes = [IsAuthenticated, IsTeamMember] # Tools derived from this ViewSet enforce permissions ``` ## Row-Level Security (Object-Level Permissions) Restrict access to specific records: ```python from rest_framework.permissions import BasePermission class IsCustomerOwner(BasePermission): """Only access your own customer records""" def has_object_permission(self, request, view, obj): return obj.owner == request.user class CustomerViewSet(viewsets.ModelViewSet): serializer_class = CustomerSerializer permission_classes = [IsAuthenticated, IsCustomerOwner] def get_queryset(self): # Filter to user's customers return Customer.objects.filter(owner=self.request.user) # When MCP agent calls retrieve_customer(id=123), # has_object_permission() checks if customer.owner == agent's user ``` ## Scope-Based Access (Fine-Grained Permissions) Use OAuth2 scopes to grant agents specific permissions: ```python # settings.py OAUTH2_SCOPES = { 'customers:read': 'Read customer data', 'customers:write': 'Modify customer data', 'customers:delete': 'Delete customers', } # Grant agents different scopes: # - Marketing agent: customers:read # - Sales agent: customers:read + customers:write # - Admin agent: customers:* ``` ```python # views.py from rest_framework.decorators import action from rest_framework.permissions import BasePermission class HasCustomerReadScope(BasePermission): def has_permission(self, request, view): scopes = request.auth.scopes if request.auth else [] return 'customers:read' in scopes class CustomerViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated, HasCustomerReadScope] ``` ## Audit Logging Log all MCP tool invocations: ```python # settings.py LOGGING = { 'version': 1, 'handlers': { 'mcp_audit': { 'level': 'INFO', 'class': 'logging.FileHandler', 'filename': 'mcp_audit.log', }, }, 'loggers': { 'drf_mcp': { 'handlers': ['mcp_audit'], 'level': 'INFO', }, }, } ``` Audit trail includes: - Agent/User ID - Tool name - Arguments (redacted for sensitive data) - Timestamp - Result (success/denied/error) - IP address (if available) ## Transport Security ### Stdio (Local) Secure by default for local development. Claude Desktop runs on same machine. ### HTTP **Always use HTTPS in production:** ```python # settings.py SECURE_SSL_REDIRECT = True SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True ``` **CORS Configuration:** ```bash uv add django-cors-headers ``` ```python # settings.py INSTALLED_APPS = [ 'corsheaders', ... ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', ... ] CORS_ALLOWED_ORIGINS = [ "https://trusted-client.example.com", ] CORS_ALLOW_CREDENTIALS = True ``` ### Stdio over SSH For remote execution, tunnel via SSH: ```bash ssh user@remote-server \ -L 8000:localhost:8000 \ "cd /path && python mcp_server.py --transport http --host 127.0.0.1" ``` ## Data Protection ### Sensitive Fields Exclude sensitive data from serializer schemas: ```python class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id', 'name', 'email'] # Don't include 'password' # or use SerializerMethodField for computed data ``` ### Field-Level Redaction ```python from django_filters import BaseInFilter class RedactedCharFilter(BaseInFilter, filters.CharFilter): """Custom filter that redacts sensitive data""" pass class SensitiveDataViewSet(viewsets.ModelViewSet): filterset_fields = { 'ssn': [RedactedCharFilter()], # Hide SSN in logs } ``` ### Read-Only Fields Mark sensitive fields as read-only: ```python class CustomerSerializer(serializers.ModelSerializer): # Agents can read but not modify created_at = serializers.DateTimeField(read_only=True) last_modified_by = serializers.StringRelatedField(read_only=True) class Meta: model = Customer fields = ['id', 'name', 'email', 'created_at', 'last_modified_by'] ``` ## Rate Limiting Prevent abuse via rate limiting: ```bash uv add djangorestframework-simplejwt ``` ```python # settings.py REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle', ], 'DEFAULT_THROTTLE_RATES': { 'anon': '100/hour', 'user': '1000/hour', # Per-agent limit }, } ``` ## Secrets Management **Never commit secrets to git:** ```python # ✗ WRONG: PASSWORD = "hardcoded-secret" # ✓ CORRECT: import os from decouple import config PASSWORD = config('OAUTH2_SECRET', default=None) ``` ```bash # .env (add to .gitignore) OAUTH2_SECRET=your-secret-here OIDC_RP_CLIENT_SECRET=your-oidc-secret ``` ## Compliance ### GDPR Implement data deletion for agents: ```python # views.py class CustomerViewSet(viewsets.ModelViewSet): @action(detail=True, methods=['delete']) def gdpr_delete(self, request, pk=None): """Securely delete customer data""" customer = self.get_object() # Check permissions self.check_object_permissions(request, customer) # Delete with audit log customer.delete() return Response(status=status.HTTP_204_NO_CONTENT) ``` ### SOC 2 Maintain audit logs: ```bash uv add django-auditlog ``` ```python from auditlog.registry import auditlog class Customer(models.Model): ... auditlog.register(Customer) ``` ## Security Checklist - [ ] Agents authenticate before tool access - [ ] DRF permission classes enforced on all tools - [ ] HTTPS only in production - [ ] CORS properly configured - [ ] Sensitive fields marked read-only - [ ] Rate limiting enabled - [ ] Audit logging configured - [ ] OAuth2 scopes implemented - [ ] Secrets in environment variables - [ ] Regular security audits scheduled ## Next Steps - [Getting Started](./getting_started.md) - Implement basic auth - [Architecture](./architecture.md) - Understand permission flow - [API Reference](../api/) - Detailed security APIs