Recentemente, um cliente se aproximou de nossa equipe com uma necessidade específica e complexa: a criação de senhas robustas e diversificadas para seus usuários. A demanda incluiu requisitos como tamanho definido, quantidade mínima de números, letras maiúsculas e minúsculas, caracteres especiais e regras para iniciar a senha.
Em um mundo cada vez mais digitalizado, onde a segurança é vital, senhas fortes são essenciais para proteger informações sensíveis. No entanto, criar senhas complexas manualmente pode ser uma tarefa demorada e propensa a erros.
A Solução Inicial
Nossa primeira abordagem foi fornecer ao cliente uma função PL/SQL simples que poderia atender às suas necessidades básicas. A função foi projetada para ser executada diretamente no banco de dados Oracle, evitando a necessidade de pacotes especiais ou software adicional.
CREATE OR REPLACE FUNCTION GERAR_SENHA (
p_tamanho IN NUMBER,
p_numeros IN NUMBER,
p_minusculas IN NUMBER,
p_maiusculas IN NUMBER,
p_iniciar_com IN CHAR,
p_tem_especial IN CHAR,
p_qtd_especial IN NUMBER
) RETURN VARCHAR2 AS
v_senha VARCHAR2(100);
v_caract_espec VARCHAR2(10) := '!@#$%^&*()';
v_alpha VARCHAR2(52) := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
v_num VARCHAR2(10) := '0123456789';
v_count NUMBER;
v_pos NUMBER;
BEGIN
IF p_tamanho < (p_numeros + p_minusculas + p_maiusculas + p_qtd_especial) THEN
RETURN 'Parâmetros inválidos.';
END IF;
IF p_iniciar_com = 'L' THEN
v_senha := SUBSTR(v_alpha, DBMS_RANDOM.VALUE(1, 26), 1);
ELSIF p_iniciar_com = 'N' THEN
v_senha := SUBSTR(v_num, DBMS_RANDOM.VALUE(1, 10), 1);
ELSIF p_iniciar_com = 'C' AND p_tem_especial = 'S' THEN
v_senha := SUBSTR(v_caract_espec, DBMS_RANDOM.VALUE(1, 10), 1);
ELSE
RETURN 'Parâmetros inválidos.';
END IF;
FOR v_count IN 1 .. p_minusculas - 1 LOOP
v_pos := LENGTH(v_senha) + 1;
v_senha := SUBSTR(v_senha, 1, v_pos - 1) || SUBSTR(v_alpha, DBMS_RANDOM.VALUE(1, 26), 1);
END LOOP;
FOR v_count IN 1 .. p_maiusculas LOOP
v_pos := LENGTH(v_senha) + 1;
v_senha := SUBSTR(v_senha, 1, v_pos - 1) || SUBSTR(v_alpha, DBMS_RANDOM.VALUE(27, 52), 1);
END LOOP;
FOR v_count IN 1 .. p_numeros LOOP
v_pos := LENGTH(v_senha) + 1;
v_senha := SUBSTR(v_senha, 1, v_pos - 1) || SUBSTR(v_num, DBMS_RANDOM.VALUE(1, 10), 1);
END LOOP;
IF p_tem_especial = 'S' THEN
FOR v_count IN 1 .. p_qtd_especial LOOP
v_pos := LENGTH(v_senha) + 1;
v_senha := SUBSTR(v_senha, 1, v_pos - 1) || SUBSTR(v_caract_espec, DBMS_RANDOM.VALUE(1, 10), 1);
END LOOP;
END IF;
WHILE LENGTH(v_senha) < p_tamanho LOOP
v_pos := LENGTH(v_senha) + 1;
v_senha := SUBSTR(v_senha, 1, v_pos - 1) || SUBSTR(v_alpha, DBMS_RANDOM.VALUE(1, 52), 1);
END LOOP;
RETURN v_senha;
END GERAR_SENHA;
/
O cliente ficou satisfeito com a solução, mas vimos uma oportunidade de aperfeiçoá-la.
Aprimorando a Solução
Decidimos ir além e melhorar a função, tornando-a mais flexível e segura. Introduzimos uma lógica adicional para embaralhar os caracteres da senha, aumentando a imprevisibilidade e a robustez da senha gerada.
CREATE OR REPLACE FUNCTION EMARALHAR (
p_str_txt IN VARCHAR2
) RETURN VARCHAR2 AS
v_length NUMBER;
v_output VARCHAR2(100);
v_random NUMBER;
v_aux VARCHAR2(100);
BEGIN
v_length := LENGTH(p_str_txt);
v_aux := p_str_txt;
FOR i IN 1 .. v_length LOOP
v_random := DBMS_RANDOM.VALUE(1, LENGTH(p_str_txt));
v_output := v_output || SUBSTR(p_str_txt, v_random, 1);
v_aux := SUBSTR(p_str_txt, 1, v_random - 1) || SUBSTR(p_str_txt, v_random + 1);
END LOOP;
RETURN v_output;
END EMARALHAR;
Além disso, encapsulamos as funções em uma package PL/SQL, separando a lógica de geração da lógica de embaralhamento e promovendo uma organização de código mais clara.
CREATE OR REPLACE PACKAGE SENHA_PKG AS
FUNCTION GERAR_SENHA (
p_tamanho IN NUMBER,
p_numeros IN NUMBER,
p_minusculas IN NUMBER,
p_maiusculas IN NUMBER,
p_iniciar_com IN CHAR,
p_tem_especial IN CHAR,
p_qtd_especial IN NUMBER
) RETURN VARCHAR2;
END SENHA_PKG;
/
CREATE OR REPLACE PACKAGE BODY SENHA_PKG AS
FUNCTION EMARALHAR (
p_str_txt IN VARCHAR2
) RETURN VARCHAR2 AS
v_length NUMBER;
v_output VARCHAR2(100);
v_random NUMBER;
v_aux VARCHAR2(100);
BEGIN
v_length := LENGTH(p_str_txt);
v_aux := p_str_txt;
FOR i IN 1 .. v_length LOOP
v_random := DBMS_RANDOM.VALUE(1, LENGTH(p_str_txt));
v_output := v_output || SUBSTR(p_str_txt, v_random, 1);
v_aux := SUBSTR(p_str_txt, 1, v_random - 1) || SUBSTR(p_str_txt, v_random + 1);
END LOOP;
RETURN v_output;
END EMARALHAR;
FUNCTION GERAR_SENHA (
p_tamanho IN NUMBER,
p_numeros IN NUMBER,
p_minusculas IN NUMBER,
p_maiusculas IN NUMBER,
p_iniciar_com IN CHAR,
p_tem_especial IN CHAR,
p_qtd_especial IN NUMBER
) RETURN VARCHAR2 AS
v_senha VARCHAR2(100);
v_caract_espec VARCHAR2(10) := '!@#$%^&*()';
v_alpha VARCHAR2(52) := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
v_num VARCHAR2(10) := '0123456789';
v_count NUMBER;
v_pos NUMBER;
BEGIN
IF p_tamanho < (p_numeros + p_minusculas + p_maiusculas + p_qtd_especial) THEN
RETURN 'Parâmetros inválidos.';
END IF;
IF p_iniciar_com = 'L' THEN
v_senha := SUBSTR(v_alpha, DBMS_RANDOM.VALUE(1, 26), 1);
ELSIF p_iniciar_com = 'N' THEN
v_senha := SUBSTR(v_num, DBMS_RANDOM.VALUE(1, 10), 1);
ELSIF p_iniciar_com = 'C' AND p_tem_especial = 'S' THEN
v_senha := SUBSTR(v_caract_espec, DBMS_RANDOM.VALUE(1, 10), 1);
ELSE
RETURN 'Parâmetros inválidos.';
END IF;
FOR v_count IN 1 .. p_minusculas - 1 LOOP
v_pos := LENGTH(v_senha) + 1;
v_senha := SUBSTR(v_senha, 1, v_pos - 1) || SUBSTR(v_alpha, DBMS_RANDOM.VALUE(1, 26), 1);
END LOOP;
FOR v_count IN 1 .. p_maiusculas LOOP
v_pos := LENGTH(v_senha) + 1;
v_senha := SUBSTR(v_senha, 1, v_pos - 1) || SUBSTR(v_alpha, DBMS_RANDOM.VALUE(27, 52), 1);
END LOOP;
FOR v_count IN 1 .. p_numeros LOOP
v_pos := LENGTH(v_senha) + 1;
v_senha := SUBSTR(v_senha, 1, v_pos - 1) || SUBSTR(v_num, DBMS_RANDOM.VALUE(1, 10), 1);
END LOOP;
IF p_tem_especial = 'S' THEN
FOR v_count IN 1 .. p_qtd_especial LOOP
v_pos := LENGTH(v_senha) + 1;
v_senha := SUBSTR(v_senha, 1, v_pos - 1) || SUBSTR(v_caract_espec, DBMS_RANDOM.VALUE(1, 10), 1);
END LOOP;
END IF;
WHILE LENGTH(v_senha) < p_tamanho LOOP
v_pos := LENGTH(v_senha) + 1;
v_senha := SUBSTR(v_senha, 1, v_pos - 1) || SUBSTR(v_alpha, DBMS_RANDOM.VALUE(1, 52), 1);
END LOOP;
v_senha := EMARALHAR(v_senha);
RETURN v_senha;
END GERAR_SENHA;
END SENHA_PKG;
/
Exemplo Prático
Aqui está um exemplo de como chamar a função GERAR_SENHA através da package:
SELECT SENHA_PKG.GERAR_SENHA(10, 2, 2, 2, 'L', 'S', 1) FROM DUAL;
Esta chamada geraria uma senha com 10 caracteres, incluindo pelo menos 2 números, 2 letras minúsculas, 2 letras maiúsculas, começando com uma letra e contendo pelo menos 1 caractere especial.

Utilidade na Vida do Banco de Dados
A solução aprimorada não é apenas útil para o cliente em questão, mas também tem amplas aplicações em muitos outros cenários. Bancos de dados em toda parte podem se beneficiar de uma função como essa para:
- Gerar senhas para novos usuários de forma automatizada.
- Redefinir senhas de maneira segura.
- Integrar com sistemas existentes sem a necessidade de pacotes especiais.
Portabilidade
Embora a solução tenha sido escrita em PL/SQL para Oracle, o conceito pode ser facilmente traduzido para outros bancos de dados. Isso a torna uma solução atrativa para organizações que operam em diferentes ambientes de banco de dados.
Conclusão
O desafio trazido pelo cliente nos permitiu não apenas fornecer uma solução eficaz mas também aprimorar e expandir essa solução para um uso mais amplo.
A função aperfeiçoada demonstra como é possível atender a requisitos complexos de geração de senhas diretamente no banco de dados, sem depender de ferramentas externas, e é um excelente exemplo de como a flexibilidade e o poder da linguagem SQL podem ser usados para fornecer soluções eficientes, seguras e portáveis.
A capacidade de gerar senhas complexas dentro do próprio banco de dados, com parâmetros personalizáveis, é uma adição valiosa a qualquer sistema que lide com dados sensíveis e segurança do usuário. Nosso cliente agora possui uma ferramenta robusta que atende às suas necessidades, e esperamos que essa abordagem possa ser útil para outros também.