Skip to main content

Media Assets Security Architecture

This document specifies the storage classification, access control policies, and serving architecture for uploaded media assets across ScannerSky Django.


1. Threat Model & Classification

ScannerSky processes various categories of binary media. Because certain media contain sensitive personal identifiable information (PII) and biometric data, media assets are strictly divided into Protected (Staff-Only) and Public classifications:

ClassificationFile Path PatternDescription & Privacy ImpactAuthorization Requirement
Protected (High Risk)media/signatures/*
media/persons/*_signature.*
Handwritten/pad electronic signatures (biometric and legal signature PII).is_authenticated and (is_superuser or core.view_person_sensitive_media)
Protected (High Risk)media/id_cards/*
media/persons/*_id_front.*
media/persons/*_id_back.*
Rendered institutional smart ID cards containing full name, emergency contacts, blood type, address, and QR codes.is_authenticated and (is_superuser or core.view_person_sensitive_media)
Public (Low Risk)media/profiles/*
media/persons/*_profile.*
Student and employee portrait photos used across public gate scan kiosks and real-time presentation.Unrestricted / Public
Public (Low Risk)media/id_templates/*Blank graphical ID card background templates.Unrestricted / Public
Public (Low Risk)media/syllabi/*Exported course syllabi PDFs produced for MCP / downloadable links.Unrestricted / Public

:::note Static Assets vs Media Assets Application branding, UI icons, and favicons (/static/img/icon.png, /static/img/logofull.png) are true static assets packaged with the application codebase. They are served directly from /static/ via Nginx / MinIO static storage and are completely decoupled from the /media/ authorization pipeline. :::


2. Request Flow & Serving Mechanics

graph TD
Client[Client Request: /media/path] --> Route[scannersky/urls.py: protected_media_view]
Route --> Classifier[is_sensitive_media_path?]

Classifier -- Yes (Signatures / ID Cards) --> AuthCheck{is_authenticated & view_person_sensitive_media?}
AuthCheck -- No --> Deny[403 PermissionDenied]
AuthCheck -- Yes --> Streamer

Classifier -- No (Profiles / Templates / Syllabi) --> Streamer

subgraph Serving Tier
Streamer{USE_X_ACCEL_REDIRECT?}
Streamer -- True (Production) --> Nginx[X-Accel-Redirect Header to /protected_media/]
Streamer -- False (Dev/WSGI) --> StaticServe[django.views.static.serve with Private Cache-Control]
end

Development vs Production Offloading

  1. Development Mode: Served directly via Django's internal static server (django.views.static.serve) with Cache-Control: private, max-age=3600 set on protected files to prevent local disk re-reads while keeping test environments simple.
  2. Production Mode (USE_X_ACCEL_REDIRECT = True):
    • Django handles sub-millisecond authentication verification.
    • On authorization, Django returns an empty HttpResponse with the X-Accel-Redirect: /protected_media/... header.
    • Nginx fulfills the request directly at the kernel level (sendfile), consuming zero Python worker memory.

3. Nginx Reverse Proxy Configuration Reference

For production deployments, the corresponding Nginx virtual host configuration:

server {
server_name scannersky.example.com;

# Public gateway proxy to Django WSGI / Gunicorn / Uvicorn
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

# Internal protected media location (cannot be reached directly from external clients)
location /protected_media/ {
internal;
alias /var/www/scannersky/media/;
}
}

4. Future Roadmap & Enhancements

  • Signed Kiosk URLs: If profile photos or ID card previews are later gated behind strict authentication, short-lived HMAC signed tokens (/media/.../?token=...&expires=...) can be generated by /api/scan/ for gate display screens without persistent sessions.
  • Per-Object Permissions: Future expansion may limit signature inspection to users with explicit core.view_person or departmental scope.