Entendendo o DuckDB para Privacidade e Segurança de Dados
A 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, mantendo a utilidade dos dados. Este artigo explora como aproveitar o DuckDB, um banco de dados analítico em processo, para uma remediação eficiente de dados sensíveis.
Por que DuckDB? (E por que você deve se importar?)
Considere o DuckDB como o primo analiticamente talentoso do SQLite. É um banco de dados embutido que roda diretamente em seu processo, mas é especificamente projetado para lidar com cargas de trabalho analíticas. O que o torna perfeito para a remediação de dados? Bem, imagine poder processar grandes conjuntos de dados com uma velocidade impressionante, sem precisar 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:
- É incrivelmente rápido graças ao seu armazenamento orientado a colunas.
- Você pode executá-lo diretamente em seu ambiente Python existente.
- Ele lida com vários 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 juntamente com DuckDB. DuckDB também suporta outras linguagens, conforme mencionado em sua documentação.
Começando com DuckDB para Privacidade de Dados
Pré-requisitos
- Python 3.9 ou superior instalado
- Conhecimento prévio na configuração de 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 com 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
Aqui está como implementar um robusto mascaramento de PII (Informações Pessoalmente Identificáveis):
Vamos supor que você tenha um conjunto de dados com informações de clientes que precisam ser limpas. Aqui está como 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;
- Isto 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 padrões de mascaramento 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 (tanto maiúsculas quanto minúsculas) por
'X'
- Exemplo:
"John Doe"
se torna"XXXX XXX"
- Substitui todas as letras (tanto maiúsculas quanto 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(telefone, '[0-9]', '#') as telefone_mascarado
:- 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 com Base em Regras
Deixe-me explicar a redação de dados em termos simples antes de entrar 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, preservando 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 formato digital.
Vamos agora implementar a Redação de Dados com DuckDB e Python. Adicionei este trecho de código com comentários para que você possa seguir 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 Exemplo
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
O DuckDB é um banco de dados em memória simples, mas poderoso, que pode ajudar na remediação de dados sensíveis.
Lembre-se sempre de:
- Validar seus dados mascarados.
- Utilizar processamento paralelo para conjuntos de dados grandes.
- Aproveitar a integração do DuckDB com o S3 para dados na nuvem.
- Monitorar o uso de memória ao processar arquivos grandes.
Source:
https://dzone.com/articles/developers-guide-handling-sensitive-data-with-duckdb