Freedec: Sistema de Entrega y Canje Desatendido de Documentos Confidenciales
🇪🇸 Leer en Español (Guía Completa) | 🇬🇧 Read in English (Full Guide)
🇪🇸 Español: Guía de Freedec
Bienvenido a la documentación de Freedec, una aplicación Django diseñada para la entrega y canje desatendido de documentos confidenciales con datos personales, basada en el cotejo unívoco del Hash SHA-256 del archivo y verificación en tiempo real por OTP (One-Time Password).
🛡️ Principios de Diseño y Reglas de Oro
- CERO TRANSPORTE DEL BINARIO POR EL CLIENTE:
El usuario final NUNCA sube el archivo
.encpara identificarse o solicitar acceso. El localizador oficial y prueba de trámite es exclusivamente el hash SHA-256 del archivo original (cadena hexadecimal de 64 caracteres). - SIN CONTRASEÑAS HUMANAS (Zero Human-Readable Passwords): Ningún usuario ni administrador visualiza, ingresa o gestiona contraseñas en texto claro. El descifrado es 100% interno y desatendido por el propio código del backend en memoria RAM tras validar el OTP.
- BURN-AFTER-READ CON DESTRUCCIÓN SEGURA (Zeroization/Shredding O(1) RAM):
Al primer canje exitoso por un usuario autorizado, el archivo cifrado en disco se sobrescribe con bytes criptográficamente aleatorios en bloques de 64 KB (
os.urandom(CHUNK_SIZE)), forzando la sincronización en disco físico conflush()yos.fsync()antes de desvincularlo del sistema de archivos (os.remove()), y el registro pasa a estado consumido.
📑 Índice de Contenidos
- Flujo Criptográfico y Operativo
- Entorno de Pruebas y Sandbox de Staging
- Uso de la Interfaz Web (Web GUI)
- Gestión en Django Admin
- Endpoints de la API REST
- Comandos de Gestión CLI
- Configuración de Seguridad en settings.py
1. Flujo Criptográfico y Operativo
Alta del Documento (Admin):
- El administrador sube el documento original (PDF, LibreOffice u Office) y especifica la lista de correos autorizados.
- El sistema valida las cabeceras binarias (Magic Bytes) y calcula el hash SHA-256 mediante lectura en bloques de 64 KB (anti-DoS/OOM).
- Genera una Clave Maestra de Datos (DEK) única con
Fernet.generate_key(). - Cifra el contenido del archivo con la DEK y lo guarda en disco como
.enc. - Cifra la DEK internamente con la clave maestra del servidor
settings.FREEDEC_FERNET_KEYy la almacena enencrypted_dek. - El administrador entrega el Hash SHA-256 al usuario como prueba y localizador oficial.
Solicitud de Acceso:
- El usuario final accede a
/freedec/solicitar/e introduce el Hash SHA-256 y su correo electrónico (o mediante enlace con?hash=...). - Si el hash no existe o el correo no está autorizado, el backend ejecuta un cálculo simulado PBKDF2 para neutralizar ataques de temporización (Timing Attacks) y devuelve una respuesta neutra preventiva.
- Si el documento ya fue consumido (
is_consumed == True), envía un correo formal al solicitante notificando quién y cuándo lo retiró (consumed_by,consumed_at), registrandointento_post_consumoen la auditoría. - Si está activo y autorizado, genera un código OTP numérico de 6 dígitos (
secrets.choice), fija caducidad estricta a 15 minutos, envía el código por correo con enlace a/freedec/canjear/y auditaotp_enviado.
Canje y Destrucción Física (Burn-After-Read):
- El usuario introduce el código OTP en
/freedec/canjear/. - Se valida el código mediante comparación en tiempo constante (
secrets.compare_digest). Si falla 3 veces, el token se bloquea definitivamente (otp_invalido_bloqueado). - Al validar el OTP, el backend descifra la DEK en RAM usando
FREEDEC_FERNET_KEY, descifra los bytes del archivo original en memoria, ejecuta la sobrescritura física con bytes aleatorios (os.urandom) y borrado en disco, marca el documento como consumido y devuelve inmediatamente unFileResponsecon el archivo original en memoria.
2. Entorno de Pruebas y Sandbox de Staging (100% Desatendido)
Freedec incluye un comando de arranque integral para montar un entorno de pruebas completo en segundos sin intervención manual:
Inicialización Rápida
# 1. Clonar e instalar dependencias con Poetry
git clone https://github.com/jrubioh1/Freedec.git
cd Freedec
poetry install
# 2. Inicializar el sandbox de staging de forma 100% desatendida
poetry run python manage.py setup_staging --clean
El comando setup_staging realiza automáticamente:
- Genera el archivo de configuración
freedec.inidesdefreedec.ini.templatesi no existe. - Aplica todas las migraciones de base de datos desde cero.
- Crea el Administrador Maestro (
admin/admin123). - Importa la malla de Unidades Organizativas (UO) (
DIRECCION,RRHH,LEGAL,AUDITORIA) y usuarios departamentales con contraseñas predefinidas. - Genera y cifra documentos confidenciales de prueba con distintas políticas (
FIRST_ACCESSyALL_RECIPIENTS). - Imprime un panel resumen en consola con todas las URLs de acceso, credenciales, hashes SHA-256 y enlaces de 1-clic directos.
Iniciar el Servidor de Pruebas
poetry run python manage.py runserver 8000
Ejecutar Suite de Pruebas Automatizadas (Pytest)
poetry run pytest -v
Demostración Interactiva del Flujo
poetry run python scripts/demo_flow.py
3. Uso de la Interfaz Web (Web GUI)
- Solicitar Acceso (
/freedec/solicitar/):- Formulario limpio con dos campos: Hash SHA-256 y Correo Electrónico.
- Soporta parámetro URL directo:
/freedec/solicitar/?hash=<SHA256>. - Redirige a la vista de canje guardando los datos en sesión.
- Canjear OTP (
/freedec/canjear/):- Entrada del código OTP numérico de 6 dígitos.
- Entrega inmediata del archivo original vía descarga de navegador (
FileResponse). - Destrucción simultánea del archivo cifrado en disco.
4. Gestión en Django Admin
-
Panel
EncryptedDocumentAdmin:- Columnas:
original_filename,file_hash,burn_policy,consumption_status_badge,expiry_status_badge,audit_download_button,corporate_email_button,terminate_action_button. - Cero contraseñas: Ningún campo muestra ni almacena contraseñas humanas.
- Descripción editable del documento: Campo editable con valor corporativo por defecto, modificable tanto en el alta como en la edición y durante procesos de reactivación. Esta descripción se incorpora automáticamente en todas las notificaciones por correo electrónico (OTP, avisos de retirada y advertencias).
- Caducidad Automática (Por Defecto 2 Semanas): Todo asiento cuenta con una fecha de caducidad (
expires_at), inicializada por defecto a 14 días desde su registro. La columnaexpiry_status_badgemuestra de un vistazo el tiempo restante (⏳ 13d 22h restantes), alertas de expiración próxima (⚠️ 36h restantes) o estado expirado (⏱️ Caducado). - Plantilla de Correo Corporativo (📋 / 📨):
- Botón
📋 Copiar texto correoen el listado para copiar instantáneamente al portapapeles el texto oficial corporativo con el nombre del archivo, su descripción, el Hash SHA-256 y el enlace directo de acceso. - Panel en el detalle del documento con visor del texto preformateado, botón de copia y enlace directo para abrir el cliente de correo (
mailto:).
- Botón
- Selector de Política de Destrucción (
burn_policy):⚡ 1er Acceso(FIRST_ACCESS): Trituración y borrado físico inmediato tras el primer canje exitoso.👥 Todos(ALL_RECIPIENTS): Cada destinatario autorizado dispone de una descarga individual; el archivo se preserva en disco hasta que todos los destinatarios hayan canjeado su copia.
- Verificación Dinámica y Reactivación: Cálculo de SHA-256 en cliente con WebCrypto API; detecta si el documento ya existe y si hay destinatarios previos con canje pendiente, ofreciendo el botón
➕ Volver a añadir pendientes a la lista de correospara reactivarlo sin colisiones y permitiendo actualizar la descripción. - Inmutabilidad y No Repudio Legal (Prohibición de Borrado): Los asientos (
EncryptedDocument) y las pistas de auditoría (DocumentAccessLog) son estrictamente inmutables. El borrado está deshabilitado a nivel de interfaz (has_delete_permission = False, eliminación de la acción en lotedelete_selected) y bloqueado a nivel de modelo ORM (delete()arrojaPermissionDenied). - Finalización de Asiento y Trituración Segura (⏹️): En lugar de borrar asientos, el personal autorizado dispone del botón
⏹️ Finalizarque redirige a una pantalla de confirmación exigiendo un motivo obligatorio. Al confirmar, el sistema tritura físicamente el archivo en disco (shred_and_delete_file), marca el asiento como consumido (is_consumed=True) y asienta la vicisitudfinalizacion_manualen el log de auditoría con el motivo y usuario responsable. - Descarga de Auditoría Administrativa: Botón y acción que permite al personal staff con permiso
can_audit_downloaddescargar una copia descifrada para fines legales o de auditoría mientras el documento no haya sido consumido, sin destruir el archivo ni marcarlo como consumido, registrandoadmin_descarga_preservada. - Control de Acceso y Permisos Estrictos de Django:
- Toda la autorización de acciones recae estrictamente en el sistema de permisos de Django (
django.contrib.auth.models.Permission). - La condición de
is_staffsolo autoriza el acceso al panel/admin/; no confiere privilegios implícitos ni bypasses sobre los modelos. - El botón de finalización (
terminate_action_button) y el botón de descarga administrativa (audit_download_button) se filtran dinámicamente según los permisos del usuario (freedec.change_encrypteddocument,freedec.can_audit_download). - Inmutabilidad estricta de auditoría: los registros de acceso (
DocumentAccessLog) no admiten creación, modificación ni eliminación desde el admin (has_add_permission=False,has_change_permission=False,has_delete_permission=False).
- Toda la autorización de acciones recae estrictamente en el sistema de permisos de Django (
- Malla de Unidades Organizativas (UO Emisora y Receptora):
- Los documentos se asocian a una
UO Emisoray opcionalmente a unaUO Receptora (Malla). - Aislamiento estricto de datos: usuarios no-superusuarios solo pueden listar y acceder a los documentos vinculados a sus UOs activas.
- Atajos inteligentes: botones para autocompletar la lista de destinatarios autorizados con el buzón corporativo de la UO o todos sus miembros activos.
- Los documentos se asocian a una
- Inline de auditoría legal de solo lectura (
DocumentAccessLogInline).
- Columnas:
-
Panel
OrganizationalUnitAdmine Importación Masiva:- Gestión de Unidades Organizativas con código numérico único, alias, correo de buzón y miembros asignados en línea.
- Módulo interactivo de Carga Masiva JSON (
/admin/freedec/organizationalunit/import-batch/) reservado para el Rol Maestro (is_superuser) para aprovisionar simultáneamente UOs y usuarios con feedback en tiempo real.
5. Endpoints de la API REST
1. Solicitud de Código OTP
curl -X POST http://127.0.0.1:8000/freedec/api/public/request-access/ \
-H "Content-Type: application/json" \
-d '{
"file_hash": "4c7ded034bb71b3802a0f17dd37226ff26217f37388d1bc50d3a6bf774091497",
"email": "destinatario@seguro.gob.es"
}'
Respuesta:
{
"status": "processed",
"message": "Si los datos indicados corresponden a un documento activo y una dirección de correo autorizada, recibirá en breves momentos un código de verificación (OTP) en su buzón."
}
2. Canje y Descarga con OTP
curl -X POST http://127.0.0.1:8000/freedec/api/public/consume/ \
-H "Content-Type: application/json" \
-d '{
"file_hash": "4c7ded034bb71b3802a0f17dd37226ff26217f37388d1bc50d3a6bf774091497",
"email": "destinatario@seguro.gob.es",
"otp_code": "455653"
}' \
--output documento_descifrado.pdf
6. Comandos de Gestión CLI
# Inicializar entorno staging desatendido
poetry run python manage.py setup_staging --clean
# Carga masiva desatendida de UOs y Usuarios desde archivo JSON
poetry run python manage.py load_batch examples/batch_import_example.json
# Cierre y trituración de asientos caducados (Batch periódico para asientos no canjeados)
poetry run python manage.py close_expired_documents --dry-run
poetry run python manage.py close_expired_documents
# Purga y optimización de base de datos (elimina tokens OTP caducados o usados)
poetry run python manage.py purge_expired_tokens --days=7 --dry-run
poetry run python manage.py purge_expired_tokens
# Listar todos los documentos registrados o finalizar asientos administrativamente
poetry run python manage.py delete_document --list
poetry run python manage.py delete_document <hash_o_archivo>
# Descifrado por OTP mediante CLI (consumo y destrucción Burn-After-Read)
poetry run python manage.py decrypt_document <hash_o_archivo> --otp 123456 --email usuario@empresa.com
# Descarga de auditoría administrativa preservada (Audit Bypass)
poetry run python manage.py decrypt_document <hash_o_archivo> --admin --output copia_auditoria.pdf
7. Formato JSON para Cargas Masivas de UOs y Usuarios
Freedec permite aprovisionar Unidades Organizativas (UO) y Usuarios de forma simultánea tanto desde el panel de administración (/admin/freedec/organizationalunit/import-batch/) como por línea de comandos con load_batch.
El archivo JSON debe seguir la estructura documentada en examples/batch_import_example.json:
{
"organizational_units": [
{
"numeric_code": 1001,
"alias": "DIRECCION",
"name": "Dirección General y Presidencia",
"email": "direccion@freedec.local",
"description": "Unidad de gobierno corporativo.",
"is_active": true
},
{
"numeric_code": 1002,
"alias": "RRHH",
"name": "Recursos Humanos y Relaciones Laborales",
"email": "rrhh@freedec.local",
"description": "Gestión de nóminas y personal.",
"is_active": true
}
],
"users": [
{
"username": "admin_dir",
"email": "admin_dir@freedec.local",
"first_name": "Elena",
"last_name": "Vázquez",
"password": "PasswordDir#2026",
"uo_alias": "DIRECCION",
"role": "ADMIN",
"is_staff": true
},
{
"username": "gestor_rrhh",
"email": "gestor_rrhh@freedec.local",
"first_name": "Carlos",
"last_name": "Mendoza",
"password": "PasswordRRHH#2026",
"uo_alias": "RRHH",
"role": "OPERATOR",
"is_staff": true
}
]
}
Campos admitidos:
organizational_units:numeric_code(int, obligatorio): Código numérico único identificativo (ej.1001).alias(str, obligatorio): Siglas o identificador en mayúsculas (ej.RRHH).name(str, obligatorio): Denominación oficial completa.email(str, obligatorio): Buzón corporativo oficial de la unidad.description(str, opcional): Descripción funcional de la UO.is_active(bool, opcional, por defectotrue).
users:username(str, obligatorio): Nombre de usuario de acceso.email(str, obligatorio): Correo electrónico seguro del usuario.password(str, opcional): Contraseña inicial (si se omite, se autogenera una segura).uo_aliasouo_numeric_code(obligatorio): UO a la que se vincula el usuario.role(str, obligatorio): Uno deADMIN(gestión de UO),OPERATOR(emisión/recepción) oAUDITOR(solo lectura y auditoría).is_staff(bool, opcional, por defectotrue): Acceso al portal administrativo.
8. Configuración por Archivo INI (freedec.ini) y ConfigParser
A partir de la versión 2.0.0, Freedec centraliza su configuración en un archivo estándar .ini gestionado mediante el módulo nativo de Python configparser, eliminando la dependencia dispersa de variables de entorno sueltas.
Se proporciona una plantilla completa en freedec.ini.template. Copie este archivo a freedec.ini para personalizarlo:
[django]
secret_key = clave_secreta_django
debug = true
allowed_hosts = *
[security]
# Clave maestra Fernet del servidor (32 bytes url-safe base64) para cifrar las DEK
fernet_key = TU_CLAVE_FERNET_BASE64_URL_SAFE_DE_32_BYTES=
max_file_size = 52428800
timing_pbkdf2_iterations = 20000
[email]
# Backend de correo: estándar corporativo SMTP (por defecto, no consola)
backend = django.core.mail.backends.smtp.EmailBackend
host = localhost
port = 25
use_tls = false
use_ssl = false
host_user =
host_password =
default_from_email = no-reply@freedec.local
timeout = 5
async_email = true
email_max_workers = 8
[throttling]
web_request_limit_per_minute = 15
api_access_request_rate = 15/minute
api_access_consume_rate = 30/minute
Validación en arranque:
FreedecConfig.ready()detiene el inicio de Django conImproperlyConfiguredsifernet_keyno es válida o está ausente. En producción, el backend SMTP garantiza la entrega real de las contraseñas temporales OTP.
8.1. Jerarquía de Búsqueda Automática del .ini (Linux y Windows)
Tanto si Freedec se ejecuta de forma autónoma (standalone) como si se usa integrada a través de las funciones de abstracción, el sistema resuelve la ruta del .ini mediante resolve_freedec_ini_path siguiendo este orden de prelación:
- Ruta explícita indicada en la función de abstracción:
- Vía parámetro
ini_fileeninject_freedec_settings(globals(), ini_file="...")oget_freedec_defaults(ini_file="..."). - Vía variable de entorno
FREEDEC_INI_PATH. - Nota: Si la ruta indicada es un directorio, buscará automáticamente
freedec.inien su interior.
- Vía parámetro
- En el raíz del sistema de archivos, en una carpeta que ponga
freedec:- Linux / Unix:
/freedec/freedec.ini(y/etc/freedec/freedec.ini). - Windows:
C:\freedec\freedec.iniy<SystemDrive>\freedec\freedec.ini. - Multiplataforma unificada:
Path(root.anchor) / "freedec" / "freedec.ini".
- Linux / Unix:
- En el directorio raíz del proyecto (
BASE_DIR):BASE_DIR / "freedec" / "freedec.ini"(en la carpetafreedecdel proyecto).BASE_DIR / "freedec.ini"(directamente en la raíz del proyecto).
- En el directorio actual de trabajo (
cwd):./freedec/freedec.inio./freedec.ini.
- Plantillas fallback:
freedec.ini.templateen la raíz del proyecto o integrada en el paquete Freedec.
9. Acoplamiento e Integración en Proyectos Django Existentes
Freedec está concebido arquitectónicamente como una Django Reusable App lista para acoplarse directamente a cualquier proyecto Django preexistente (monolito corporativo o backend modular) sin fricciones ni conflictos con configuraciones preexistentes.
A partir de la versión 2.0.0, Freedec incluye el módulo freedec.conf con asistentes nativos diseñados específicamente para resolver la configuración en una sola línea.
9.1. Instalación del Paquete
# Vía Poetry
poetry add git+https://github.com/jrubioh1/Freedec.git
# O vía pip
pip install git+https://github.com/jrubioh1/Freedec.git
9.2. Configuración en settings.py del Proyecto Anfitrión
Se ofrecen dos alternativas equivalentes:
🟢 Método A: Asistente Automático de 1 Línea (Recomendado)
Añada al final del archivo settings.py de su proyecto Django anfitrión:
from freedec import inject_freedec_settings
# Inyecta y fusiona de forma segura toda la configuración necesaria
inject_freedec_settings(globals())
El asistente inject_freedec_settings(globals()):
- Detección inteligente de configuración: Resuelve automáticamente
freedec.inibuscando en el raíz del sistema (/freedec/freedec.inien Linux oC:\freedec\freedec.inien Windows), en la carpetafreedecdel proyecto (BASE_DIR / "freedec"), en la raíz del proyecto (BASE_DIR / "freedec.ini"), o la ruta/carpeta personalizada pasada enini_file. INSTALLED_APPS: Registrarest_frameworkyfreedec.apps.FreedecConfigsin duplicar elementos existentes.MIDDLEWARE: Detecta y añadedjango.middleware.locale.LocaleMiddlewareinmediatamente después deSessionMiddlewarepara habilitar el soporte multi-idioma (Español / Inglés).REST_FRAMEWORK(Mezcla No Destructiva): Fusiona las tasas de throttling (access_requestyaccess_consume) y throttles anónimos enDEFAULT_THROTTLE_RATESsin sobreescribir ni romper las clases de autenticación (JWT, Token, Session) o permisos configuradas por el anfitrión.- Caché y Almacenamiento: Garantiza un backend de caché (
LocMemCache) y rutas deMEDIA_URL/MEDIA_ROOTseguras si no estaban previamente definidas. - Correo SMTP Asíncrono: Configura el backend estándar SMTP corporativo y el pool de workers en segundo plano.
Sobreescritura dinámica de parámetros: Puede sobreescribir cualquier clave pasando argumentos con nombre a
inject_freedec_settings:inject_freedec_settings( globals(), ini_file="config/entorno_produccion.ini", FREEDEC_MAX_FILE_SIZE=25 * 1024 * 1024, # Límite a 25 MB FREEDEC_EMAIL_MAX_WORKERS=16, )
🛠️ Método B: Configuración Manual y Granular
Si su proyecto utiliza múltiples ficheros de configuración modular (base.py, production.py) o desea declarar explícitamente cada directiva:
from pathlib import Path
# 1. Aplicaciones instaladas
if "rest_framework" not in INSTALLED_APPS:
INSTALLED_APPS.append("rest_framework")
if "freedec.apps.FreedecConfig" not in INSTALLED_APPS:
INSTALLED_APPS.append("freedec.apps.FreedecConfig")
# 2. Middleware multi-idioma
if "django.middleware.locale.LocaleMiddleware" not in MIDDLEWARE:
MIDDLEWARE.insert(
MIDDLEWARE.index("django.contrib.sessions.middleware.SessionMiddleware") + 1,
"django.middleware.locale.LocaleMiddleware"
)
# 3. Seguridad criptográfica de Freedec
FREEDEC_FERNET_KEY = "TU_CLAVE_FERNET_BASE64_URL_SAFE_DE_32_BYTES="
FREEDEC_MAX_FILE_SIZE = 50 * 1024 * 1024 # 50 MB
FREEDEC_TIMING_PBKDF2_ITERATIONS = 20000
# 4. Correo electrónico corporativo (SMTP Estándar)
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.tu-entorno.es"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = "notificaciones@tu-entorno.es"
EMAIL_TIMEOUT = 5
FREEDEC_ASYNC_EMAIL = True
FREEDEC_EMAIL_MAX_WORKERS = 8
# 5. Throttling anti-ataques de fuerza bruta
REST_FRAMEWORK = {
**REST_FRAMEWORK, # Preserva autenticaciones previas
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
**REST_FRAMEWORK.get("DEFAULT_THROTTLE_RATES", {}),
"anon": "100/minute",
"access_request": "15/minute",
"access_consume": "30/minute",
},
}
FREEDEC_WEB_REQUEST_LIMIT_PER_MINUTE = 15
# 6. Almacenamiento y Caché
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
9.3. Enrutamiento en urls.py del Proyecto Anfitrión
Incorpore el espacio de nombres de Freedec en el urls.py principal de su proyecto:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("freedec/", include("freedec.urls")), # Rutas Web GUI y API REST
]
Rutas expuestas automáticamente:
- Web GUI:
/freedec/solicitar/: Formulario de solicitud de clave OTP por Hash SHA-256 y correo./freedec/canjear/: Canje de OTP de 6 dígitos y descarga en streaming.
- API REST:
/freedec/api/public/request-access/(POSTconfile_hashyemail)./freedec/api/public/consume/(POSTconfile_hash,emailyotp_code).
- Django Admin:
/admin/freedec/encrypteddocument/: Panel corporativo de documentos y auditoría./admin/freedec/organizationalunit/: Malla multi-tenant de UOs y panel de importación JSON.
9.4. Migraciones y Despliegue en el Proyecto Anfitrión
Ejecute las migraciones de Freedec en su base de datos anfitriona:
# Aplica la migración consolidada única 0001_initial
python manage.py migrate
# Opcional: Aprovisionar UOs y usuarios iniciales desde JSON
python manage.py load_batch ruta/a/tu_plantilla_uos.json
🇬🇧 English: Freedec Guide
Welcome to Freedec, a security-focused Django application for unattended delivery and redemption of confidential documents with personal data, based on strict SHA-256 file hash matching and real-time One-Time Password (OTP) verification.
🛡️ Design Principles & Golden Rules
- ZERO CLIENT BINARY TRANSPORT: The client NEVER uploads the
.encfile to identify themselves. The official locator is exclusively the SHA-256 hash (64 hex characters). - ZERO HUMAN-READABLE PASSWORDS: No plaintext passwords managed by users or admins. Decryption is 100% internal and unattended in RAM after validating the OTP.
- BURN-AFTER-READ WITH SECURE DESTRUCTION (Zeroization/Shredding): Upon the first successful redemption by an authorized user, the encrypted file on disk is overwritten with cryptographically random bytes (
os.urandom), flushed and synced to physical storage (flush()/os.fsync()), and deleted from the filesystem (os.remove()).
📑 Table of Contents
- Cryptographic Lifecycle
- Testing & Sandbox Setup
- Web GUI Usage
- Django Admin Management
- REST API Endpoints
- CLI Management Commands
- Security Configuration in settings.py
1. Cryptographic Lifecycle
- Upload (Admin): File validated by Magic Bytes, unique DEK generated with
Fernet.generate_key(), encrypted in storage as.enc, DEK encrypted withFREEDEC_FERNET_KEY. SHA-256 provided to recipient as sole locator. - Access Request: User submits SHA-256 and email at
/freedec/solicitar/. If unauthorized or non-existent, simulated PBKDF2 runs to prevent timing attacks. If consumed, email notice is dispatched andintento_post_consumologged. If active and authorized, 6-digit cryptographic OTP is sent with 15-minute strict expiration. - Redemption (Burn-After-Read): User enters 6-digit OTP at
/freedec/canjear/. Validated viasecrets.compare_digest(locked after 3 failed attempts). File decrypted in memory, physical file on disk shredded withos.urandom, and delivered asFileResponseattachment.
2. Testing & Sandbox Setup
git clone https://github.com/jrubioh1/Freedec.git
cd Freedec
poetry install
poetry run python manage.py migrate
poetry run python manage.py setup_staging
poetry run pytest -v
poetry run python scripts/demo_flow.py
3. Web GUI Usage
- Request Access (
/freedec/solicitar/): SHA-256 hash and email input. Supports?hash=...prefilling. - Redeem OTP (
/freedec/canjear/): 6-digit OTP input with immediate browser RAM streaming download and disk shredding.
4. Django Admin Management
-
EncryptedDocumentAdminDashboard:- Columns:
original_filename,file_hash,burn_policy,consumption_status_badge,expiry_status_badge,audit_download_button,corporate_email_button,terminate_action_button. - Zero Passwords: No field displays or stores human-readable passwords.
- Editable Document Description: Pre-filled default corporate description, editable upon upload, document modification, and during reactivation. Embedded automatically into all email notifications (OTP verification, redemption notices, post-consumption warnings).
- Automatic Expiration (Default 2 Weeks): Every entry includes an expiration date (
expires_at), initialized by default to 14 days from creation. Theexpiry_status_badgecolumn provides an at-a-glance badge of time remaining (⏳ 13d 22h remaining), warning alerts for pending expiration (⚠️ 36h remaining), or expired status (⏱️ Caducado). - Corporate Email Template (📋 / 📨):
- Changelist action button (
📋 Copiar texto correo) for one-click clipboard copying of the official notification text with filename, description, SHA-256 hash, and direct access link. - Document detail panel featuring a preformatted template viewer, copy button, and direct
mailto:link.
- Changelist action button (
- Destruction Policy Selector (
burn_policy):⚡ 1st Access(FIRST_ACCESS): Immediate physical shredding and removal upon the first successful redemption.👥 All Recipients(ALL_RECIPIENTS): Each authorized recipient receives an individual download; the file remains on disk until all recipients have redeemed their copy.
- Dynamic Client-Side Verification & Reactivation: Computes SHA-256 in the browser via WebCrypto API; detects if the document already exists and if prior recipients have pending access, providing a
➕ Add pending to recipient listbutton to reactivate without hash collisions while allowing description updates. - Strict Immutability & Legal Non-Repudiation (Deletion Disabled): Entries (
EncryptedDocument) and audit logs (DocumentAccessLog) are strictly immutable. Deletion is disabled in Django Admin (has_delete_permission = False, removal of batchdelete_selected) and forbidden at the ORM model level (delete()raisesPermissionDenied). - Entry Termination & Secure Physical Shredding (⏹️): Authorized staff can terminate active entries via the
⏹️ Finalizarbutton, which opens a confirmation page requiring a mandatory justification reason. Confirmation physically shreds the file (shred_and_delete_file), marks the document as consumed (is_consumed=True), and registers afinalizacion_manualvicisitud in the audit log preserving all historical evidence. - Administrative Audit Inspection: Action and button allowing staff users with explicit
freedec.can_audit_downloadpermission to download a decrypted copy for legal compliance while the document has not been consumed, without destroying the file or marking it as consumed, loggingadmin_descarga_preservada. - Strict Django Model Permissions:
- All authorization actions strictly rely on Django's model permission system (
django.contrib.auth.models.Permission). - Being
is_staffonly grants access to/admin/; it never bypasses model-level authorization. - Action buttons (
terminate_action_button,audit_download_button) are conditionally displayed based on active user permissions (freedec.change_encrypteddocument,freedec.can_audit_download). - Audit log immutability:
DocumentAccessLogrecords cannot be added, altered, or deleted manually from Django Admin (has_add_permission=False,has_change_permission=False,has_delete_permission=False).
- All authorization actions strictly rely on Django's model permission system (
- Organizational Unit Mesh (Origin & Target UO):
- Documents are linked to an
Origin UOand optionally aTarget UO (Mesh). - Strict multi-tenant isolation: non-superuser staff members can strictly view and operate documents associated with their active UO memberships.
- Interactive helpers: buttons to auto-populate authorized recipient lists with the target UO's corporate mailbox or all active departmental members.
- Documents are linked to an
- Read-only legal access logs inline (
DocumentAccessLogInline).
- Columns:
-
OrganizationalUnitAdmin& Bulk Provisioning:- Manage flat Organizational Units with unique numeric code, alias, corporate email, and member inlines.
- Interactive JSON Bulk Import dashboard (
/admin/freedec/organizationalunit/import-batch/) reserved for the Master Role (is_superuser) for simultaneous, idempotent provisioning of UOs and users.
5. REST API Endpoints
POST /freedec/api/public/request-access/(file_hash,email)POST /freedec/api/public/consume/(file_hash,email,otp_code)
6. CLI Management Commands
poetry run python manage.py load_batch config_provisioning.jsonpoetry run python manage.py close_expired_documents --dry-runpoetry run python manage.py close_expired_documentspoetry run python manage.py purge_expired_tokens --days=7 --dry-runpoetry run python manage.py purge_expired_tokenspoetry run python manage.py delete_document --listpoetry run python manage.py delete_document <hash_or_filename>poetry run python manage.py decrypt_document <hash> --otp 123456 --email user@corp.compoetry run python manage.py decrypt_document <hash> --admin --output audit.pdf
7. Security Configuration in settings.py
# Mandatory Fernet master key (32 bytes base64 url-safe)
# Generated via cryptography.fernet.Fernet.generate_key().decode()
FREEDEC_FERNET_KEY = "YOUR_FERNET_KEY_BASE64_URL_SAFE_32_BYTES="
# Maximum allowed file upload size (default 50 MB)
FREEDEC_MAX_FILE_SIZE = 50 * 1024 * 1024
Startup validation:
FreedecConfig.ready()halts Django startup withImproperlyConfiguredifFREEDEC_FERNET_KEYis missing or invalid.
8. Integration into Host Django Projects
Freedec is designed as a modular, pluggable Django Reusable App. It can be integrated into any existing Django codebase with minimal effort.
Starting with v2.0.0, Freedec includes freedec.conf providing helper functions to inject settings into any host project automatically.
Method A: One-Line Automatic Injection (Recommended)
Add to the end of your host settings.py:
from freedec import inject_freedec_settings
# Automatically merges apps, middleware, throttles, SMTP, caches, and storage
inject_freedec_settings(globals())
What inject_freedec_settings(globals()) does:
- Discovers and loads
freedec.iniby hierarchical search: filesystem root (/freedec/freedec.inion Linux,C:\freedec\freedec.inion Windows), projectfreedec/subfolder, project root, or custom path passed toini_file. - Appends
rest_frameworkandfreedec.apps.FreedecConfigtoINSTALLED_APPSwithout duplicates. - Inserts
django.middleware.locale.LocaleMiddlewareimmediately afterSessionMiddleware. - Merges throttling rates (
access_request,access_consume) intoREST_FRAMEWORKwithout altering existing authentication or permission classes. - Ensures a valid
CACHESbackend (LocMemCache) andMEDIA_ROOT/MEDIA_URLpaths. - Configures corporate SMTP email settings and asynchronous worker pools.
Method B: Manual Granular Configuration
For partitioned settings architectures (base.py, production.py):
INSTALLED_APPS += [
"rest_framework",
"freedec.apps.FreedecConfig",
]
MIDDLEWARE += [
"django.middleware.locale.LocaleMiddleware",
]
FREEDEC_FERNET_KEY = "YOUR_FERNET_KEY_BASE64_URL_SAFE_32_BYTES="
FREEDEC_MAX_FILE_SIZE = 50 * 1024 * 1024
FREEDEC_TIMING_PBKDF2_ITERATIONS = 20000
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.corp.local"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = "no-reply@corp.local"
FREEDEC_ASYNC_EMAIL = True
FREEDEC_EMAIL_MAX_WORKERS = 8
REST_FRAMEWORK = {
**REST_FRAMEWORK,
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
**REST_FRAMEWORK.get("DEFAULT_THROTTLE_RATES", {}),
"anon": "100/minute",
"access_request": "15/minute",
"access_consume": "30/minute",
},
}
URL Configuration
Include Freedec URLs in your host urls.py:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("freedec/", include("freedec.urls")),
]
Apply Migrations
python manage.py migrate
Metadata
Release files for freedec 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| freedec-2.0.0.tar.gz | 147.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| freedec-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 308.9 kB
Release files / freedec-2.0.0.tar.gz
| Download URL | freedec-2.0.0.tar.gz |
|---|---|
| Size | 147.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7e46cc5cb1c40dd5240e88b9a0cff6fc83e5e5a5e53146abca8f26eaf824970b
|
|
BLAKE2b-256 checksum How to use checksums |
2151f98b37657d7d54bfc10a83ac7aa584ed3eeb8868c27d54339cf0b1d23fc9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.5.1 CPython/3.14.9 Linux/7.2.8-gentoo-dist-hardened
|
Release files / freedec-2.0.0-py3-none-any.whl
| Download URL | freedec-2.0.0-py3-none-any.whl |
|---|---|
| Size | 161.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9f7d011f7a5d3268d2146ed57bab844bfd004c57c4882610a8e57fef1de34ad7
|
|
BLAKE2b-256 checksum How to use checksums |
84e377c786401cd305ff48807e133175c3452f25fcb966ceeaf898a6c983e2c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.5.1 CPython/3.14.9 Linux/7.2.8-gentoo-dist-hardened
|
Security for freedec 2.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.