OpenJane es una plataforma educativa de demostración. Este documento describe su modelo de seguridad, las amenazas conocidas, las mitigaciones implementadas y cómo reportar vulnerabilidades.
OpenJane is an educational demo platform. This document describes its security model, known threats, implemented mitigations, and how to report vulnerabilities.
El servidor no almacena nada. / The server stores nothing.
| Dato | Dónde vive | Cuándo desaparece |
|---|---|---|
| API keys del usuario | Solo en el body del POST request | Al terminar el request |
| Resultados de análisis | Solo en el navegador del usuario | Al recargar o cerrar |
| Rate limiting (IP + timestamps) | RAM del servidor (dict en memoria) | Al reiniciar el servidor |
| Logs de Render.com | Render infrastructure | Según política de Render |
No hay base de datos. No hay sesiones. No hay cookies de sesión. No hay almacenamiento persistente de ningún tipo.
Amenaza: Un usuario malicioso podría enviar ../../etc/passwd o ICE?injected=true como ticker, intentando manipular las URLs que el servidor construye para las APIs externas.
Mitigación:
TICKER_RE = re.compile(r'^[A-Z0-9.\-]{1,12}$')Todo ticker se valida con esta regex antes de usarse en cualquier URL. Un ticker inválido retorna HTTP 422 inmediatamente, sin llegar a construir ninguna URL externa.
Amenaza: Una implementación naive que use os.environ["API_KEY"] = key seguido de importlib.reload(module) en FastAPI (que es concurrente/async) puede cruzar las keys de dos usuarios simultáneos: el Request A escribe la key del usuario A en la variable de entorno global, pero antes de que el módulo recargue, el Request B escribe la key del usuario B. El Request A termina usando la key del usuario B.
Mitigación: Los clientes HTTP se instancian por request, recibiendo la key como parámetro de función. No se usa os.environ como canal de comunicación entre el request y los clientes.
# ❌ Patrón inseguro (eliminado):
os.environ["MASSIVE_API_KEY"] = key
importlib.reload(massive_client)
# ✅ Patrón seguro (implementado):
def _massive_get(key: str, endpoint: str, ...) -> dict:
req = urllib.request.Request(
url, headers={"Authorization": f"Bearer {key}"}
)Amenaza: Un usuario podría intentar inyectar instrucciones en los campos de texto (tickers, keys) para manipular el comportamiento del servidor.
Mitigación: El servidor no usa LLMs ni procesa texto libre. Los únicos campos de entrada son:
ticker: validado con regex estricto, solo alfanuméricomassive_key/eodhd_key: validados por longitud (8-200 chars), nunca interpolados en strings ejecutables
No hay superficie de prompt injection porque no hay prompt.
Amenaza: Un atacante podría enviar miles de requests para agotar el free tier de las APIs externas del usuario o sobrecargar el servidor.
Mitigación: Rate limiting en memoria por IP real:
RATE_WINDOW = 60 # segundos
RATE_LIMIT = 10 # requests por ventana por IPLa IP real se extrae de X-Forwarded-For (correcto para Render.com que está detrás de un proxy) con fallback a request.client.host.
Amenaza: Si el servidor logueara el body de los requests, las API keys del usuario quedarían en los logs de Render.com.
Mitigación:
- El servidor no loguea el body de ningún request
- Los mensajes de error están truncados a 200 caracteres y no incluyen keys
- FastAPI no loguea request bodies por defecto
Amenaza: Una API sin restricciones CORS puede ser llamada desde cualquier origen, incluyendo páginas maliciosas que el usuario visite.
Mitigación: CORS configurado con orígenes explícitos:
allow_origins=["https://ussleo.github.io", "https://openjane.onrender.com"]Amenaza: Enviar un payload JSON de varios MB para consumir memoria o tiempo de procesamiento.
Mitigación: Validación de longitud en todos los campos de string:
if not key or len(key) < 8 or len(key) > 200:
raise ValueError("API key inválida")FastAPI también tiene límites de tamaño de request por defecto.
Si un usuario sospecha que sus API keys de Massive.com o EODHD fueron comprometidas, OpenJane no puede revocarlas. El usuario debe hacerlo directamente en:
- Massive.com: dashboard → API Keys → Revoke
- EODHD: dashboard → Settings → API Token → Regenerate
Si el dispositivo del usuario está comprometido (keylogger, extensión maliciosa, red insegura sin HTTPS), las keys pueden ser capturadas antes de llegar al servidor. OpenJane usa HTTPS (enforced por Render.com) pero no puede proteger el endpoint del cliente.
Un observador de red con acceso a la conexión HTTPS del usuario puede ver los metadatos (IP destino, tamaño del request) pero no el contenido (keys, tickers) gracias a TLS.
La página /analyze incluye un botón de pánico visible que:
- Borra todas las API keys del formulario del navegador
- Elimina todos los resultados visibles
- Limpia
sessionStorageylocalStorage - Reabre el onboarding de bienvenida
El botón de pánico opera exclusivamente en el navegador del usuario. No tiene acceso al servidor ni puede revocar keys externas.
Si encuentras una vulnerabilidad de seguridad en OpenJane:
- No abras un Issue público en GitHub si la vulnerabilidad podría ser explotada antes de ser corregida
- Describe la vulnerabilidad, los pasos para reproducirla y el impacto potencial
- Abre un Issue con el label
securityo contacta directamente via GitHub
Nos comprometemos a:
- Confirmar la recepción en 48 horas
- Investigar y responder en 7 días
- Dar crédito al reportero en el fix (si lo desea)
OpenJane es una plataforma educativa y de demostración. No es un sistema financiero de producción, no maneja dinero real, no ejecuta órdenes de mercado y no almacena datos de usuario.
El nivel de seguridad implementado es apropiado para este alcance: protege a los usuarios de las amenazas más comunes (inyección, crossover de keys, flooding) sin necesitar los controles de un sistema financiero regulado (SOC2, PCI-DSS, etc.).
OpenJane · Apache 2.0 · github.com/ussleo/openjane