Entendendo o DuckDB para Privacidade e Segurança de Dados
Privacidade e segurança de dados tornaram-se críticas para todas as organizações ao redor do mundo. As organizações frequentemente precisam identificar, mascarar ou remover informações sensíveis de seus conjuntos de dados enquanto mantêm a utilidade dos dados. Este artigo explora como aproveitar o DuckDB, um banco de dados analítico em processo, para uma remoção eficiente de dados sensíveis.
Por que DuckDB? (E por que você deve se importar?)
Pense no DuckDB como o primo analiticamente talentoso do SQLite. É um banco de dados incorporado que roda diretamente em seu processo, mas foi projetado especificamente para lidar com cargas de trabalho analíticas. O que o torna perfeito para a remoção de dados? Bem, imagine poder processar grandes conjuntos de dados com velocidade impressionante, sem configurar um servidor de banco de dados complicado. Parece bom, certo?
Aqui está o que torna o DuckDB particularmente incrível para nosso caso de uso:
- É extremamente rápido graças ao seu armazenamento orientado a colunas.
- Você pode executá-lo diretamente em seu ambiente Python existente.
- Ele lida com múltiplos formatos de arquivo como se não fosse nada.
- Ele se integra bem com armazenamento em nuvem (mais sobre isso depois).
Neste guia, estarei usando Python junto com DuckDB. O DuckDB suporta outras linguagens também, como mencionado na sua documentação.
Introdução ao DuckDB para Privacidade de Dados
Pré-requisitos
- Python 3.9 ou superior instalado
- Conhecimento prévio sobre como configurar projetos Python e ambientes virtuais ou ambientes Conda
Instale o DuckDB dentro de um ambiente virtual executando o seguinte comando:
pip install duckdb --upgrade
Agora que você instalou o DuckDB, vamos criar uma conexão DuckDB:
import duckdb
import pandas as pd
# Create a DuckDB connection - it's this simple!
conn = duckdb.connect(database=':memory:')
Técnicas Avançadas de Mascaramento de Dados PII
Veja como implementar um robusto mascaramento de PII (Informações Pessoais Identificáveis):
Vamos supor que você tenha um conjunto de dados cheio de informações de clientes que precisam ser limpas. Veja como você pode lidar com cenários comuns.
Vamos criar dados de exemplo:
CREATE TABLE customer_data AS
SELECT
'John Doe' as name,
'123-45-6789' as ssn,
'[email protected]' as email,
'123-456-7890' as phone;
- Isso cria uma tabela chamada
customer_data
com uma linha de dados sensíveis de exemplo. - Os dados incluem um nome, CPF, e-mail e número de telefone.
A segunda parte envolve mascarar padrões usando regexp_replace
:
-- Implement PII masking patterns
CREATE TABLE masked_data AS
SELECT
regexp_replace(name, '[a-zA-Z]', 'X') as masked_name,
regexp_replace(ssn, '[0-9]', '*') as masked_ssn,
regexp_replace(email, '(^[^@]+)(@.*$)', '****$2') as masked_email,
regexp_replace(phone, '[0-9]', '#') as masked_phone
FROM customer_data;
Deixe-me explicar o que o código SQL acima faz.
regexp_replace(name, '[a-zA-Z]', 'X')
- Substitui todas as letras (maiúsculas e minúsculas) por
'X'
- Exemplo:
"John Doe"
se torna"XXXX XXX"
- Substitui todas as letras (maiúsculas e minúsculas) por
regexp_replace(ssn, '[0-9]', '*') as masked_ssn
- Substitui todos os dígitos por
'*'
- Exemplo:
"123-45-6789"
se torna"--***"
- Substitui todos os dígitos por
regexp_replace(email, '(^[^@]+)(@.*$)', '****$2') as masked_email:
(^[^@]+)
captura tudo antes do símbolo@
(@.*$)
captura o@
e tudo que vem depois dele- Substitui a primeira parte por
'****'
e mantém a parte do domínio - Exemplo:
""
se torna"****@email.com"
regexp_replace(phone, '[0-9]', '#') as masked_phone
:- Substitui todos os dígitos por
'#'
- Exemplo:
"123-456-7890"
se torna"###-###-####"
- Substitui todos os dígitos por
Então seus dados são transformados como abaixo:
- Dados originais:
name: John Doe
ssn: 123-45-6789
email: [email protected]
phone: 123-456-7890
- Dados mascarados:
masked_name: XXXX XXX
masked_ssn: ***-**-****
masked_email: ****@email.com
masked_phone: ###-###-####
Implementação em Python
import duckdb
import pandas as pd
def mask_pii_data():
# Create a DuckDB connection in memory
conn = duckdb.connect(database=':memory:')
try:
# Create and populate sample data
conn.execute("""
CREATE TABLE customer_data AS
SELECT
'John Doe' as name,
'123-45-6789' as ssn,
'[email protected]' as email,
'123-456-7890' as phone
""")
# Implement PII masking
conn.execute("""
CREATE TABLE masked_data AS
SELECT
regexp_replace(name, '[a-zA-Z]', 'X') as masked_name,
regexp_replace(ssn, '[0-9]', '*') as masked_ssn,
regexp_replace(email, '(^[^@]+)(@.*$)', '****$2') as masked_email,
regexp_replace(phone, '[0-9]', '#') as masked_phone
FROM customer_data
""")
# Fetch and display original data
print("Original Data:")
original_data = conn.execute("SELECT * FROM customer_data").fetchdf()
print(original_data)
print("\n")
# Fetch and display masked data
print("Masked Data:")
masked_data = conn.execute("SELECT * FROM masked_data").fetchdf()
print(masked_data)
return original_data, masked_data
except Exception as e:
print(f"An error occurred: {str(e)}")
return None, None
finally:
# Close the connection
conn.close()
Redação de Dados Baseada em Regras
Deixe-me explicar a redação de dados em termos simples antes de mergulhar em seus aspectos técnicos.
A redação de dados é o processo de ocultar ou remover informações sensíveis de documentos ou bancos de dados, enquanto preserva a estrutura geral e o conteúdo não sensível. Pense nisso como usar um marcador preto para ocultar informações confidenciais em um documento impresso, mas em forma digital.
Vamos agora implementar a Redação de Dados com DuckDB e Python. Eu adicionei este trecho de código com comentários para que você possa acompanhar facilmente.
import duckdb
import pandas as pd
def demonstrate_data_redaction():
# Create a connection
conn = duckdb.connect(':memory:')
# Create sample data with various sensitive information
conn.execute("""
CREATE TABLE sensitive_info AS SELECT * FROM (
VALUES
('John Doe', '[email protected]', 'CC: 4532-1234-5678-9012', 'Normal text'),
('Jane Smith', '[email protected]', 'SSN: 123-45-6789', 'Some notes'),
('Bob Wilson', '[email protected]', 'Password: SecretPass123!', 'Regular info'),
('Alice Brown', '[email protected]', 'API_KEY=abc123xyz', 'Basic text')
) AS t(name, email, sensitive_field, normal_text);
""")
# Define redaction rules
redaction_rules = {
'email': r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', # Email pattern
'sensitive_field': r'(CC:\s*\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}|SSN:\s*\d{3}-\d{2}-\d{4}|Password:\s*\S+|API_KEY=\S+)', # Various sensitive patterns
'name': r'[A-Z][a-z]+ [A-Z][a-z]+' # Full name pattern
}
# Show original data
print("Original Data:")
print(conn.execute("SELECT * FROM sensitive_info").fetchdf())
# Apply redaction
redact_sensitive_data(conn, 'sensitive_info', redaction_rules)
# Show redacted data
print("\nRedacted Data:")
print(conn.execute("SELECT * FROM redacted_data").fetchdf())
return conn
def redact_sensitive_data(conn, table_name, rules):
"""
Redact sensitive data based on specified patterns.
Parameters:
- conn: DuckDB connection
- table_name: Name of the table containing sensitive data
- rules: Dictionary of column names and their corresponding regex patterns to match sensitive data
"""
redaction_cases = []
# This creates a CASE statement for each column
# If the pattern matches, the value is redacted
# If not, the original value is kept
for column, pattern in rules.items():
redaction_cases.append(f"""
CASE
WHEN regexp_matches({column}, '{pattern}')
THEN '(REDACTED)'
ELSE {column}
END as {column}
""")
query = f"""
CREATE TABLE redacted_data AS
SELECT
{', '.join(redaction_cases)}
FROM {table_name};
"""
conn.execute(query)
# Example with custom redaction patterns
def demonstrate_custom_redaction():
conn = duckdb.connect(':memory:')
# Create sample data
conn.execute("""
CREATE TABLE customer_data AS SELECT * FROM (
VALUES
('John Doe', '123-45-6789', 'ACC#12345', '$5000'),
('Jane Smith', '987-65-4321', 'ACC#67890', '$3000'),
('Bob Wilson', '456-78-9012', 'ACC#11111', '$7500')
) AS t(name, ssn, account, balance);
""")
# Define custom redaction rules with different patterns
custom_rules = {
'name': {
'pattern': r'[A-Z][a-z]+ [A-Z][a-z]+',
'replacement': lambda match: f"{match[0][0]}*** {match[0].split()[1][0]}***"
},
'ssn': {
'pattern': r'\d{3}-\d{2}-\d{4}',
'replacement': 'XXX-XX-XXXX'
},
'account': {
'pattern': r'ACC#\d{5}',
'replacement': 'ACC#*****'
}
}
def apply_custom_redaction(conn, table_name, rules):
redaction_cases = []
for column, rule in rules.items():
redaction_cases.append(f"""
CASE
WHEN regexp_matches({column}, '{rule['pattern']}')
THEN '{rule['replacement']}'
ELSE {column}
END as {column}
""")
query = f"""
CREATE TABLE custom_redacted AS
SELECT
{', '.join(redaction_cases)},
balance -- Keep this column unchanged
FROM {table_name};
"""
conn.execute(query)
# Show original data
print("\nOriginal Customer Data:")
print(conn.execute("SELECT * FROM customer_data").fetchdf())
# Apply custom redaction
apply_custom_redaction(conn, 'customer_data', custom_rules)
# Show results
print("\nCustom Redacted Data:")
print(conn.execute("SELECT * FROM custom_redacted").fetchdf())
# Run demonstrations
print("=== Basic Redaction Demo ===")
demonstrate_data_redaction()
print("\n=== Custom Redaction Demo ===")
demonstrate_custom_redaction()
Resultados de Amostra
Antes da redação:
name email sensitive_field
John Doe [email protected] CC: 4532-1234-5678-9012
Depois da redação:
name email sensitive_field
(REDACTED) (REDACTED) (REDACTEd)
Conclusão
DuckDB é um banco de dados em memória simples, mas poderoso, que pode ajudar na remediação de dados sensíveis.
Lembre-se de sempre:
- Validar seus dados mascarados.
- Usar processamento paralelo para grandes conjuntos de dados.
- Aproveitar a integração do DuckDB com S3 para dados em nuvem.
- Fique atento ao uso da sua memória ao processar arquivos grandes.
Source:
https://dzone.com/articles/developers-guide-handling-sensitive-data-with-duckdb