Criei 2 geradores de senhas, e vou direto ao ponto sobre algumas noções sobre segurança e geração de números aleatórios
Erros na programação em geradores de senhas, e como evita-los
Um grande problema que eu vejo em muitos desenvolvedores, especialmente vários projetos que eu vi no Github por aí, é o excesso de confiança em criar geradores de senhas e para manipular RNG, sem estudar minimamente sobre as implicações de como esses números são gerados.
Eles se preocupam em ter uma estetica toda bonitona para o programa ou site, em puxar um monte de API só pra ficar com cara moderno, porém, no fim das contas, é muito frequente o uso de geradores de números pseudoaleatórios fracos, baseados em fórmulas simples que são matematicamente previsíveis após 2 dígitos, como o tal do Math.random que tem em muitas linguagens populares de programação.
Isso é um problema tão grande, que já teve até carteira de criptomoeda roubada por causa disso, com prejuízo de mais de US$ 110 milhões de Dólares, por causa de um gerador de números pseudoaleatórios ridiculamente fraco que está disponível da forma mais conveniente na sua linguagem de programação favorita, ou que foi ensinado pelo teu professor de faculdade.
Como eu evito isso e como você também pode evitar?
Para começo de conversa, você deve entender que existe o CSPRNG (Cryptographically Secure PseudoRandom Rumber Generator) - gerador de números pseudoaleatórios criptografiamente seguro.
Ao invés de serem baseado numa fórmula matemática que só coleta a semente do relógio e gera um "número irracional" previsível digamos assim, o CSPRNG é baseado em coleta de uma série de dados de entropia (como ruídos térmicos, manipulações de entradas e saídas do computadores) e cálculos criptográficos que dificultam a previsibilidade do próximo número gerado.
Um CSPRNG não necessariamente é um TRNG (True Randon Number Generator) - Gerador de Números Aleatórios Verdadeiro como aqueles abajures de lava da Cloudflare, mas é uma solução matematicamente provada como de eficácia similar ao TRNG para efeitos criptográficos práticos.
A sua linguagem de programação muito provavelmente tem CSPRNG:
Se for o Java por exemplo, você obtem SecureRandom por meio do import java.security.SecureRandom.
Se for Shell Script de Linux por exemplo, você simplesmente usa os dados do dispositivo virtual localizado no "arquivo" /dev/urandom e trata isso para o problema que você quer resolver.
Se for JavaScript, você usa uma função do seu próprio navegador chamada window.crypto.getRandomValues().
Ao invés de usar o Math.random ou algum outro gerador pseudoaleatório fraco, pesquise referências para geradores de números que sejam realmente seguros (CSRNG ou TRNG) para a sua linguagem de programação favorita, pois certamente existe uma implementação segura pra gerar numeros imprevisíveis.
Minhas implementações de geradores de senhas criados base nestes aprendizados
Eu criei 2 implementações sob licença GPL v3, de código aberto para quem quiser estudar, refutar e melhorar.
Eu tenho uma implementação em JavaScript e uma em Java CLI
A aplicação Java CLI eu fiz com import do java.security.SecureRandom
A aplicação JavaScript eu fiz sem nenhuma dependência que não seja o navegador, escrevendo tudo na unha mesmo e usando a segurança que o navegador já oferece diretamente via window.crypto.getRandomValues().
Sintam-se a vontade para testar e reportar bugs.