Métodos eficientes para criar senhas seguras no terminal Linux utilizando Bash. As técnicas abaixo combinam diferentes ferramentas para gerar senhas contendo letras, números e caracteres especiais — de one-liners rápidos a um script completo instalável no sistema.
/dev/urandom com trMétodo simples e amplamente utilizado por sua segurança e facilidade — é a opção recomendada para uso geral.
head /dev/urandom | tr -dc 'A-Za-z0-9!@#$%^&*()' | head -c 20 ; echo
Ou, para incluir uma gama maior de caracteres especiais:
head /dev/urandom | tr -dc '[:alnum:][:punct:]' | head -c 20 ; echo
Explicação:
head /dev/urandom: lê uma quantidade de dados aleatóriostr -dc 'A-Za-z0-9!@#$%^&*()': filtra os caracteres para incluir letras, números e alguns símbolostr -dc '[:alnum:][:punct:]': filtra caracteres alfanuméricos e de pontuação para mais variedadehead -c 20: define o comprimento da senha para 20 caracteres (ajustável)Opção com símbolos, sem caracteres ambíguos (sem l, 1, O, 0):
head /dev/urandom | tr -dc 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#%^&*_=+[]{}:,.?~-' | head -c 20 ; echo
Sem símbolos e sem ambiguidade:
head /dev/urandom | tr -dc 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789' | head -c 20 ; echo
opensslO openssl gera dados criptograficamente fortes e está disponível na maioria dos sistemas.
openssl rand -base64 24 | tr -dc '[:alnum:][:punct:]' | head -c 20 ; echo
Explicação:
openssl rand -base64 24: gera 24 bytes aleatórios codificados em Base64tr -dc '[:alnum:][:punct:]': filtra apenas caracteres alfanuméricos e de pontuaçãohead -c 20: define o comprimento da senhaparte1=$(date +%s%N) # Segundos e nanossegundos desde 1970
parte2=$(openssl rand -base64 10 | tr -dc '[:alnum:][:punct:]')
senha="${parte1:0:10}$parte2"
echo "$senha"
Perigo: a porção baseada em data/hora não é secreta — um atacante que saiba aproximadamente quando a senha foi criada reduz drasticamente as tentativas necessárias. A data garante unicidade, não segurança. Não use este método para nada sensível; prefira os métodos 1 ou 2.
shufpalavras=('Casa' 'Carro' 'Livro' 'Árvore' 'Sol')
senha=$(printf "%s" "${palavras[@]}" | fold -w1 | shuf | tr -d '\n' | head -c 20 ; echo)
echo "$senha"
Explicação:
printf "%s" "${palavras[@]}": imprime todas as palavras sem espaçosfold -w1: coloca cada caractere em uma linhashuf: embaralha os caractereshead -c 20: limita a senha a 20 caracteresAtenção: este método usa apenas letras de um conjunto pequeno e conhecido — a entropia é muito menor que a dos métodos aleatórios. Use apenas para senhas descartáveis ou de baixa criticidade.
pwgen (pacote externo)# Instalação: sudo apt-get install pwgen (Debian/Ubuntu)
pwgen -s 20 1 # Gera uma senha segura de 20 caracteres
/dev/randomhead -c 32 /dev/random | base64 | tr -dc '[:alnum:][:punct:]' | head -c 20 ; echo
Nota: em kernels modernos (4.8+),
/dev/randome/dev/urandomusam o mesmo gerador (CRNG). O/dev/urandomnão bloqueia e é a fonte recomendada para praticamente todos os usos — inclusive criptográficos. Não há ganho de segurança em preferir/dev/randomem sistemas atuais.
python3 -c "import secrets, string; print(''.join(secrets.choice(string.ascii_letters + string.digits + '!@#$%^&*') for i in range(20)))"
gerar-senhasA versão completa em script, instalável globalmente no sistema. Oferece escolha de comprimento, quantidade, tipos de caracteres, métodos de hash, cópia para a área de transferência (X11, Wayland e macOS), passphrase estilo Diceware, PIN numérico e salvamento em arquivo com permissões seguras.
Crie o diretório para seus scripts (se ainda não existir):
mkdir -p ~/workspace-as-code/scripts
Crie o arquivo e torne-o executável:
touch ~/workspace-as-code/scripts/gerar-senhas
chmod +x ~/workspace-as-code/scripts/gerar-senhas
Edite o arquivo com seu editor favorito e cole o conteúdo abaixo.
#!/bin/bash
################################################################################
# gerar-senhas - Gerador Seguro de Senhas
# Uso: Produção e Homelab Kubernetes
# Suporta: X11, Wayland, macOS | OpenSSL, mkpasswd, urandom
################################################################################
set -euo pipefail
# Cores
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly CYAN='\033[0;36m'
readonly MAGENTA='\033[0;35m'
readonly NC='\033[0m'
# Limites de segurança
readonly COMPRIMENTO_MAX=256
readonly QUANTIDADE_MAX=100
readonly URANDOM_BUFFER=8192
readonly MIN_ENTROPIA=1000
readonly PIN_MAX=12
readonly WORDLIST_MIN=4
# Diretórios de wordlists comuns
readonly WORDLISTS=(
"/usr/share/dict/words"
"/usr/share/dict/american-english"
"/usr/share/dict/brazilian"
"/usr/share/dict/portuguese"
"/usr/share/dict/cracklib-words"
)
# Funções de logging
_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
_error() { echo -e "${RED}[ERRO]${NC} $*" >&2; }
_success() { echo -e "${GREEN}[SUCESSO]${NC} $*"; }
_debug() { [[ "${DEBUG:-0}" == "1" ]] && echo -e "${MAGENTA}[DEBUG]${NC} $*" >&2 || true; }
# Limpeza segura de memória
_limpar_variaveis_sensiveis() {
local vars=("senha" "senha_gerada" "caracteres" "ultima_senha" "ultima_senha_clipboard"
"senha_plana" "pin" "passphrase" "temp_senha" "hash_result")
for var in "${vars[@]}"; do
unset "$var" 2>/dev/null || true
done
# Limpar histórico do bash se em modo interativo
if [[ -n "${PS1:-}" ]]; then
history -c 2>/dev/null || true
history -w 2>/dev/null || true
fi
}
# Verificação de comandos
command_exists() { command -v "$1" &>/dev/null; }
# Verificação de entropia do sistema
_verificar_entropia() {
if [ -r /proc/sys/kernel/random/entropy_avail ]; then
local entropy
entropy=$(cat /proc/sys/kernel/random/entropy_avail)
_debug "Entropia disponível: $entropy bits"
if [ "$entropy" -lt "$MIN_ENTROPIA" ]; then
_warn "Entropia baixa ($entropy bits). Considere instalar haveged ou rng-tools"
_warn "Em VMs recém-iniciadas, aguarde alguns segundos e tente novamente"
if [[ "${FORCA_BAIXA_ENTROPIA:-0}" != "1" ]]; then
read -rp "Continuar mesmo assim? [s/N]: " resposta
[[ "$resposta" =~ ^[Ss]$ ]] || return 1
fi
fi
fi
}
# Detecção de ambiente clipboard
_detectar_clipboard() {
if [ -n "${WAYLAND_DISPLAY:-}" ] && command_exists wl-copy; then
echo "wayland"
elif [ -n "${DISPLAY:-}" ] && command_exists xclip; then
echo "x11"
elif command_exists pbcopy; then
echo "macos"
else
echo "none"
fi
}
# Copiar para clipboard de forma segura
_copiar_clipboard() {
local texto="$1"
local clipboard_type
clipboard_type=$(_detectar_clipboard)
case "$clipboard_type" in
wayland)
printf '%s' "$texto" | wl-copy 2>/dev/null && return 0
;;
x11)
printf '%s' "$texto" | xclip -selection clipboard 2>/dev/null && return 0
;;
macos)
printf '%s' "$texto" | pbcopy 2>/dev/null && return 0
;;
esac
return 1
}
# Geração de conjunto de caracteres (escape seguro)
_gerar_caracteres() {
local apenas_hex=$1
local usar_simbolos=$2
local sem_ambiguos=$3
if [ "$apenas_hex" = true ]; then
printf '%s' '0123456789ABCDEF'
elif [ "$sem_ambiguos" = true ]; then
if [ "$usar_simbolos" = true ]; then
printf '%s' 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#%^&*_=+[]{}:,.?~-'
else
printf '%s' 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'
fi
elif [ "$usar_simbolos" = true ]; then
printf '%s' 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#%^&*_=+[]{}:,.?~-'
else
printf '%s' 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
fi
}
# Geração via /dev/urandom (método primário)
_gerar_senha_urandom() {
local comprimento=$1
local caracteres=$2
local resultado
# Desabilitar pipefail localmente: quando head fecha após ler os bytes
# necessários, tr recebe SIGPIPE (sinal 13). Isso é comportamento esperado
# do pipeline e não um erro real. Sem essa correção, pipefail captura o
# exit code 141 do tr e falso-negativa a geração mesmo com dados válidos.
set +o pipefail
resultado=$(LC_ALL=C tr -dc "$caracteres" < /dev/urandom 2>/dev/null | head -c "$comprimento")
set -o pipefail
if [ "${#resultado}" -eq "$comprimento" ]; then
printf '%s' "$resultado"
return 0
fi
return 1
}
# Geração via openssl com redução de viés
_gerar_senha_openssl_rand() {
local comprimento=$1
local sem_ambiguos=${2:-false}
# Aumentar buffer para reduzir viés do base64 + tr
# Fator 4 é conservador para garantir entropia suficiente após filtragem
local bytes_necessarios=$((comprimento * 4 + 64))
local filtro
local resultado
if [ "$sem_ambiguos" = true ]; then
filtro='a-km-np-zA-KM-NP-Z2-9'
else
filtro='A-Za-z0-9'
fi
# Tentar múltiplas vezes se o resultado for curto (filtragem agressiva)
for tentativa in 1 2 3; do
set +o pipefail
resultado=$(openssl rand -base64 "$bytes_necessarios" 2>/dev/null | \
LC_ALL=C tr -dc "$filtro" | \
head -c "$comprimento")
set -o pipefail
if [ "${#resultado}" -eq "$comprimento" ]; then
printf '%s' "$resultado"
return 0
fi
_debug "Tentativa $tentativa: resultado curto (${#resultado}), retrying..."
bytes_necessarios=$((bytes_necessarios * 2)) # Dobrar buffer
done
return 1
}
# Geração de PIN numérico (sem leading zero para compatibilidade legacy)
_gerar_pin() {
local digitos=$1
local primeiro
local resto
# Primeiro dígito: 1-9 (evitar leading zero em sistemas que tratam como octal)
set +o pipefail
primeiro=$(LC_ALL=C tr -dc '1-9' < /dev/urandom | head -c1)
set -o pipefail
[ -n "$primeiro" ] || return 1
if [ "$digitos" -eq 1 ]; then
printf '%s' "$primeiro"
return 0
fi
# Demais dígitos: 0-9 permitido
set +o pipefail
resto=$(LC_ALL=C tr -dc '0-9' < /dev/urandom | head -c$((digitos - 1)))
set -o pipefail
[ -n "$resto" ] || return 1
printf '%s%s' "$primeiro" "$resto"
}
# Geração de passphrase estilo Diceware
_gerar_passphrase() {
local num_palavras=$1
local separador=${2:--} # Default: hífen
local wordlist=""
# Encontrar wordlist disponível
for wl in "${WORDLISTS[@]}"; do
if [ -r "$wl" ] && [ "$(wc -l < "$wl")" -gt 1000 ]; then
wordlist="$wl"
_debug "Usando wordlist: $wordlist"
break
fi
done
if [ -z "$wordlist" ]; then
_error "Nenhuma wordlist encontrada. Instale: sudo apt install wamerican ou wbritish"
return 1
fi
# Selecionar palavras aleatórias (shuf é mais seguro que sort -R)
if command_exists shuf; then
shuf -n "$num_palavras" "$wordlist" 2>/dev/null | paste -sd "$separador" -
else
# Fallback sem shuf (menos eficiente)
local palavras=()
for ((i=0; i<num_palavras; i++)); do
local linha_max
linha_max=$(wc -l < "$wordlist")
local rand_line=$(( (RANDOM % linha_max) + 1 ))
palavras+=("$(sed -n "${rand_line}p" "$wordlist")")
done
local IFS="$separador"
printf '%s' "${palavras[*]}"
fi
}
# Verificação de força da senha (opcional)
_verificar_forca() {
local senha="$1"
# Verificações básicas
local comprimento=${#senha}
local tem_maiuscula=0
local tem_minuscula=0
local tem_numero=0
local tem_simbolo=0
if [[ "$senha" =~ [A-Z] ]]; then tem_maiuscula=1; fi
if [[ "$senha" =~ [a-z] ]]; then tem_minuscula=1; fi
if [[ "$senha" =~ [0-9] ]]; then tem_numero=1; fi
if [[ "$senha" =~ [^A-Za-z0-9] ]]; then tem_simbolo=1; fi
local score=$((tem_maiuscula + tem_minuscula + tem_numero + tem_simbolo))
if [ "$comprimento" -lt 8 ]; then
_warn "Senha muito curta ($comprimento caracteres) - Muito Fraca"
elif [ "$comprimento" -lt 12 ] && [ "$score" -lt 3 ]; then
_warn "Senha curta com pouca variedade - Fraca"
elif [ "$score" -lt 3 ]; then
_warn "Senha sem variedade de caracteres - Média"
fi
# Verificação via cracklib se disponível
if command_exists cracklib-check && [ "$comprimento" -lt 20 ]; then
local check_result
check_result=$(echo "$senha" | cracklib-check 2>/dev/null || true)
if [[ "$check_result" =~ "dictionary" ]] || [[ "$check_result" =~ "too simple" ]]; then
_warn " cracklib: Padrão detectável em dicionário"
fi
fi
}
# Hashing functions
_gerar_hash_mkpasswd() {
local senha=$1
local metodo=${2:-SHA-512}
mkpasswd -m "$metodo" "$senha" 2>/dev/null
}
_gerar_hash_openssl() {
local senha=$1
# -6 = SHA512, -5 = SHA256, -1 = MD5 (evitar)
openssl passwd -6 "$senha" 2>/dev/null
}
_gerar_hash_bcrypt() {
local senha=$1
local custo=${2:-10}
# Usar python3 passlib ou htpasswd como fallback
if command_exists python3; then
python3 -c "from passlib.hash import bcrypt; print(bcrypt.using(rounds=$custo).hash('$senha'))" 2>/dev/null
elif command_exists htpasswd; then
htpasswd -bnBC "$custo" "" "$senha" 2>/dev/null | cut -d: -f2
else
return 1
fi
}
mostrar_ajuda() {
cat << 'EOF'
Uso: gerar-senha [OPÇÕES]
Gera senhas aleatórias seguras, PINs, passphrases ou hashes para produção.
Opções Principais:
-c, --comprimento N Comprimento da senha (padrão: 20, máx: 256)
-q, --quantidade N Quantidade de senhas (padrão: 1, máx: 100)
-m, --metodo METODO Método: random, openssl-rand, mkpasswd, openssl, bcrypt
(padrão: random)
Modos Especiais:
-p, --pin N Gerar PIN numérico de N dígitos (evita leading zero)
-w, --words N Gerar passphrase com N palavras (Diceware)
-W, --word-sep CHAR Separador para passphrase (padrão: -)
Configuração de Caracteres:
-s, --sem-simbolos Apenas alfanumérico
-a, --sem-ambiguos Remove 0,O,l,1,I, etc.
-x, --hexadecimal Hexadecimal (0-9,A-F)
-e, --entropia Verifica entropia do sistema antes de gerar
Saída:
-S, --mostrar-senha Mostra senha em claro quando usando hash methods
-C, --clipboard Copia última senha para clipboard (auto-detecta X11/Wayland)
-f, --arquivo ARQUIVO Salva em arquivo (perm 600) com metadata
--custo N Custo para bcrypt (padrão: 10)
Segurança:
-F, --forca Verifica força da senha gerada
--force-low-entropy Ignora aviso de baixa entropia
Exemplos:
# Básico
gerar-senha
# PIN de 6 dígitos (2FA/banco)
gerar-senha -p 6
# Passphrase memorável (Diceware)
gerar-senha -w 5
# Hash bcrypt para aplicação web
gerar-senha -c 16 -m bcrypt --custo 12 -S
# Múltiplas senhas para Kubernetes secrets
gerar-senha -q 10 -c 32 -f senhas-k8s.txt
# Senha forte com verificação
gerar-senha -c 24 -a -F -C
Integração Kubernetes:
# Gerar secret diretamente
kubectl create secret generic app-secret \
--from-literal=password="$(gerar-senha -c 32 -s)"
Ambiente:
DEBUG=1 Modo verbose
FORCA_BAIXA_ENTROPIA=1 Ignora checagem de entropia
EOF
}
main() {
# Defaults
local comprimento=20
local usar_simbolos=true
local quantidade=1
local apenas_hex=false
local sem_ambiguos=false
local metodo="random"
local mostrar_senha=false
local copiar_clipboard=false
local arquivo_saida=""
local verificar_forca=false
local check_entropia=false
local modo_pin=false
local modo_passphrase=false
local num_palavras=0
local separador_palavras="-"
local custo_bcrypt=10
# Traps estendidos para todas as saídas possíveis
trap '_limpar_variaveis_sensiveis' RETURN INT TERM EXIT
# Parse de argumentos
while [[ $# -gt 0 ]]; do
case $1 in
-c|--comprimento)
[[ -n "${2:-}" ]] || { _error "Opção $1 requer valor"; return 1; }
[[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -gt 0 ] || { _error "Comprimento deve ser inteiro positivo"; return 1; }
comprimento="$2"
shift 2
;;
-q|--quantidade)
[[ -n "${2:-}" ]] || { _error "Opção $1 requer valor"; return 1; }
[[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -gt 0 ] || { _error "Quantidade inválida"; return 1; }
quantidade="$2"
shift 2
;;
-p|--pin)
[[ -n "${2:-}" ]] || { _error "Opção $1 requer valor"; return 1; }
[[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -gt 0 ] && [ "$2" -le "$PIN_MAX" ] || {
_error "PIN deve ter 1-$PIN_MAX dígitos"; return 1;
}
modo_pin=true
comprimento="$2" # Reusa variável para tamanho do PIN
shift 2
;;
-w|--words)
[[ -n "${2:-}" ]] || { _error "Opção $1 requer valor"; return 1; }
[[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -ge "$WORDLIST_MIN" ] || {
_error "Mínimo de $WORDLIST_MIN palavras"; return 1;
}
modo_passphrase=true
num_palavras="$2"
shift 2
;;
-W|--word-sep)
[[ -n "${2:-}" ]] || { _error "Opção $1 requer valor"; return 1; }
separador_palavras="$2"
shift 2
;;
-s|--sem-simbolos)
usar_simbolos=false
shift
;;
-a|--sem-ambiguos)
sem_ambiguos=true
shift
;;
-x|--hexadecimal)
apenas_hex=true
shift
;;
-m|--metodo)
[[ -n "${2:-}" ]] || { _error "Opção $1 requer valor"; return 1; }
case "$2" in
random|openssl-rand|mkpasswd|openssl|bcrypt) metodo="$2" ;;
*) _error "Método inválido: random, openssl-rand, mkpasswd, openssl, bcrypt"; return 1 ;;
esac
shift 2
;;
--custo)
[[ -n "${2:-}" ]] || { _error "Opção $1 requer valor"; return 1; }
[[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -ge 4 ] && [ "$2" -le 31 ] || {
_error "Custo bcrypt deve ser 4-31"; return 1;
}
custo_bcrypt="$2"
shift 2
;;
-S|--mostrar-senha)
mostrar_senha=true
shift
;;
-C|--clipboard)
copiar_clipboard=true
shift
;;
-f|--arquivo)
[[ -n "${2:-}" ]] || { _error "Opção $1 requer valor"; return 1; }
arquivo_saida="$2"
shift 2
;;
-F|--forca)
verificar_forca=true
shift
;;
-e|--entropia)
check_entropia=true
shift
;;
--force-low-entropy)
export FORCA_BAIXA_ENTROPIA=1
shift
;;
-h|--help)
mostrar_ajuda
return 0
;;
*)
_error "Opção desconhecida: $1"
mostrar_ajuda
return 1
;;
esac
done
# Validações de combinações inválidas
if [ "$modo_pin" = true ] && [ "$modo_passphrase" = true ]; then
_error "Não use --pin e --words simultaneamente"
return 1
fi
if [ "$apenas_hex" = true ] && [ "$sem_ambiguos" = true ]; then
_error "Hexadecimal e sem-ambiguos são incompatíveis"
return 1
fi
if [ "$modo_passphrase" = true ]; then
if [ "$apenas_hex" = true ] || [ "$sem_ambiguos" = true ] || [ "$usar_simbolos" = false ]; then
_error "Opções de caracteres não aplicáveis a passphrases"
return 1
fi
fi
# Limites globais
if [ "$comprimento" -gt "$COMPRIMENTO_MAX" ]; then
_error "Comprimento máximo: $COMPRIMENTO_MAX"
return 1
fi
if [ "$quantidade" -gt "$QUANTIDADE_MAX" ]; then
_error "Quantidade máxima: $QUANTIDADE_MAX"
return 1
fi
# Verificação de entropia se solicitado ou em modo paranóico
if [ "$check_entropia" = true ] || [ "$modo_pin" = false -a "$comprimento" -gt 32 ]; then
_verificar_entropia || return 1
fi
# Verificação de dependências por método
case "$metodo" in
mkpasswd)
command_exists mkpasswd || { _error "Instale: sudo apt install whois"; return 1; }
;;
openssl|openssl-rand)
command_exists openssl || { _error "OpenSSL não instalado"; return 1; }
;;
bcrypt)
command_exists python3 || command_exists htpasswd || {
_error "bcrypt requer python3-passlib ou apache2-utils"; return 1
}
;;
esac
# Setup de arquivo de saída
if [ -n "$arquivo_saida" ]; then
touch "$arquivo_saida" 2>/dev/null || { _error "Não foi possível criar $arquivo_saida"; return 1; }
chmod 600 "$arquivo_saida"
# Metadata
{
echo "# ==========================================="
echo "# Gerado por: gerar-senha"
echo "# Data: $(date -Iseconds)"
echo "# Host: $(hostname)"
echo "# Usuário: $(whoami)"
echo "# Método: $metodo"
if [ "$modo_passphrase" = true ]; then
echo "# Palavras: $num_palavras | Sep: $separador_palavras"
else
echo "# Comprimento: $comprimento | Quantidade: $quantidade"
fi
echo "# Recomendação: Remover após uso (auto-destruição em 24h ideal)"
echo "# ==========================================="
} >> "$arquivo_saida"
fi
# Mensagem informativa
if [ "$modo_pin" = true ]; then
_info "Gerando PIN de $comprimento dígitos..."
elif [ "$modo_passphrase" = true ]; then
_info "Gerando passphrase Diceware ($num_palavras palavras)..."
else
case "$metodo" in
random) _info "Geração via /dev/urandom..." ;;
openssl-rand) _info "Geração via OpenSSL..." ;;
mkpasswd) _info "Hash SHA-512 (whois)..." ;;
openssl) _info "Hash SHA-512 (OpenSSL)..." ;;
bcrypt) _info "Hash bcrypt (custo: $custo_bcrypt)..." ;;
esac
fi
# Geração principal
local ultima_senha=""
local ultima_senha_clipboard=""
local clipboard_detectado
clipboard_detectado=$(_detectar_clipboard)
if [ "$copiar_clipboard" = true ]; then
if [ "$clipboard_detectado" == "none" ]; then
_warn "Clipboard não detectado (instale: wl-clipboard, xclip, ou pbcopy)"
copiar_clipboard=false
else
_debug "Clipboard detectado: $clipboard_detectado"
fi
fi
for ((i=1; i<=quantidade; i++)); do
[ "$quantidade" -gt 1 ] && echo -e "${CYAN}--- Item $i ---${NC}"
local senha=""
local senha_plana="" # Para hash methods
local caracteres=""
# Lógica de geração
if [ "$modo_pin" = true ]; then
senha=$(_gerar_pin "$comprimento") || { _error "Falha ao gerar PIN"; return 1; }
elif [ "$modo_passphrase" = true ]; then
senha=$(_gerar_passphrase "$num_palavras" "$separador_palavras") || {
_error "Falha ao gerar passphrase"; return 1
}
else
# Modo senha tradicional
case "$metodo" in
random)
caracteres=$(_gerar_caracteres "$apenas_hex" "$usar_simbolos" "$sem_ambiguos")
senha=$(_gerar_senha_urandom "$comprimento" "$caracteres") || {
_error "Falha na geração urandom"; return 1
}
;;
openssl-rand)
senha=$(_gerar_senha_openssl_rand "$comprimento" "$sem_ambiguos") || {
_error "Falha no OpenSSL rand"; return 1
}
;;
mkpasswd|openssl|bcrypt)
# Gerar senha plana primeiro
caracteres=$(_gerar_caracteres "$apenas_hex" "$usar_simbolos" "$sem_ambiguos")
senha_plana=$(_gerar_senha_urandom "$comprimento" "$caracteres") || {
_error "Falha ao gerar senha base"; return 1
}
[ -n "$senha_plana" ] || { _error "Senha vazia"; return 1; }
# Aplicar hash
case "$metodo" in
mkpasswd) senha=$(_gerar_hash_mkpasswd "$senha_plana") ;;
openssl) senha=$(_gerar_hash_openssl "$senha_plana") ;;
bcrypt) senha=$(_gerar_hash_bcrypt "$senha_plana" "$custo_bcrypt") ;;
esac
if [ -z "$senha" ]; then
_error "Falha no hashing"; return 1
fi
if [ "$mostrar_senha" = true ]; then
echo -e "${MAGENTA}Senha:${NC} ${GREEN}${senha_plana}${NC}"
ultima_senha_clipboard="$senha_plana"
else
ultima_senha_clipboard="$senha"
fi
;;
esac
fi
# Validação final
[ -n "$senha" ] || { _error "Senha vazia gerada"; return 1; }
# Saída formatada
if [ "$modo_passphrase" = true ]; then
echo -e "${GREEN}${senha}${NC}"
else
echo -e "${GREEN}${senha}${NC}"
fi
# Verificação de força (se solicitado e modo não-hash)
if [ "$verificar_forca" = true ] && [[ "$metodo" =~ ^(random|openssl-rand)$ ]]; then
_verificar_forca "$senha"
fi
# Persistência
[ -n "$arquivo_saida" ] && echo "$senha" >> "$arquivo_saida"
# Atualizar clipboard candidate
ultima_senha="$senha"
if [[ "$metodo" =~ ^(random|openssl-rand)$ ]] || [ "$modo_pin" = true ] || [ "$modo_passphrase" = true ]; then
ultima_senha_clipboard="$senha"
fi
# Separador visual
[ "$quantidade" -gt 1 ] && [ "$i" -lt "$quantidade" ] && echo
done
# Clipboard final
if [ "$copiar_clipboard" = true ] && [ -n "$ultima_senha_clipboard" ]; then
if _copiar_clipboard "$ultima_senha_clipboard"; then
_success "Copiado para clipboard ($(echo "$clipboard_detectado" | tr '[:lower:]' '[:upper:]'))"
_info "Clipboard será limpo automaticamente em 30s (se possível)"
# Tentar limpar após 30s (background, não bloqueante)
(sleep 30 && echo "" | _copiar_clipboard "" 2>/dev/null) &
else
_warn "Falha ao copiar para clipboard"
fi
fi
# Resumo
if [ -n "$arquivo_saida" ]; then
_success "Salvo em: $arquivo_saida (perm: 600, $(wc -l < "$arquivo_saida") linhas)"
_info "Remova o arquivo após uso: rm -f $arquivo_saida"
fi
# Avisos de segurança contextuais
if [ "$comprimento" -lt 12 ] && [ "$modo_pin" = false ] && [ "$modo_passphrase" = false ]; then
_warn "Senhas < 12 caracteres não recomendadas para produção"
fi
if [ "$metodo" == "bcrypt" ]; then
_info "Formato bcrypt ideal para aplicações web modernas"
elif [[ "$metodo" =~ ^(mkpasswd|openssl)$ ]]; then
_info "Use este hash em /etc/shadow ou sistemas legados"
fi
# Cleanup explícito antes de sair
_limpar_variaveis_sensiveis
return 0
}
main "$@"
Para executar de qualquer lugar, crie um link simbólico em /usr/local/bin:
sudo ln -s ~/workspace-as-code/scripts/gerar-senhas /usr/local/bin/gerar-senhas
Verifique se o link foi criado corretamente:
ls -l /usr/local/bin/gerar-senhas
gerar-senhas --help
gerar-senhas -c 16 -q 3
gerar-senhas --metodo mkpasswd -S
gerar-senhas -c 32 -C # copia para o clipboard (requer xclip)
gerar-senhas -q 5 -f minhas_senhas.txt
/dev/urandom e openssl, fontes de entropia confiáveis--clipboard requer xclip (X11), wl-copy (Wayland) ou pbcopy (macOS)mkpasswd requer o pacote whois (sudo apt install whois)-f é criado com permissões 600 (leitura/escrita só para o proprietário)Os métodos cobrem uma ampla gama de abordagens para gerar senhas direto no terminal. Orientações de escolha:
/dev/urandom ou openssl) — aleatoriedade criptograficamente adequada em kernels modernosgerar-senhas oferece flexibilidade máxima e se integra a outros processosLembrete: senhas geradas no terminal ficam no histórico do shell e no scrollback. Em ambientes compartilhados, use a opção
--clipboarddo script ou limpe o histórico após gerar (history -c).
/dev/random vs /dev/urandom