fix: bloqueia open-redirect via next=/\evil.com no login
_proximo_seguro() (app/routers/auth.py) so recusava next que comecasse com "//", mas navegadores modernos tratam "/\" como equivalente a "//" (URL protocol-relative), entao next=/\evil.com ainda era um open redirect explorável via link de login malicioso. Bloqueia tambem o prefixo "/\". Teste de regressao em tests/test_router_auth.py: POST /login com next=/\evil.com precisa redirecionar para /clientes, nao para o host externo. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
aa1c528197
commit
ba8ffaede7
+1
-1
@@ -21,7 +21,7 @@ templates = Jinja2Templates(directory="app/templates")
|
|||||||
def _proximo_seguro(next_: Optional[str]) -> str:
|
def _proximo_seguro(next_: Optional[str]) -> str:
|
||||||
"""Só aceita um path interno (começa com "/", sem "//") — nunca uma URL
|
"""Só aceita um path interno (começa com "/", sem "//") — nunca uma URL
|
||||||
externa, para não virar um open redirect via POST /login?next=..."""
|
externa, para não virar um open redirect via POST /login?next=..."""
|
||||||
if next_ and next_.startswith("/") and not next_.startswith("//"):
|
if next_ and next_.startswith("/") and not next_.startswith(("//", "/\\")):
|
||||||
return next_
|
return next_
|
||||||
return "/clientes"
|
return "/clientes"
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,17 @@ def test_post_login_next_externo_e_ignorado_por_seguranca():
|
|||||||
assert resposta.headers["location"] == "/clientes"
|
assert resposta.headers["location"] == "/clientes"
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_login_next_com_barra_invertida_e_ignorado_por_seguranca():
|
||||||
|
# "/\evil.com" é tratado por navegadores modernos como equivalente a
|
||||||
|
# "//evil.com" (URL protocol-relative) — precisa ser bloqueado também.
|
||||||
|
resposta = client.post(
|
||||||
|
"/login",
|
||||||
|
data={"usuario": "admin", "senha": "teste123", "next": "/\\evil.com", "csrf_token": CSRF_TESTE},
|
||||||
|
)
|
||||||
|
assert resposta.status_code == 303
|
||||||
|
assert resposta.headers["location"] == "/clientes"
|
||||||
|
|
||||||
|
|
||||||
def test_post_login_credenciais_erradas_nao_grava_cookie():
|
def test_post_login_credenciais_erradas_nao_grava_cookie():
|
||||||
resposta = client.post(
|
resposta = client.post(
|
||||||
"/login", data={"usuario": "admin", "senha": "errada", "csrf_token": CSRF_TESTE}
|
"/login", data={"usuario": "admin", "senha": "errada", "csrf_token": CSRF_TESTE}
|
||||||
|
|||||||
Reference in New Issue
Block a user