Dynamic OAuth Callback URL Constructed from Host Header
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 52/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Tranquilo
- Stack tecnológico
- python
- Área
- authentication, backend, security
Línea de trabajo
Empieza leyendo las líneas 63-68 de src/asfquart/generics.py y el punto de entrada setup_oauth() para rastrear cómo se construye la URL de callback a partir de quart.request.host_url. Implementa la configuración fija OAUTH_CALLBACK_HOST y actualiza el flujo de OAuth, las pruebas, la documentación y la guía de despliegue indicada en los criterios de aceptación. La tarea estará completada cuando la cabecera Host no pueda alterar la URL de callback y la URL fija esté cubierta por pruebas unitarias y de integración.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Issue: FINDING-164 - Dynamic OAuth Callback URL Constructed from Host Header
Labels: bug, security, priority:medium, asvs-level:L1
ASVS Level(s): L1
Description:
Summary
The OAuth redirect_uri parameter is dynamically derived from the HTTP Host header (callback_host = quart.request.host_url.replace('http://', 'https://')). This violates ASVS 10.4.1 because: (1) The client sends a variable callback URL instead of a fixed, pre-registered one; (2) If the deployment violates the documented ProxyPreserveHost On assumption, an attacker controlling the Host header can craft an arbitrary callback URL; (3) If the authorization server uses pattern-based or prefix matching instead of exact string comparison, the authorization code could be redirected to an attacker-controlled domain. This could lead to authorization code theft and account takeover, contingent on AS redirect URI validation strictness and deployment configuration.
Details
The vulnerability exists in src/asfquart/generics.py lines 63-68, where the callback URL is constructed from quart.request.host_url. This creates a dynamic redirect URI that varies based on the incoming request's Host header.
Recommended Remediation
Pre-configure the callback host using an environment variable instead of deriving it from the request:
import os
import urllib.parse
# In configuration
CALLBACK_HOST = os.environ.get('OAUTH_CALLBACK_HOST', 'https://myapp.apache.org')
# Modify setup_oauth() to accept callback_host parameter
def setup_oauth(app, uri: str, callback_host: str):
@app.route(uri)
async def oauth_endpoint():
state = secrets.token_urlsafe(16)
# Use fixed callback_host instead of request.host_url
callback_url = urllib.parse.urljoin(callback_host, f'{uri}?state={state}')
# ... rest of implementation
This ensures the callback URL is fixed and can be pre-registered with exact string matching at the Authorization Server.
Acceptance Criteria
-
OAUTH_CALLBACK_HOSTenvironment variable added to configuration -
setup_oauth()modified to acceptcallback_hostparameter - Callback URL constructed from fixed configuration instead of Host header
- Unit tests verify callback URL is not affected by Host header
- Integration tests verify OAuth flow with fixed callback URL
- Documentation updated with required environment variable
- Deployment guide updated to emphasize fixed callback URL registration
References
- Source reports: L1:10.4.1.md
- Related findings: FINDING-273
- ASVS sections: 10.4.1
- CWE: CWE-601
Priority
Medium
- Lenguaje dominante
- Python
- Estrellas
- 7
- Forks
- 14
- Merge medio
- 11 h 58 min
- PR fusionados (30 d)
- 1
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de apache/infrastructure-asfquart
-
Set connection and total timeouts for OAuth token exchangePosiblemente ocupada @AnKiT-GaRG2 la tomó hace 1 día. Abiertobug priority
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
apache/infrastructure-asfquart#130 ·
Los mantenedores suelen responder en 1 día
-
Document external service endpoints and failure behaviorPosiblemente ocupada @AnKiT-GaRG2 la tomó hace 5 días. Abiertodocumentation
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
apache/infrastructure-asfquart#129 ·
Los mantenedores suelen responder en 1 día
-
Document OAuth callback consistency rulesPosiblemente ocupada @Marrshal15 la tomó hace 23 días. Abiertodocumentation
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
apache/infrastructure-asfquart#126 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
ASVS priority
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
apache/infrastructure-asfquart#85 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Triage and track asfquart issuesPosiblemente ocupada @sbp la tomó hace 22 días. Abierto
apache/infrastructure-asfquart#134 · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de apache/infrastructure-asfquart
Issues similares
-
backlog
Dificultad 1/5 Menos de una hora Aptitud para principiantes 91/100
EchoTools/nevr-runtime#1108 ·
Los mantenedores suelen responder en 1 día
-
parse names a key after a top-level multi() threshold Bitcoin Core v31.1 refuses as not validAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
btclib-org/btclib-wallet#587 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
documentation
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Judgment-Pack/judgment-pack-spec#275 ·
Los mantenedores suelen responder en 1 día