Endurecimiento de contraseñas con VOPRF para Django: una base de datos robada no se puede crackear offline.
Project description
quipu-oprf-django
Endurecimiento de contraseñas con VOPRF para Django. Una línea en PASSWORD_HASHERS
y tu tabla de usuarios deja de ser crackeable offline.
Argon2 solo: te roban la BD -> fuerza bruta offline, a la velocidad de su GPU.
Con OPRF: te roban la BD -> no pueden derivar nada. Cada intento exige una
petición a tu servidor: la ves, la limitas y la cortas.
El servidor nunca ve la contraseña (viaja cegada) y no puede mentir: cada respuesta trae una prueba DLEQ que el cliente verifica contra una clave pública que fijas tú.
Estado: beta. El servicio está pre-auditoría. Ver "Antes de producción".
Instalación
pip install quipu-oprf-django
# settings.py
PASSWORD_HASHERS = [
"quipu_oprf_django.hashers.OprfArgon2PasswordHasher", # preferido
"django.contrib.auth.hashers.Argon2PasswordHasher", # para migrar (ver abajo)
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
]
QUIPU_OPRF = {
"BASE_URL": os.environ["QUIPU_OPRF_URL"], # https://oprf.xiliux.com
"API_KEY": os.environ["QUIPU_OPRF_API_KEY"], # quipu_live_...
"PUBLIC_KEY": os.environ["QUIPU_OPRF_PUBKEY"], # 64 hex — ver "Fijar la clave"
"TIMEOUT": 5.0,
}
Nada más. User.set_password() y authenticate() siguen funcionando igual.
Migrar usuarios existentes
No hace falta script. Deja Argon2PasswordHasher (o el que uses) después del
nuestro en la lista: Django verifica cada hash antiguo con su hasher original y lo
re-codifica con el preferido en el siguiente login correcto. Los usuarios migran
solos según entran.
Fijar la clave pública
PUBLIC_KEY debe venir fuera de banda — del README de tu proveedor, de tu gestor
de secretos, de donde sea menos del propio servidor.
Pedírsela a /v1/public-key anula la garantía: la prueba DLEQ demuestra que el
servidor usó la clave correspondiente a la clave pública que tú fijaste. Si el
servidor también elige contra qué se le verifica, un servidor comprometido puede
responder lo que quiera. Por eso este paquete exige PUBLIC_KEY y no la descarga.
Instancia beta:
BASE_URL https://oprf.xiliux.com
PUBLIC_KEY 88b623ccc00c9eac39f8e02a0f41851535dc2e196ed9836cad00f77644af214b
La clave cambió el 2026-07-17 (antes
f84ef413…): la instancia migró a RFC 9497 y deriva la clave conDeriveKeyPair. Si fijaste la anterior, tuPUBLIC_KEYya no valida y verásOprfRejected— que es exactamente lo que debe pasar cuando la clave fijada no cuadra. Actualízala.
Falla cerrado, a propósito
Si el servicio no responde, el login falla; no se degrada a Argon2 pelado.
No es rigidez: un hash sin endurecer no casaría con el guardado, así que degradar
sería además de inseguro, incorrecto. Y verify() levanta en vez de devolver
False, porque una caída no es una contraseña incorrecta — decir "clave errónea"
durante un incidente de red manda al usuario a resetear su contraseña por nada.
| Excepción | Qué pasó | Qué hacer |
|---|---|---|
OprfUnavailable |
red, timeout, 5xx, o API key rechazada | reintentar; mirar el servicio |
OprfRejected |
la prueba DLEQ no valida contra tu clave fijada | investigar: no lo produjo esa clave |
OprfRejected nunca es un fallo transitorio. Significa servidor comprometido,
suplantado, o PUBLIC_KEY mal configurada. No lo reintentes a ciegas.
Antes de producción
- Cada login paga un viaje de red. Ajusta
TIMEOUTy ten en cuenta la latencia hasta tu servidor OPRF. - El servicio es un punto único de fallo (R2 del modelo de amenaza): si cae, nadie entra. Planifica alta disponibilidad.
- La semilla del servidor es crítica. Si se pierde, ningún usuario vuelve a entrar: los hashes guardados no se pueden reproducir. Respáldala.
- Servicio en beta, pre-auditoría externa.
Licencia
Sin decidir, y hay que resolverlo antes de publicar.
El núcleo de Quipu es AGPL-3.0-or-later. Este plugin importa quipu, así que hoy la
AGPL se propagaría a cualquier SaaS que lo instale — y ninguna empresa mete AGPL en
sus PASSWORD_HASHERS. O sea: la licencia bloquearía la rampa de entrada al servicio
de pago, que es exactamente lo que este paquete existe para abrir.
El patrón habitual de open-core es núcleo copyleft + SDK de cliente permisivo (Apache-2.0): MongoDB con SSPL y drivers Apache, Elastic, Redis. El titular único del copyright puede relicenciar el subconjunto del cliente VOPRF sin tocar el núcleo.
Pruebas
pip install -e ".[dev]" argon2-cffi
pytest
Las pruebas usan un doble de quipu que simula el contrato del VOPRF: aquí se
verifica el cableado del plugin (clasificación de errores, fallo cerrado, migración),
no la criptografía. Esa vive en Rust — src/voprf.rs,
crates/quipu-oprf-server/tests/e2e.rs — y los 4 clientes de referencia se contrastan
entre sí en scripts/oprf-e2e.sh.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file quipu_oprf_django-0.1.0.tar.gz.
File metadata
- Download URL: quipu_oprf_django-0.1.0.tar.gz
- Upload date:
- Size: 13.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d419b2f6b4e24818c0c6ada102328de5e70510bfe3ac760f82eb1297168ba9d1
|
|
| MD5 |
0e2af032e6f61afba5b5d0cab01fd4d3
|
|
| BLAKE2b-256 |
c0fcc3cf92a05ddcc26a5cc05e83fedd1aa2e61b091dad5d33fb1dec6feeb5ab
|
Provenance
The following attestation bundles were made for quipu_oprf_django-0.1.0.tar.gz:
Publisher:
release.yml on isazajuancarlos/quipu
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
quipu_oprf_django-0.1.0.tar.gz -
Subject digest:
d419b2f6b4e24818c0c6ada102328de5e70510bfe3ac760f82eb1297168ba9d1 - Sigstore transparency entry: 2188383150
- Sigstore integration time:
-
Permalink:
isazajuancarlos/quipu@637aca8cfdd07c83df8ceba946f78a751ad18335 -
Branch / Tag:
refs/tags/django-v0.1.0 - Owner: https://github.com/isazajuancarlos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@637aca8cfdd07c83df8ceba946f78a751ad18335 -
Trigger Event:
push
-
Statement type:
File details
Details for the file quipu_oprf_django-0.1.0-py3-none-any.whl.
File metadata
- Download URL: quipu_oprf_django-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f018b9ffd4c6caae3d36051e77bedfe165ceac517f759498261ba184b5878476
|
|
| MD5 |
8b9adba229054779e2dad4d54d06b46d
|
|
| BLAKE2b-256 |
95ac97cd3a7645e90fc8d53c5ef234e1c23ae2210e5b595716bb6bfc8224c994
|
Provenance
The following attestation bundles were made for quipu_oprf_django-0.1.0-py3-none-any.whl:
Publisher:
release.yml on isazajuancarlos/quipu
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
quipu_oprf_django-0.1.0-py3-none-any.whl -
Subject digest:
f018b9ffd4c6caae3d36051e77bedfe165ceac517f759498261ba184b5878476 - Sigstore transparency entry: 2188383154
- Sigstore integration time:
-
Permalink:
isazajuancarlos/quipu@637aca8cfdd07c83df8ceba946f78a751ad18335 -
Branch / Tag:
refs/tags/django-v0.1.0 - Owner: https://github.com/isazajuancarlos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@637aca8cfdd07c83df8ceba946f78a751ad18335 -
Trigger Event:
push
-
Statement type: