Entendiendo DuckDB para la Privacidad y Seguridad de los Datos
La privacidad y seguridad de los datos se han vuelto críticas para todas las organizaciones en todo el mundo. Las organizaciones a menudo necesitan identificar, enmascarar o eliminar información sensible de sus conjuntos de datos mientras mantienen la utilidad de los datos. Este artículo explora cómo aprovechar DuckDB, una base de datos analítica en proceso, para una remediación eficiente de datos sensibles.
¿Por qué DuckDB? (¿Y por qué deberías importarte?)
Piensa en DuckDB como el primo analíticamente dotado de SQLite. Es una base de datos embebida que se ejecuta directamente en tu proceso, pero está diseñada específicamente para manejar cargas de trabajo analíticas. ¿Qué lo hace perfecto para la remediación de datos? Bueno, imagina poder procesar grandes conjuntos de datos a una velocidad vertiginosa, sin tener que configurar un complicado servidor de base de datos. Suena bien, ¿verdad?
Esto es lo que hace que DuckDB sea particularmente impresionante para nuestro caso de uso:
- Es extremadamente rápido gracias a su almacenamiento orientado a columnas.
- Puedes ejecutarlo directamente en tu entorno Python existente.
- Maneja múltiples formatos de archivo como si nada.
- Funciona bien con el almacenamiento en la nube (más sobre eso más adelante).
En esta guía, estaré utilizando Python junto con DuckDB. DuckDB también admite otros idiomas, como se menciona en su documentación.
Comenzando con DuckDB para la Privacidad de Datos
Requisitos
- Python 3.9 o superior instalado
- Conocimientos previos sobre cómo configurar proyectos de Python y entornos virtuales o entornos Conda
Instalar DuckDB dentro de un entorno virtual ejecutando el siguiente comando:
pip install duckdb --upgrade
Ahora que has instalado DuckDB, creemos una conexión DuckDB:
import duckdb
import pandas as pd
# Create a DuckDB connection - it's this simple!
conn = duckdb.connect(database=':memory:')
Técnicas Avanzadas de Enmascaramiento de Datos PII
Aquí tienes cómo implementar un enmascaramiento robusto de PII (Información de Identificación Personal):
Supongamos que tienes un conjunto de datos lleno de información de clientes que necesita ser limpiada. Aquí tienes cómo puedes manejar escenarios comunes.
Creemos datos de muestra:
CREATE TABLE customer_data AS
SELECT
'John Doe' as name,
'123-45-6789' as ssn,
'[email protected]' as email,
'123-456-7890' as phone;
- Esto crea una tabla llamada
datos_cliente
con una fila de datos sensibles de muestra. - Los datos incluyen un nombre, SSN, correo electrónico y número de teléfono.
La segunda parte implica patrones de enmascaramiento utilizando 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;
Déjame explicarte qué hace el código SQL anterior.
regexp_replace(name, '[a-zA-Z]', 'X')
- Reemplaza todas las letras (mayúsculas y minúsculas) con
'X'
- Ejemplo:
"John Doe"
se convierte en"XXXX XXX"
- Reemplaza todas las letras (mayúsculas y minúsculas) con
regexp_replace(ssn, '[0-9]', '*') as masked_ssn
- Reemplaza todos los dígitos con
'*'
- Ejemplo:
"123-45-6789"
se convierte en"--***"
- Reemplaza todos los dígitos con
regexp_replace(email, '(^[^@]+)(@.*$)', '****$2') as masked_email:
(^[^@]+)
captura todo antes del símbolo@
(@.*$)
captura el@
y todo después de él- Reemplaza la primera parte con
'****'
y mantiene la parte del dominio - Ejemplo:
""
se convierte en"****@email.com"
regexp_replace(phone, '[0-9]', '#') as masked_phone
:- Reemplaza todos los dígitos con
'#'
- Ejemplo:
"123-456-7890"
se convierte en"###-###-####"
- Reemplaza todos los dígitos con
Así que tus datos se transforman como se muestra a continuación:
- Datos originales:
name: John Doe
ssn: 123-45-6789
email: [email protected]
phone: 123-456-7890
- Datos enmascarados:
masked_name: XXXX XXX
masked_ssn: ***-**-****
masked_email: ****@email.com
masked_phone: ###-###-####
Implementación en 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()
Redacción de datos basada en reglas
Déjame explicar la redacción de datos en términos simples antes de profundizar en sus aspectos técnicos.
La redacción de datos es el proceso de ocultar o eliminar información sensible de documentos o bases de datos mientras se preserva la estructura general y el contenido no sensible. Piensa en ello como usar un marcador negro para ocultar información confidencial en un documento impreso, pero en forma digital.
Ahora implementemos la redacción de datos con DuckDB y Python. He añadido este fragmento de código con comentarios para que puedas seguir fácilmente.
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 muestra
Antes de la redacción:
name email sensitive_field
John Doe [email protected] CC: 4532-1234-5678-9012
Después de la redacción:
name email sensitive_field
(REDACTED) (REDACTED) (REDACTEd)
Conclusión
DuckDB es una base de datos en memoria simple pero poderosa que puede ayudar con la remediación de datos sensibles.
Recuerda siempre:
- Validar tus datos enmascarados.
- Utilizar procesamiento paralelo para grandes conjuntos de datos.
- Aprovechar la integración de S3 de DuckDB para datos en la nube.
- Presta atención al uso de memoria al procesar archivos grandes.
Source:
https://dzone.com/articles/developers-guide-handling-sensitive-data-with-duckdb