O mecanismo de bloqueio de login em Budibase cria uma discrepância de resposta observable que permite que os atacantes não autênticos enumeram endereços de e- mail válidos. Quando a conta de um usuário existente está bloqueada após 5 tentativas de login falhadas, o servidor retorna um ` distinto 403 ` resposta com ` X-Account- Locked: 1 ` e ` Retest- After: 900 ` cabeçalhos mais a mensagem "Conteu temporariamente bloqueado." Para usuários não existentes, a resposta é sempre um ` genérico 403 "Não autorizado"` independentemente da contagem de tentativas, porque o contador de bloqueio nunca é incrementado. A vulnerabilidade existe em dois arquivos que implementam a funcionalidade de bloqueio do login: **`pacotes/trabalhador/ src/middleware/lockout.ts: 18 - 36 `** — O intergiro de bloqueio só bloqueia os pedidos para usuários que existem no banco de dados E estão bloqueados:.
````tipo de exportação padrão async (ctx: Ctx, próximo: Próximo) => { const email = ctx.request.body.username if (!email) { retornar aguarde next() } const dbUser = aguarde userSdk.db.getUserByEmail(email) se (dbUser & & (await isLocked(email))) { // linha 26: usuários não existentes pulam este ctx.set("X-Account-Locked", " 1 ") ctx.set("Retry-After", String(env.LOGIN_LOCKOUT_SECONDS)) ctx.throw( 403, "Conteia temporariamente trancado. Tente novamente mais tarde. ") } retornar aguarde o próximo() } ``` **`pacotes/trabalhador/ src/ api/controllers/global/auth.ts: 127 - 141 `** — O gerenciador de login só aumenta o contador de falhas para usuários existentes: ```` script if (errї! user) { if (dbUser) { // linha 129: usuários não existentes nunca ativam o Failed() aguardam no Failed( e- mail) } se (await isLocked( e- mail)) { retorno de manaLockoutResponse(ctx, e- mail) } //... retorno passaporteCallback(ctx, usuário como qualquer, err, informações) } ````.
**Fluxo de execução para usuários existentes (após 5 tentativas falhadas):** 1. `Lockout` Middleware → `getUserByEmail` retorna usuário → `isLocked` retorna verdadeiro → 403 + `X-Account- Locked: 1 ` + ` Retest- After: 900 ` + "Conta temporariamente trancada" **Fluxo de execução para usuários não existentes (qualquer número de tentativas):** 1. `Lockout` Middleware → `getUserByEmail` retorna null → `dbUser && isLocked` é falso → passa por 2. O gestor de login → passaporte falha → `se (dbUser)` é falso → `onFaiiled()` nunca chamado → bloqueio nunca definido 3. Sempre retorna 403 "Não autorizado" Não existe limitação de taxas baseada em IP no endpoint de login (`POST /api/global/auth/:tenantId/ login`). A rota é registrada via `loggedInRoutes` que não aplica nenhum middleware de autenticação. O endpoint de reset de senha tem limite de taxa baseado em IP adequado, mas o endpoint de login não.
Teste contra um e-mail conhecido e um e-mail não existente # Substituir 'default' com o ID do locatário-alvo # Passo 1: Enviar 6 tentativas de login para um eco de usuário existente "=== Testando usuário existente ===" para i em $(seq 1 6 ); faça eco "--- Tentativa $i ---" curl -s -D - -X POST 10000 /api/global/auth/default/ login \ -H 'Content-Type: app/json' \ -d '{"username":"local@budibase.com","password":"wrongpassword"}' 2 >& 1 \ Ì grep - E 'HTTP/ ÌX-Account-Locked Ì Retest-After Ìlocked Ì Eco "" não autorizado" feito # Esperado: Tentativas 1 - 5 retorna a tentativa "não autorizada" 6 retorna: "Conte temporariamente bloqueado" + X-Conte bloqueado: 1 + Reexperimentar- Depois: 900.
# Passo 2: Enviar 6 tentativas de login para um echo de usuário não existente "=== Testando usuário não existente ===" para i em $(seq 1 6 ); faça eco "--- Tentativa $i ---" curl -s -D - -X POST 10000 /api/global/auth/default/ login \ -H 'Content-Type: app/json' \ -d '{"username":"nonexistent@example.com","password":"wrongpassword"}" 2 >& 1 \ Ì grep - E 'HTTP/ ÌX-Account-Locked Ì Retest-After Ìlocked Ì Eco "" não autorizado" feito # Esperado: Todos 6 tentativas de retorno "Não autorizado" — nenhum bloqueio jamais ativa # A diferença de comportamento após 5 tentativas confirmam se o email existe. ```.
- **Enumeração da conta**: Um atacante não autenticado pode determinar se algum endereço de e- mail está registrado em um inquilino do Budibase ao enviar 5 - 6 solicitações de login e observando se a resposta muda para "Account temporariamente bloqueada" com o cabeçalho `X-Account-Locked`. - **Sem limitação de taxas**: O endpoint do login não tem limitação de taxas baseada em IP, permitindo que um atacante enumera e-mails a alta velocidade a partir de um único endereço IP (~ 5 solicitações por e- mail). - **Denial of service sede- effect**: Cada e- mail existente enumerado está bloqueado para 15 minutos ( 900 segundos), impedindo que usuários legítimos façam login durante essa janela. - **Activa ataques adicionais**: Os e- mails válidos confirmados podem ser usados para phishing alvo, recheio credencial contra outros serviços ou engenharia social. O comportamento de bloqueio deve ser idêntico, independentemente de o usuário existir. Aplicar o rastreamento do lockout baseado na própria string de e- mail, não condicionado à existência do usuário do banco de dados:.
**`pacotes/trabalhador/src/middleware/lockout.ts`** — Remova a verificação `dbUser`. ```` tiposcript exportar async padrão (ctx: Ctx, próximo: Próximo) => { const email = ctx.request.body.username if (!email) { retornar aguarda next() } // Verifique o status de bloqueio baseado apenas no e- mail, e não a existência do usuário se (await isLocked(email)) { ctx.set("X-Account-Locked", " 1 ") ctx.set("Retry-After", String(env.LOGIN_LOCKOUT_SECONDS)) ctx.throw( 403, "Conteia temporariamente trancado. Tente novamente mais tarde. ") } retornar aguarde o próximo() } ``` **`pacotes/trabalhador/src/api/controllers/global/auth.ts`** — Remova a guarda `dbUser` em torno de `onFailed`. ````typescript if (errї!user) { // Sempre o contador de falhas de aumento independentemente da existência do usuário aguarda no Failed(email) se (await isLocked(email)) { handle de retornoLockoutResponse(ctx, e- mail) } //... } ```.
Além disso, considere adicionar taxa baseada em IP limitando ao endpoint de login (similar ao que já existe no endpoint de reset de senha) para limitar o rendimento de enumeração. Registro de aconselhamento: GHSA-cr 7 p-cr 3 q- h 5 cm. Identificadores relacionados: CVE- 2026 - 73306.
Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 24 T 21: 43: 11.000 Z e lista a sua última modificação como 2026 - 08 - 12 T 18: 56: 22.000 Z. Gravidade: MODERAR. Dados de pontuação publicados: CVSS_V 3: CVSS: 3.1 /AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N.
Informações sobre software e versão afetadas: pacote npm @budibase/ server — ECOSISTEM: introduzido 0, última afetada 3.38.1. Classificação e evidência: identificadores de fraqueza CWE- 204. O registro contém 5 suporte de referências nestes tipos: WEB, PACKAGE.