Mascarar documentos na saída do sistema sem confundir exibição parcial com anonimização ou conformidade legal
Tutorial · prática orientada · IntermediárioUnits, strings e separação entre dados de domínio e apresentação
Compilada e testada com DCC32 37.0, build 37.0.59082.6021.Documentação, instalação e limites de CSCoder.LGPD
Experimentar o contrato no navegador
Uma lista de clientes mostra CPF completo para qualquer usuário que consiga abrir a tela. Alguém faz uma captura de tela para pedir suporte, e o documento segue junto. A operação era consultar um status; exibir o identificador inteiro não ajudou nessa tarefa.
Esse cenário é um bom ponto de partida para discutir proteção de dados no desenvolvimento. Vamos criar CSCoder.LGPD, com OcultarCPF e OcultarCNPJ, mas também analisar onde essas funções ajudam e onde a proteção precisa acontecer antes da interface.
O que é a LGPD
A Lei Geral de Proteção de Dados Pessoais, Lei nº 13.709/2018, trata do uso de dados pessoais e protege direitos de privacidade. Seu art. 5º distingue informação ligada a pessoa identificada ou identificável e dado anonimizado. O art. 6º inclui necessidade, segurança e prevenção; o art. 46 prevê medidas técnicas e administrativas de proteção. Texto compilado da LGPD no Planalto.
Isso não se resume a adicionar uma caixa de consentimento. A ANPD explica as hipóteses legais de tratamento e os direitos dos titulares em suas perguntas frequentes. A finalidade da operação e a base legal aplicável precisam ser definidas pela organização, não por uma função de formatação.
Para quem desenvolve, uma pergunta concreta é: o que esta operação realmente precisa receber e mostrar? Uma tela de acompanhamento pode trabalhar com ID interno, status e nome reduzido. Uma operação fiscal pode ter outra necessidade. Tratar todas as telas como se precisassem do mesmo cadastro completo torna os limites do sistema difíceis de controlar.
CPF, CNPJ e contexto
CPF identifica uma pessoa natural. O CNPJ identifica uma pessoa jurídica e não é automaticamente um dado pessoal só por ser um CNPJ; informações associadas a pessoas naturais exigem avaliar o contexto. A orientação da ANPD sobre dados pessoais e pessoas jurídicas ajuda a separar esses conceitos.
Oferecer OcultarCNPJ também é útil como política de exposição do produto: o sistema pode escolher reduzir documentos em listagens, imagens de suporte e relatórios sem afirmar que todo cadastro empresarial tem o mesmo enquadramento jurídico de um CPF.
Máscara não é anonimização
***.***.***-09 reduz o que aparece, mas uma linha com nome completo, telefone e endereço ainda pode identificar alguém. O dado original pode continuar no banco ou chegar intacto ao navegador. A anonimização tem requisitos ligados à possibilidade de associação à pessoa, considerando os meios disponíveis; trocar caracteres visíveis por asteriscos não comprova esse resultado. LGPD, arts. 5º e 12.
Também não existe, nessa lei, um desenho único de máscara obrigatório para CPF e CNPJ. O padrão deste artigo é uma decisão de implementação: mostrar dois caracteres no meio por padrão, escolher início ou fim, ou ocultar todas as posições quando nem essa referência for necessária.
O contrato da unit
As duas funções retornam string. O primeiro parâmetro é o documento; o segundo define a quantidade visível: 0 oculta tudo, 1 ou 2 revelam essa quantidade. O terceiro escolhe pvInicio, pvMeio ou pvFim. Os padrões são 2 e pvMeio. Quantidades fora de 0 a 2 retornam ocultação total. Não existe uma posição universalmente mais segura: revele só o que a operação precisa.
| Chamada | Retorno |
|---|---|
OcultarCPF('12345678909') |
***.*56.***-** |
OcultarCPF('123.456.789-09', 0) |
***.***.***-** |
OcultarCNPJ('12ABC34501DE35') |
**.***.*45/****-** |
OcultarCNPJ('12.ABC.345/01DE-35', 0) |
**.***.***/****-** |
OcultarCPF('12345678909', 2, pvInicio) |
12*.***.***-** |
OcultarCNPJ('12ABC34501DE35', 1, pvFim) |
**.***.***/****-*5 |
OcultarCPF('entrada incorreta') |
***.***.***-** |
Os valores são exemplos de entrada para estudar a transformação; não identificam clientes do autor. A rotina aceita documento sem pontuação ou com a máscara completa na posição correta. CPF exige 11 números. CNPJ aceita números e letras ASCII na base, inclusive minúsculas, e exige números nas duas posições finais. Assim, também atende o formato alfanumérico.
Entrada fora dessa estrutura retorna a máscara totalmente oculta. Retornar o original em caso de erro seria justamente o comportamento que queremos evitar. Uma string vazia também retorna essa máscara: se a tela precisar mostrar “não informado”, trate a ausência separadamente, sem confundir isso com liberação de acesso.
Essas funções não validam dígitos verificadores. Um documento estruturalmente aceito, mas com DV incorreto, ainda deve ser ocultado. Para validar CNPJ no cadastro, use CSCoder.Validacoes; para proteger a exibição, use a unit deste artigo. São responsabilidades diferentes.
Código completo: CSCoder.LGPD
O cabeçalho traz a licença MIT e o crédito a Claudio Santos (CS Coder). A única dependência é System.SysUtils.
{
CSCoder.LGPD
Claudio Santos | CS Coder | https://cscoder.com.br/
SPDX-License-Identifier: MIT
Uso, modificacao e redistribuicao permitidos, inclusive em projetos
comerciais. Preserve o aviso de autoria e o texto da licenca.
Fornecido sem garantias, conforme a licenca abaixo.
MIT License
Copyright (c) 2026 Claudio Santos (CS Coder)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
}
unit CSCoder.LGPD;
interface
type
TPosicaoVisivel = (pvInicio, pvMeio, pvFim);
// Mascaramento de exibicao; nao e anonimizacao nem validacao de DV.
function OcultarCPF(const Valor: string;
QuantidadeVisivel: Integer = 2;
Posicao: TPosicaoVisivel = pvMeio): string;
function OcultarCNPJ(const Valor: string;
QuantidadeVisivel: Integer = 2;
Posicao: TPosicaoVisivel = pvMeio): string;
implementation
uses System.SysUtils;
function NormalizarCPF(const Valor: string; out Documento: string): Boolean;
var
S: string;
I: Integer;
begin
Result := False;
Documento := '';
S := Trim(Valor);
case Length(S) of
11: Documento := S;
14:
begin
if (S[4] <> '.') or (S[8] <> '.') or (S[12] <> '-') then
Exit;
Documento := Copy(S, 1, 3) + Copy(S, 5, 3) +
Copy(S, 9, 3) + Copy(S, 13, 2);
end;
else
Exit;
end;
for I := 1 to 11 do
if (Documento[I] < '0') or (Documento[I] > '9') then
Exit;
Result := True;
end;
function NormalizarCNPJ(const Valor: string; out Documento: string): Boolean;
var
S: string;
I: Integer;
C: Char;
begin
Result := False;
Documento := '';
S := Trim(Valor);
case Length(S) of
14: Documento := S;
18:
begin
if (S[3] <> '.') or (S[7] <> '.') or
(S[11] <> '/') or (S[16] <> '-') then
Exit;
Documento := Copy(S, 1, 2) + Copy(S, 4, 3) +
Copy(S, 8, 3) + Copy(S, 12, 4) + Copy(S, 17, 2);
end;
else
Exit;
end;
for I := 1 to 12 do
begin
C := Documento[I];
if (C >= 'a') and (C <= 'z') then
C := Char(Ord(C) - Ord('a') + Ord('A'));
if not (((C >= '0') and (C <= '9')) or
((C >= 'A') and (C <= 'Z'))) then
Exit;
Documento[I] := C;
end;
for I := 13 to 14 do
if (Documento[I] < '0') or (Documento[I] > '9') then
Exit;
Result := True;
end;
function AplicarMascara(const Documento, Modelo: string;
QuantidadeVisivel: Integer; Posicao: TPosicaoVisivel): string;
var
Inicio, I, PosicaoDocumento: Integer;
begin
Result := Modelo;
if (QuantidadeVisivel < 1) or (QuantidadeVisivel > 2) then
Exit;
case Posicao of
pvInicio: Inicio := 1;
pvMeio: Inicio := (Length(Documento) - QuantidadeVisivel) div 2 + 1;
pvFim: Inicio := Length(Documento) - QuantidadeVisivel + 1;
else
Exit;
end;
PosicaoDocumento := 0;
for I := 1 to Length(Result) do
if Modelo[I] = '*' then
begin
Inc(PosicaoDocumento);
if (PosicaoDocumento >= Inicio) and
(PosicaoDocumento < Inicio + QuantidadeVisivel) then
Result[I] := Documento[PosicaoDocumento];
end;
end;
function OcultarCPF(const Valor: string;
QuantidadeVisivel: Integer; Posicao: TPosicaoVisivel): string;
var
Documento: string;
begin
Result := '***.***.***-**';
if (QuantidadeVisivel < 1) or (QuantidadeVisivel > 2) then
Exit;
if NormalizarCPF(Valor, Documento) then
Result := AplicarMascara(Documento, Result, QuantidadeVisivel, Posicao);
end;
function OcultarCNPJ(const Valor: string;
QuantidadeVisivel: Integer; Posicao: TPosicaoVisivel): string;
var
Documento: string;
begin
Result := '**.***.***/****-**';
if (QuantidadeVisivel < 1) or (QuantidadeVisivel > 2) then
Exit;
if NormalizarCNPJ(Valor, Documento) then
Result := AplicarMascara(Documento, Result, QuantidadeVisivel, Posicao);
end;
end.
O normalizador verifica comprimento e separadores antes de acessar posições. Não remove pontuação indiscriminadamente nem trata qualquer letra Unicode como equivalente a ASCII. As funções começam com o retorno totalmente mascarado e só revelam os caracteres escolhidos quando a estrutura foi aceita.
Aplique a máscara antes de entregar o dado
uses CSCoder.LGPD;
// Exemplo de projecao para uma listagem, nao de alteracao do cadastro.
DocumentoParaLista := OcultarCPF(CPFDoCadastro);
DocumentoParaSuporte := OcultarCPF(CPFDoCadastro, 0);
EmpresaParaLista := OcultarCNPJ(CNPJDoCadastro);Essas variáveis representam strings da aplicação; o trecho ilustra o ponto de integração, não é um programa independente. Em Delphi desktop, monte o valor de exibição antes de preencher um grid ou relatório. Em uma API, projete um DTO reduzido conforme a autorização da operação. Em UniGUI, pense também no conteúdo que efetivamente chega ao cliente.
Enviar o CPF inteiro e ocultar visualmente uma coluna com CSS não impede que o valor seja lido na resposta da rede ou no HTML. Uma tela com botão “revelar documento” deve exigir autorização no servidor para a operação correspondente. A decisão de acesso vem antes da formatação.
Não substitua o CPF original do cadastro pelo texto com asteriscos: você perderia informação que outra operação legítima pode precisar. Mantenha separados o valor de domínio e a projeção destinada a cada consumidor. Isso evita depender de um campo mascarado como chave de busca ou identificação.
Proteção que continua fora da máscara
A ANPD disponibiliza um guia de segurança da informação para agentes de pequeno porte, com orientações sobre medidas administrativas e técnicas. Ele é uma referência para organizar controles de acesso, proteção de comunicações, backups e gestão de segurança.
No seu projeto, comece com um fluxo e acompanhe o documento: entrada, persistência, consulta, log, exportação e atendimento. Identifique onde o valor completo é necessário e quem pode acessá-lo. Confira se mensagens de erro ou logs de payload registram mais do que a operação precisa. Quando o documento não for necessário, omita-o; a máscara não precisa virar um motivo para enviar o campo em todas as respostas.
Revise também o que fica no arquivo exportado. A tela pode mostrar asteriscos enquanto uma exportação lê diretamente o dataset e produz o valor inteiro. O mesmo vale para caches e imagens de suporte. Teste a saída concreta de cada caminho, em vez de concluir que o sistema está protegido porque uma função foi chamada em uma tela.
Compilação e testes
O projeto console acompanha a unit. Ele verifica retornos exatos para documentos sem máscara e formatados, ocultação configurável e total, vazios, tamanhos incorretos, separadores trocados, letras nos finais e caracteres Unicode parecidos com dígitos. Também utiliza entradas sintéticas variadas; elas não são cadastros reais.
Foram executadas 3814 verificações, após compilar com DCC32 37.0, build 37.0.59082.6021. Os testes cobrem a política de saída da unit; não auditam um sistema inteiro nem certificam conformidade legal. O ambiente efetivamente verificado está identificado acima.
Como exercício, implemente uma listagem que não entregue o documento completo para um perfil sem autorização e confira o JSON da resposta. Depois crie uma exportação com a mesma política. É um teste mais relevante que olhar apenas para os asteriscos no grid.
Downloads e licença
Adicione o arquivo ao projeto e inclua CSCoder.LGPD no uses. O pacote de testes está disponível abaixo e na página de downloads.
MIT — Copyright 2026 Claudio Santos (CS Coder). Uso e modificação são permitidos, inclusive em produtos comerciais, preservando autoria e licença. O .pas contém o texto completo; o ZIP inclui LICENSE.txt. A licença do pacote não se estende automaticamente à marca, às imagens ou a todos os artigos do blog.
Referências para continuar a leitura
- Lei nº 13.709/2018 — texto compilado no Planalto: conceitos, princípios e medidas de segurança.
- Perguntas frequentes da ANPD: dados pessoais, bases legais e direitos.
- Guia de segurança da informação da ANPD: material para organizar as medidas do sistema e da operação.
- Separação entre tela, serviço e domínio no Delphi: onde colocar regras e projeções de saída.
Projeção de dados e laboratório
Cadastro autorizado → domínio restrito → projeção de exibição → resposta → tela. Não envie o original ao navegador esperando que CSS o esconda.
- Abra o laboratório de máscaras com dados fictícios.
- Compare 0/1/2 caracteres, início/meio/fim.
- Introduza pontuação incompleta: espere ocultação total.
- Integre OcultarCPF/OcultarCNPJ antes de entregar a projeção.
Para 12345678901, dois caracteres no meio: .56.-*. Não valida DV, cifra ou certifica conformidade legal. Documentos não são gravados. O código integral permanece acessível no artigo e no .pas; API resumida.
