O blog da AWS
Como anonimizar seus dados usando o AWS Glue
Por Melissa Ravanini, Arquiteta de Soluções AWS Brasil e Angelo Carvalho Arquiteto de Soluções AWS Brasil.
Com o avanço das leis de proteção de dados no mundo todo, muito se tem discutido sobre a anonimização dos dados identificáveis, também chamados de PII (Personally Identifiable Information), que são dados que têm o poder de identificar um indivíduo direta ou indiretamente. Como exemplos de identificação direta pode-se citar o nome ou o número de documentos oficiais, como CPF ou RG. Já para identificação indireta, podemos citar endereço eletrônico ou número de telefone.
A anonimização é um processo irreversível de desidentificação dos dados, e técnicas utilizadas para tal fim serão o tema deste blog post. Vale mencionar que a pseudoanonimização é o processo de desidentificação o qual pode ser revertido, possibilitando-se assim recuperar o dado original.
Além de citarmos as técnicas, ao final do artigo mostramos como é possível implementá-las utilizando o serviço AWS Glue, por meio de sua funcionalidade de Built-In Transforms, ou transformadores integrados. O AWS Glue é um serviço de ETL (extração, transformação e carga) totalmente gerenciado, o que torna mais fácil e econômico o processo de categorizar, limpar, aprimorar e mover dados de modo confiável entre vários repositórios de dados. Quanto à transformação, o AWS Glue provê uma série de transformadores que podem ser usados para agilizar a confecção de seus scripts de ETL. DrodFields, Filter, e RenameFields são exemplos de alguns dos transformadores fornecidos pela ferramenta.
Técnicas de anonimização
Vamos utilizar um exemplo fictício de uma paciente cujos dados estão armazenados em um sistema de prontuário eletrônico. Suponhamos que o objetivo final seja extrair estes dados da origem para carregá-los em um data lake a ser utilizado pela área de dados do hospital com fins de análise de negócios e geração de modelos de inteligência artificial. Anonimizar estas informações no momento da carga torna-se a escolha mais intuitiva, já que não é interessante para o hospital gerar uma nova fonte de dados a ser regida pela lei de proteção de dados.
Vamos partir de um exemplo do registro como encontrado na origem:
Observe na Figura 1 que há vários dados nesta tabela que podem ser considerados como dados identificáveis. Ao final deste blog, nosso objetivo será removê-los todos para que possamos então utilizar as informações restantes no data lake do hospital sem preocupações de as leis de proteção de dados recaírem sobre eles.
Vamos então às técnicas de anonimização:
- Supressão
Consiste na técnica de encobrir parte dos caracteres ou remover parte das colunas de um conjunto de dados. É bastante fácil de ser realizada e exige muito pouco poder computacional, no entanto ela é irreversível e remove qualquer possibilidade de se extrair valor analítico da informação posteriormente.
Para a técnica de supressão, vamos aplicar o transformador chamado DropFields, cuja função é de remover campos. O objetivo desta estratégia será de remover colunas cujos dados são identificáveis, mas que não nos são úteis para fins de análise. As colunas a serem removidas são “rua” e “número” da residência.
- Generalização
É a técnica de substituir uma informação muito específica por outra mais genérica, impedindo-se a identificação mas possibilitando que ainda seja possível extrair valor da informação. Essa técnica é irreversível e demanda a descoberta da informação que substituirá a original, aumentando-se o tempo de processamento da rotina.
Para a técnica de generalização, vamos aplicar o transformador chamado Map, para editar dados usando funções de transformação. O transformador Map permite aplicar uma função à todos registros do nosso data set, sendo uma das transformações mais flexíveis do AWS Glue. Ele será utilizado várias vezes ao longo deste blog post.
As colunas a serem modificadas são:
- “nome” : manteremos apenas o último sobrenome
- “email” : manteremos apenas o domínio do email, ou seja, apenas o conteúdo após o @
- “cep”: manteremos apenas os 5 primeiros dígitos, que são capazes de identificar cidade, região, bairro e subsetor, mas não identificam a rua. Para mais informações sobre a estrutura de CEPs brasileiros, consulte este link.
- “nascimento”: removeremos as informações de dia e mês do campo nascimento, deixando apenas o ano, que pode ser uma informação relevante para fins de análise.
- Perturbação
É a técnica de alterar – ou mascarar – a informação adicionando-se um conteúdo de ruído sem, no entanto, tirar o valor da informação. Também é bastante fácil de ser executada e exige pouco poder de processamento.
Para a técnica de perturbação, vamos aplicar no nosso exemplo o mesmo transformador anterior, o Map, para arredondar o “peso” de cada paciente à casa mais próxima de 5 pontos.
- Hashing
É a técnica de tornar um dado praticamente ilegível aplicando-se algoritmos matemáticos. Existem múltiplos algoritmos de hash, alguns deles já foram inclusive quebrados e seu uso em produção não é mais recomendado.
A utilização de hashing para anonimização é um tanto controversa. Vamos pegar como exemplo o CPF: toda vez que é aplicado um determinado algoritmo de hash ao seu CPF, o resultado será sempre o mesmo. Se alguém tiver acesso ao seu CPF e à base anonimizada, ele poderá aplicar o hash ao seu CPF e buscar o valor resultante na base e encontrar seus dados. Para contornar esta vulnerabilidade, existe uma técnica que aumenta a segurança da anonimização por hash denominada “Salt Hashing”. Ela consiste em adicionar um pouco de entropia à informação que será anonimizada, por exemplo, acrescentando um dado randômico ao final do dado original antes de se aplicar o hashing.
No nosso exemplo, aplicamos o algoritmo SHA256 como função de hash com a adição de um “Salt Hashing” randômico por meio do transformador Map ao nosso último dado identificável da tabela: o CPF.
O resultado final da nossa tabela, agora com dados anonimizados por várias técnicas de desidentificação, pode ser observado na Figura 5.
Usando os transformadores integrados do AWS Glue no seu script
Agora vamos detalhar como alcançamos os resultados acima usando os transformadores integrados do AWS Glue. Todos os exemplos são baseados em PySpark e o código completo pode ser visualizado no repositório do GitHub.
Operação de supressão:
O transformador utilizado para a operação de supressão é o DropFields. Ele tem por objetivo remover as colunas listadas no parâmetro paths. Observe que originalData é um objeto do tipo DynamicFrame contendo o conteúdo original da tabela a ser transformada. DynamicFrame é uma extensão do Apache Spark SQL DataFrame. As colunas a serem removidas são “rua” e “número” e o resultado será um novo DynamicFrame modificado chamado de “supressao”.
supressao = DropFields.apply(frame = originalData,
paths=['rua', 'numero'])
Operação de generalização:
O transformador utilizado para a operação de generalização é o Map. Ele tem por objetivo iterar em todas as linhas do dataset alterando o seu conteúdo de acordo com alguma função f . As colunas a serem generalizadas são “nome”, “email”, “cep” e “nascimento”. No código abaixo foi utilizada uma função para cada uma das colunas para fins de entendimento, mas o leitor poderá compor apenas uma função que transforme todos os campos de uma vez só.
No exemplo abaixo, a função f = RecuperaUltimoSobrenome será aplicada ao DynamicFrame “supressao”. O resultado será um novo DynamicFrame chamado “nomeGeneralizado” cuja coluna “nome” conterá apenas o sobrenome do paciente.
def RecuperaUltimoSobrenome(rec):
nomeCompleto = rec["nome"]
nomeSeparado = nomeCompleto.split()
rec["nome"] = nomeSeparado[len(nomeSeparado)-1]
return rec
nomeGeneralizado = Map.apply(frame = supressao,
f = RecuperaUltimoSobrenome)
No próximo passo, a função f = RecuperaDominioEmail será aplicada ao DynamicFrame “nomeGeneralizado”. O resultado será um novo DynamicFrame chamado “emailGeneralizado” cuja coluna “email” conterá apenas o domínio do endereço de email.
def RecuperaDominioEmail(rec):
emailCompleto = rec["email"]
dominioEmail = emailCompleto.split("@")
rec["email"] = dominioEmail[1]
return rec
emailGeneralizado = Map.apply(frame = nomeGeneralizado,
f = RecuperaDominioEmail)
Na sequência, a função f = GeneralizaCep será aplicada ao DynamicFrame “emailGeneralizado”. O resultado será um novo DynamicFrame chamado “cepGeneralizado” cuja coluna “cep” conterá apenas os 5 primeiros dígitos do CEP.
def GeneralizaCep(rec):
cepOriginal = rec["cep"]
rec["cep"] = cepOriginal[0:5]
return rec
cepGeneralizado = Map.apply(frame = emailGeneralizado,
f = GeneralizaCep)
Finalmente, a função f = AnoNascimento será aplicada ao DynamicFrame “cepGeneralizado”. O resultado será um novo DynamicFrame chamado “anoNascimento” cuja coluna “nascimento” conterá apenas o ano da data de nascimento.
def AnoNascimento(rec):
nascimentoOriginal = rec["nascimento"]
anoNascimento = nascimentoOriginal.split("/")
rec["nascimento"] = anoNascimento[2]
return rec
anoNascimento = Map.apply(frame = cepGeneralizado,
f = AnoNascimento)
Operação de perturbação:
Voltamos a usar o transformador Map para a operação de perturbação. A coluna a ser perturbada é “peso”, com base na função f = AddRuidoPeso, que arredondará o peso para o valor mais próximo da base 5.
def Round(x, base=5):
return base * round(x/base)
def AddRuidoPeso(rec):
pesoOriginal = rec["peso"]
rec["peso"] = Round(pesoOriginal)
return rec
pesoArredondado = Map.apply(frame = anoNascimento,
f = AddRuidoPeso)
Operação de hashing:
O último passo será aplicar a função f = HashCPF no campo “cpf” também por meio do transformador Map. Para tanto, é necessária a importação das bibliotecas random e hashlib do Python para posterior uso de suas funções randrange e sha256, respectivamente.
import random
import hashlib
def HashCPF(rec):
cpfOriginal = str(rec["cpf"]) + str(random.randrange(100,999))
rec["cpf"] = hashlib.sha256(cpfOriginal.encode()).hexdigest()
return rec
cpfHasheado = Map.apply(frame = pesoArredondado,
f = HashCPF)
Após a execução de todos os passos acima, finalizamos com um DynamicFrame totalmente anonimizado. Seu conteúdo então poderá ser salvo em um novo bucket no Amazon S3, o qual poderá ser consumido pelo seu time de cientistas de dados.
glueContext.write_dynamic_frame.from_options(frame = cpfHasheado,
connection_type = "s3",
connection_options = {"path": s3_output_path},
format = "parquet")
Adicionando o script de anonimização ao seu pipeline
O último passo consiste em adicionar este script a um job no AWS Glue. Siga as instruções descritas na própria documentação da AWS para criar este job. O preenchimento dos campos ficará essencialmente assim:
Conclusão
O serviço gerenciado AWS Glue e seus transformadores integrados podem agilizar o processo de confecção de scripts para anonimizar informação pessoal identificável. Isto pode previnir que sua organização gere novas fontes de dados a serem regidas por leis de proteção de dados ao redor do mundo. O AWS Glue pode anonimizar grandes quantidades de informação em pouco tempo através da arquitetura distribuída e escalável do Apache Spark. A economia também é um benefício, já que o serviço cobra apenas o custo de processamento do job, não havendo desperdício com infra-estrutura ociosa. Para visualizar o notebook Jupyter completo e o script a ser importado no job do AWS Glue, visite o repositório do GitHub.
Sobre os autores
Melissa Ravanini é arquiteta de soluções na AWS com foco em clientes da área da saúde, particularmente naqueles sem fins lucrativos. Ela apoia clientes em sua jornada para a nuvem e já atuou em projetos de processamento de genoma, telemedicina, data lakes, blockchain, PACs/RIS, HIS, interoperabilidade e muito mais.
Angelo Carvalho é Arquiteto de Soluções especialista em Big Data e Analytics. Trabalha com clientes de setor público em toda a América Latina em projetos de bancos de dados, big data, e data lakes.
Use seus dados para impulsionar o crescimento do negócio. Inove continuamente usando o data flywheel.