# Como gerar senha segura em código (sem Math.random)

> Uma senha segura depende de duas coisas: um gerador aleatório criptográfico e um sorteio sem viés entre os caracteres. Este guia explica por que Math.random não serve, calcula a força de uma senha em bits e mostra como gerar senhas em JavaScript, Python e PHP com as funções seguras de cada linguagem.

- Endereço oficial: https://pradev.com.br/guias/como-gerar-senha-segura-em-codigo
- Tipo: Guia
- Publicado em: 2026-09-30
- Atualizado em: 2026-09-30
- Autor: Leonardo Gaertner (https://pradev.com.br/sobre)
- Ferramentas: [Gerador de Senha](https://pradev.com.br/gerador-de-senha)

## O gerador certo

`Math.random` no JavaScript, `random` no Python e `rand` no PHP são rápidos, mas previsíveis: quem observa alguns valores pode deduzir os próximos. Para senhas, tokens e códigos de recuperação, use o gerador criptográfico: `crypto.getRandomValues` no JavaScript, o módulo `secrets` no Python e `random_int` no PHP.

Para gerar uma senha agora, sem código, use o [gerador de senha](https://pradev.com.br/gerador-de-senha), que usa o gerador criptográfico do navegador.

## Quão forte é uma senha

A força se mede em bits: tamanho vezes o logaritmo na base 2 do número de caracteres possíveis. Com o alfabeto dos exemplos (71 caracteres), cada caractere vale cerca de 6,15 bits.

- 12 caracteres: cerca de 74 bits.
- 16 caracteres: cerca de 98 bits, um bom padrão para senhas guardadas em gerenciador.
- Aumentar o tamanho rende mais que aumentar o alfabeto: cada caractere a mais soma 6 bits, e dobrar o alfabeto soma só 1 bit por caractere.

## O viés do módulo

O erro clássico é pegar um byte aleatório (0 a 255) e fazer `byte % tamanhoDoAlfabeto`. Com 71 caracteres, 256 não divide por 71: sobram 43 valores, e os 43 primeiros caracteres saem 4 vezes em 256 enquanto os outros saem 3. A senha continua parecendo aleatória, mas fica mais fácil de adivinhar.

A solução é descartar os bytes da sobra, como faz o exemplo em JavaScript. Python e PHP já sorteiam sem viés.

## Em JavaScript

```javascript
const ALFABETO = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
  'abcdefghijklmnopqrstuvwxyz' +
  '0123456789!@#$%&*-_';

function gerarSenha(tamanho = 16, alfabeto = ALFABETO) {
  // Descarta bytes acima do maior múltiplo do tamanho do alfabeto: sem isso, os
  // primeiros caracteres sairiam mais vezes (o viés do módulo)
  const limite = 256 - (256 % alfabeto.length);
  let senha = '';
  while (senha.length < tamanho) {
    // getRandomValues aceita no máximo 65536 bytes por chamada
    const bytes = crypto.getRandomValues(new Uint8Array(Math.min(tamanho * 2, 65536)));
    for (const b of bytes) {
      if (b < limite && senha.length < tamanho) {
        senha += alfabeto[b % alfabeto.length];
      }
    }
  }
  return senha;
}
```

_JavaScript. Funciona no navegador e no Node.js. Os bytes descartados são repostos na próxima volta._

## Em Python e PHP

```python
import secrets

ALFABETO = (
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "0123456789!@#$%&*-_"
)


def gerar_senha(tamanho: int = 16, alfabeto: str = ALFABETO) -> str:
    # secrets.choice usa o gerador seguro do sistema e sorteia sem viés
    return "".join(secrets.choice(alfabeto) for _ in range(tamanho))
```

_Python_

```php
<?php
const ALFABETO = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' .
    'abcdefghijklmnopqrstuvwxyz' .
    '0123456789!@#$%&*-_';

function gerarSenha(int $tamanho = 16, string $alfabeto = ALFABETO): string
{
    $senha = '';
    $max = strlen($alfabeto) - 1;
    for ($i = 0; $i < $tamanho; $i++) {
        $senha .= $alfabeto[random_int(0, $max)]; // random_int é seguro e sem viés
    }
    return $senha;
}
```

_PHP_

## Cuidados

- Nunca registre a senha gerada em logs, nem a envie por e-mail em texto. Se precisar entregar uma senha, prefira um link de definição de senha com validade curta.
- Exigir "pelo menos um número e um símbolo" pouco acrescenta a uma senha aleatória longa, e pode forçar um novo sorteio. O tamanho importa mais.
- Para guardar a senha no banco, nunca use o texto nem um hash simples. Veja [como guardar senhas com hash](https://pradev.com.br/guias/como-guardar-senhas-com-hash).
- Se a senha vai ser digitada à mão, tire caracteres parecidos (0 e O, 1, l e I) do alfabeto e compense com um caractere a mais.

## Perguntas frequentes

### Math.random serve para gerar senha?

Não. O Math.random não é criptográfico: a sequência pode ser prevista a partir de alguns valores. Use crypto.getRandomValues no navegador e no Node.js.

### Qual o tamanho ideal de uma senha?

Para senhas aleatórias guardadas em um gerenciador, 16 caracteres ou mais. Com letras, números e alguns símbolos, isso passa de 90 bits, fora do alcance de tentativas por força bruta.

### O que é o viés do módulo?

É a diferença de frequência que aparece quando um número aleatório é reduzido com o resto da divisão por um valor que não divide o intervalo por igual. Alguns caracteres passam a sair mais que outros.

### Como gerar senha em Python?

Use o módulo secrets: "".join(secrets.choice(alfabeto) for _ in range(16)). Ele usa o gerador seguro do sistema operacional e sorteia sem viés.

## Fontes

- [MDN: Crypto.getRandomValues](https://developer.mozilla.org/pt-BR/docs/Web/API/Crypto/getRandomValues)
- [Python: módulo secrets](https://docs.python.org/3/library/secrets.html)

## Guias relacionados

- [Como guardar senhas: por que não usar SHA-256 e o que usar](https://pradev.com.br/guias/como-guardar-senhas-com-hash): Por que MD5 e SHA-256 não servem para senhas e como guardar senhas com scrypt, bcrypt ou Argon2 em Node.js, Python e PHP, com sal e código testado.
