Desarrollo de páginas web y software a medida en Ecuador

jivsoft@hotmail.com +593 97 876 6762
Publicado: /

Privacidad, PII y cumplimiento: buenas prácticas al trabajar con datos sensibles

Clasifica y minimiza datos sensibles, redacta PII en entradas y logs, cifra en reposo y en tránsito, define retención y responde a solicitudes de usuarios. Incluye micro-workflows en n8n y snippets para Laravel.

Privacidad, PII y cumplimiento: buenas prácticas al trabajar con datos sensibles

Privacidad, PII y cumplimiento: buenas prácticas al trabajar con datos sensibles

Un asistente útil no tiene por qué ser indiscreto. Con minimización, cifrado, controles de acceso y retención adecuada, reduces riesgo sin perder valor.

En el post anterior optimizamos costos. Ahora toca proteger lo que más importa: datos personales y sensibles. Este guía es técnica (Laravel + n8n) y pragmática: lo mínimo para operar con responsabilidad.

Clasifica antes de procesar

  • Pública: textos y FAQs sin restricciones.
  • PII (identificable): nombre, email, teléfono, dirección, identificaciones.
  • Sensible: salud, biométricos, financieros (tarjetas), menores, credenciales.
  • Confidencial empresarial: secretos, contratos, claves, IDs internos.

Regla de oro: si no necesitas el dato, no lo recolectes. Si lo recolectas, limítalo y bórralo cuando cumpla su propósito.

Mapa de flujo de datos (MVP)

  1. Entrada → normaliza y detecta PII.
  2. RAG → solo colecciones allowlist; anonimiza antes de indexar.
  3. LLM → contrato de salida; sin PII innecesaria en prompts.
  4. Salida → guardrails de salida (post #16) con redacción si aparece PII.
  5. Logs/Trazas → redactados y con retención limitada.

Controles técnicos clave

1) Redacción de PII en la entrada y en logs

// app/Support/Pii.php
function redact(string $t): string {
  $patterns = [
    '/[\\w._%+-]+@[\\w.-]+\\.[A-Za-z]{2,7}/' => '[EMAIL]',
    '/\\+?\\d[\\d\\s().-]{7,}/' => '[PHONE]',
    '/\\b\\d{13,19}\\b/' => '[CARD]',
  ];
  return array_reduce(array_keys($patterns), fn($s,$k)=>preg_replace($k,$patterns[$k],$s), $t);
}

// app/Http/Middleware/RedactInput.php
public function handle($req, Closure $next) {
  $all = $req->all();
  array_walk_recursive($all, fn(&$v)=>$v=is_string($v)?redact($v):$v);
  $req->merge($all);
  return $next($req);
}

2) Cifrado en reposo (modelo Eloquent)

// app/Casts/EncryptedJson.php
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Support\Facades\Crypt;

class EncryptedJson implements CastsAttributes {
  public function get($model, string $key, $value, array $attrs) {
    return $value ? json_decode(Crypt::decryptString($value), true) : null;
  }
  public function set($model, string $key, $value, array $attrs) {
    return ['attributes' => [$key => Crypt::encryptString(json_encode($value))]];
  }
}

// app/Models/Conversation.php
class Conversation extends Model {
  protected $casts = ['payload' => EncryptedJson::class];
}

3) Logs sin secretos (processor de Monolog)

// app/Logging/SanitizeProcessor.php
class SanitizeProcessor {
  public function __invoke(array $record) {
    $s = json_encode($record['context']);
    $s = redact($s ?? '');
    $record['context'] = json_decode($s, true) ?? [];
    return $record;
  }
}

// config/logging.php (canal "app")
'processors' => [App\Logging\SanitizeProcessor::class],

4) Política de retención

// app/Console/Commands/PurgeTelemetry.php
public function handle() {
  DB::table('llm_metrics')->where('created_at','<',now()->subDays(30))->delete();
}

Ejecuta por cron (n8n/CRON o scheduler de Laravel). Mantén retenciones distintas para métricas vs. contenidos.

5) Embeddings SIN PII cruda

  • Anonimiza con surrogates (“[CLIENTE_123]”) antes de vectorizar.
  • Guarda un mapping seguro (tabla aparte, cifrada) para rehidratar solo si es imprescindible.

Acceso y permisos

  • Scopes y roles (API keys con permisos mínimos).
  • ABAC (atributos): tenant, país, sensibilidad del dato.
  • Registro de accesos con request_id y reason codes (auditoría).

Solicitudes de sujetos de datos (DSR): acceso/borrado/export

// routes/api.php
Route::post('/privacy/export', ExportDataController::class)->middleware('auth:api');
Route::delete('/privacy/delete', DeleteDataController::class)->middleware('auth:api');
// app/Jobs/DeleteUserData.php
public function handle() {
  DB::transaction(function() {
    Conversation::where('user_id',$this->userId)->delete();
    DB::table('llm_metrics')->where('user_id',$this->userId)->delete();
  });
}

Responde con plazos, confirma recepción y registra el cumplimiento (fecha, alcance, excepción si aplica).

Micro-workflows n8n útiles

1) “PII Guard” en la entrada

  1. Webhook → recibe {query, userId}.
  2. Function → aplica redacción (regex/NER) y clasifica riesgo.
  3. IF → si riesgo alto → “modo seguro” (post #16) o revisión humana.
  4. HTTP → envía al backend redaccionado.
  5. Database → registra risk_level y pii_types (sin ejemplos crudos).

2) “DSR Manager”

  1. Form Trigger → recibe solicitud de usuario.
  2. HTTP → crea ticket y dispara Export/Delete.
  3. Wait + Notifier → confirma cumplimiento al usuario.

Políticas mínimas (no asesoría legal)

  • Propósito definido por caso de uso; no “todo vale”.
  • Base legal/consentimiento cuando aplique; registra fecha y alcance.
  • Privacy by design: minimiza, anonimiza, cifra y borra a tiempo.
  • Acuerdos con terceros (proveedor LLM): asegúrate de contrato y región de datos.

Métricas operativas de privacidad

  • % prompts redaccionados y tipos de PII detectados.
  • Tiempo de respuesta DSR y tasa de cumplimiento.
  • Incidentes por fuga/violación de políticas.
  • Retención efectiva: filas purgadas por día/colección.

Anti-patrones

  • Registrar prompts crudos y completions sin redacción.
  • Indexar en vector DB PII sin anonimizar.
  • Retención infinita “por si acaso”.
  • Compartir datasets con claves/API keys o URLs internas.

Conclusión

Privacidad efectiva = menos datos + datos mejor protegidos + operación medible. Con redacción, cifrado, acceso mínimo y retención, tu asistente será útil y respetuoso.

  • Privacidad
  • PII
  • Cifrado
  • Retención
  • n8n
  • Laravel