Bcrypt
Gere um hash bcrypt ou confira uma senha sem sair do navegador: escolha o custo e a variante $2a$, $2b$ ou $2y$, e leia qualquer hash por dentro.
Nada do que você digitar aqui sai deste navegador. O trabalho roda em um Web Worker desta aba: abra o seu próprio painel de rede e veja, nenhuma requisição sai enquanto ele calcula.
Gerar um hash
Uma senha, um custo e uma variante. O sal é sorteado novo deste navegador a cada execução, então a mesma senha dá um hash diferente toda vez: é o bcrypt funcionando, não um defeito.
Conferir uma senha contra um hash
Um hash colado é lido por dentro na hora, sem senha e sem espera. Acrescente a senha para saber se ela corresponde. Um hash não pode ser revertido, nem aqui nem em lugar nenhum.
O que existe de verdade dentro de um hash bcrypt
Um hash bcrypt é uma única string de sessenta caracteres, e cada parte dela está à vista. Nada nela é secreto além da senha que a produziu, e essa não está ali de jeito nenhum. Ler um hash por dentro não exige chave, nem senha, nem cálculo algum: esta página faz isso no instante em que você cola um.
- A variante, o campo com que a string começa: $2b$, $2a$ ou $2y$. Ela diz segundo as regras de quem o hash foi produzido.
- O custo, escrito com exatamente dois dígitos: de 04 a 31. Ele é um expoente, então custo 12 quer dizer 2^12 — 4.096 — rodadas de estiramento de chave, e o custo 13 dá o dobro do trabalho do 12 em vez de um treze avos a mais.
- O sal, vinte e dois caracteres: dezesseis bytes aleatórios, guardados à vista ao lado da resposta que eles salgaram.
- O digest, os últimos trinta e um caracteres: vinte e três bytes de saída. É a única parte que depende da senha, e ele não é o hash — o hash é a string inteira.
O alfabeto de onde saem esses caracteres é o do próprio bcrypt, e não é o Base64 padrão: ele vai ./A-Za-z0-9, então um ponto e uma barra abrem onde o Base64 põe o mais e a barra no fim. Decodificar isso com um decodificador Base64 comum devolve bytes errados em vez de um erro, e essa é uma maneira clássica de perder uma tarde.
O limite de setenta e dois bytes é contado em bytes, e isso pesa
O bcrypt lê no máximo setenta e dois bytes de uma senha e ignora tudo o que vem depois. Não setenta e dois caracteres — setenta e dois bytes de UTF-8, então quantos caracteres isso dá depende do que custam as letras de cada idioma: as vinte e seis letras latinas sem sinais custam um byte cada uma, e qualquer outra letra custa mais. Em português a diferença é pequena e real ao mesmo tempo: cada vogal acentuada — á, à, â, ã, é, ê, í, ó, ô, õ, ú — e também o ç custam dois bytes, então uma senha com alguns acentos chega ao limite alguns caracteres antes dos setenta e dois. Uma feita só dessas letras o alcançaria em trinta e seis.
- O ASCII custa um byte por caractere, então quem fala inglês chega ao limite em setenta e dois caracteres e quase nunca chega.
- O hebraico, o árabe, o russo e o grego custam dois bytes por letra, então o limite chega aos trinta e seis caracteres.
- O japonês, o coreano e o chinês custam três, então ele chega aos vinte e quatro.
- A maioria dos emojis custa quatro, e muitos dos que as pessoas realmente digitam são vários pontos de código unidos, então um punhado deles já é todo o orçamento.
O contador de bytes embaixo do campo da senha está ali para que isso seja algo que você vê chegando em vez de descobrir depois. E o corte cai no byte, não no caractere, exatamente como faz toda implementação de referência — então um caractere multibyte em cima da fronteira perde uma parte de si e conserva o resto. Esse é o comportamento compatível e não um defeito, e esta página diz isso quando acontece.
E agora a parte que explica todo o resto: o truncamento é invisível na resposta. O escalonamento da chave mistura no seu estado exatamente setenta e dois bytes de chave e nunca dá a volta com uma mais longa, então gerar o hash de uma senha de cem bytes e gerar o dos seus primeiros setenta e dois bytes produzem o mesmo digest. Não há nada para uma implementação detectar nem nada para ela relatar, e é por isso que nenhuma biblioteca levanta um erro e por isso duas senhas longas diferentes que coincidem nos primeiros setenta e dois bytes correspondem ao mesmo hash armazenado. Esta página também não pode recusar uma senha longa — se você está conferindo contra um hash que um sistema real produziu truncando, precisa da resposta — então ela dá a resposta e diz o que foi cortado.
A variante muda o prefixo e não o digest
A variante parece um número de versão e não é. As três que esta ferramenta escreve não estão ordenadas, nenhuma substitui outra, e escolher entre elas é uma decisão de compatibilidade e não de segurança.
- $2b$ é aquela em que o OpenBSD parou e é a que Python, Node e Go emitem hoje. Aqui ela é o padrão e é a resposta certa quando nada obriga a outra.
- $2a$ é a mais antiga, e algumas instalações veteranas de Java e Spring Security ainda esperam vê-la.
- $2y$ é a que o password_hash do PHP e, portanto, o Laravel escrevem, então um hash colado nesse mundo costuma pedi-la.
O que separa as três é como elas tratam uma chave com mais de 255 bytes, e o bcrypt já parou de ler nos setenta e dois — então nenhuma senha que você consiga digitar aqui alcança essa diferença. Para tudo o que esta ferramenta um dia vá receber, as três produzem os mesmos vinte e três bytes e só se separam nos quatro caracteres da frente. Troque a variante e o digest não se move.
Existem mais duas e esta ferramenta as nomeia em vez de calculá-las. $2x$ não é uma correção: a Openwall a cunhou de propósito para reproduzir um bug de extensão de sinal, de modo que os hashes feitos pelo código quebrado ainda pudessem ser conferidos, e implementá-la aqui seria pôr um defeito conhecido dentro da ferramenta. $2$ é a original, de antes de o byte zero final ser acrescentado à chave. Nenhuma das duas é emitida por nada atual; se você tem uma na mão, ela saiu de um sistema velho o bastante para a variante ser o menor dos seus achados.
O custo, e o que ele custa na sua própria máquina
O custo é o fator de trabalho, de 4 a 31, e é um expoente: cada passo dobra o tempo. É nisso que consiste o bcrypt inteiro. O hash de uma senha precisa ser lento, porque um atacante com uma tabela roubada paga o mesmo preço em cada tentativa, e dobrar o custo corta pela metade o número de tentativas por segundo que o dinheiro dele compra.
Quanto um custo dado leva de verdade é um fato sobre a máquina que o roda, não um número que dê para escrever de antemão: um celular e um servidor estão a duas ordens de grandeza um do outro. Por isso esta página mede em vez de citar: ao carregar, ela roda um hash curto no seu próprio dispositivo e extrapola dali, e é por isso que a estimativa ao lado do controle do custo fica vazia por um instante e depois aparece. Acima de alguns segundos ela pergunta antes de começar, para que um dígito digitado errado não seja lido como uma página travada.
Para escolher um número: 12 é o que o Laravel e o pacote bcrypt do Python emitem por padrão e hoje é um piso razoável em produção. Custos mais baixos servem para fixtures de teste, onde uma suíte que semeia cinquenta usuários não deveria gastar um segundo por usuário. Custos acima de 15 vale a pena medir contra o seu tráfego real de login antes de publicá-los, porque cada entrada também paga o preço.
# Apache: escrever uma linha de htpasswd com custo 12
htpasswd -nbBC 12 alice "correct horse battery staple"
# PHP e Laravel: o password_hash emite $2y$
php -r 'echo password_hash("correct horse battery staple", PASSWORD_BCRYPT);'
# Python: o pacote bcrypt emite $2b$, e o gensalt recebe o custo
python -c "import bcrypt; print(bcrypt.hashpw(b'correct horse battery staple', bcrypt.gensalt(12)))"O sal, e quando um hash feito aqui não é para armazenar
O sal são dezesseis bytes aleatórios sorteados novos para cada hash, e ele fica à vista dentro da própria string. Não é um segredo e nunca foi para ser: o trabalho dele é fazer com que duas senhas idênticas produzam dois hashes sem relação entre si, de modo que uma tabela roubada não possa ser atacada um dicionário pré-calculado por vez.
É por isso também que a mesma senha dá aqui um hash diferente toda vez que você aperta o botão. Não é uma falha e a resposta anterior não está vencida: um sal novo é o bcrypt funcionando. Qualquer um daqueles hashes corresponde à senha, porque o sal viaja dentro da string que é conferida.
O campo avançado do sal existe para um trabalho só: reproduzir exatamente o hash de outra pessoa, para que duas implementações possam ser comparadas. Cole um hash inteiro nele e o custo e a variante o acompanham sozinhos, então a senha certa reproduz a entrada caractere por caractere. Tudo o que você produzir desse jeito serve para comparar e não para armazenar: um sal usado duas vezes é um sal que parou de fazer o trabalho dele.
Um hash bcrypt não pode ser revertido
As pessoas chegam a páginas como esta procurando um jeito de descriptografar um hash bcrypt. Não existe um, e o motivo não é que seja difícil: a senha não está na string. Sessenta caracteres carregam uma variante, um custo, dezesseis bytes aleatórios e vinte e três bytes de saída, e nenhum arranjo disso contém uma senha de tamanho algum. Nada foi criptografado, então não há nada para descriptografar.
O que um site pode fazer — e é o que fazem os que anunciam descriptografia — é adivinhar. Basta pegar uma lista de senhas comuns, gerar o hash de cada uma contra o seu sal e no seu custo, e ver se alguma corresponde. O bcrypt foi projetado para tornar exatamente isso caro, que é para o que serve o custo, e esta ferramenta não oferece isso de forma alguma.
A versão honesta dessa pergunta costuma ser outra, e essa tem resposta: o que há nesta string, e esta senha em particular a produz? As duas estão nesta página. Cole o hash para ver tudo o que ele carrega, e acrescente uma senha para ter um sim ou um não.
Perguntas frequentes
- A minha senha é enviada para um servidor?
- Não. Tudo acontece nesta aba do navegador — o hashing roda em um Web Worker na sua própria máquina, a senha nunca entra na URL e nada é escrito no armazenamento do navegador. Abra o seu painel de rede, digite uma senha e aperte o botão: nenhuma requisição sai. Essa é a diferença entre esta página e os dois sites que aparecem acima dela para a mesma pergunta.
- Um hash bcrypt pode ser descriptografado de volta para a senha?
- Não, e não porque seja difícil. A senha não está na string: o que há ali é uma variante, um custo, um sal e vinte e três bytes de saída, e nada disso contém a entrada. Quem se oferece para descriptografar um está adivinhando senhas comuns contra o seu sal, que é justamente o que o bcrypt foi projetado para deixar lento.
- Por que a mesma senha dá um hash diferente toda vez?
- Porque um sal aleatório novo é sorteado a cada execução, e o sal faz parte da string. Todos aqueles hashes correspondem à mesma senha — quem confere lê o sal do hash que você cola, então não precisa saber qual execução o produziu.
- A minha senha foi cortada nos setenta e dois bytes. Por que não houve erro?
- Porque não há nada a detectar. O bcrypt mistura no seu estado exatamente setenta e dois bytes de chave e nunca lê além disso, então uma senha longa e os seus primeiros setenta e dois bytes produzem o mesmo digest: o truncamento não deixa rastro para uma biblioteca notar. Esta página avisa que aconteceu, que é o máximo que uma implementação pode fazer com honestidade.
- Por que a minha senha bateu no limite com trinta e seis caracteres?
- Porque o limite é de setenta e dois bytes e não de setenta e dois caracteres, e as letras do hebraico, do árabe, do russo e do grego custam dois bytes cada uma em UTF-8. As do japonês, do coreano e do chinês custam três, então ali o limite chega aos vinte e quatro caracteres, e a maioria dos emojis custa quatro. Em português vale o mesmo para cada vogal acentuada e para o ç: uma senha feita só dessas letras é cortada em trinta e seis. O contador embaixo do campo mostra a contagem de bytes enquanto você digita.
- Qual variante eu devo escolher?
- A que o sistema em que você vai colar espera: $2y$ para PHP e Laravel, $2a$ para Spring Security antigo, $2b$ em todo o resto. É uma decisão de compatibilidade e não de segurança — em qualquer tamanho que se possa dar a esta ferramenta, as três produzem digests idênticos e só se separam naqueles quatro caracteres da frente.
- Que custo eu devo usar?
- 12 é o padrão mais comum e um piso razoável em produção. Use um custo baixo para fixtures de teste, para a sua suíte não pagar pelo estiramento de chave cinquenta vezes, e meça qualquer coisa acima de 15 contra o seu tráfego real de login primeiro, porque cada entrada bem-sucedida paga o mesmo preço que um atacante paga.
- Um hash feito aqui é seguro para pôr em uma tabela de usuários de verdade?
- Para semear um banco de dados, escrever um fixture ou acrescentar uma linha de htpasswd, sim — o sal vem da fonte criptográfica de aleatoriedade do seu navegador. Mas esta é uma página de rascunho e não um serviço de credenciais: se você mesmo digitar o sal, o resultado deixa de ser seguro para armazenar, o que a página avisa na hora, e uma senha que valha a pena proteger é melhor cunhar onde ela vai morar.
- Por que o meu hash começa com $2y$ se o código que o fez diz bcrypt?
- Porque o password_hash do PHP escreve $2y$, e o Laravel é construído em cima dele. É o mesmo algoritmo do $2b$; a variante registra qual implementação escreveu a string. Um verificador que a recusa está recusando a variante, e não falhando em bater com a senha.
- Colei um hash e ele diz que o sal está escrito de outro jeito. Ele está quebrado?
- Não. Vinte e dois caracteres podem codificar mais bits do que dezesseis bytes precisam, então quatro bits do fim são descartados e alguns sais têm mais de uma escrita que decodifica para os mesmos bytes. Sistemas reais emitiram sais assim, então esta página lê um hash desses em vez de recusá-lo, e diz qual é a escrita canônica.
Ferramentas relacionadas
- Decodificador / verificador de JWT
Decodifica e verifica JSON Web Tokens — assinatura e claims.
- Gerador de hash
MD5, SHA-1, SHA-256, SHA-384 e SHA-512 de uma vez.
- Decodificador de certificados e PEM
Leia um certificado X.509 ou uma CSR sem OpenSSL.
- Gerador de HMAC
Assine uma mensagem com uma chave, ou verifique a assinatura de um webhook.