← Todas las noticias
data4 min de lectura

PII in your application logs is a GDPR time bomb

Por Seaworthy · 20 de junio de 2026

Printed paper showing rows of numbers and data

Photo by Mika Baumeister on Unsplash

How it starts

You ask your AI tool for a logging middleware. You want to see what's hitting your API. The assistant produces something like this:

app.use((req, res, next) => {
  console.log({
    method: req.method,
    path: req.path,
    headers: req.headers,
    body: req.body,
    query: req.query,
  });
  next();
});

This is a reasonable thing to generate. During local development, full request capture is genuinely useful. Headers carry auth tokens you want to inspect. The body tells you whether your client is sending the right shape. Nothing wrong with this for a debugging session on your laptop.

The problem is that this exact code, or something close to it, ships to production. I've seen it in code reviews. I've done it myself. The AI generates it, it works, the feature gets merged, and nobody thinks twice about the logging because the feature is what's being reviewed.

What ends up in your logs

Once that middleware hits production, your log aggregator starts collecting:

  • Email addresses from login request bodies
  • Passwords, until someone notices (hopefully)
  • Session tokens and JWTs from Authorization headers
  • Password reset tokens from form submissions
  • Names and addresses from checkout flows
  • Any PII your users send in query parameters

This data flows into whatever you're running, whether that's Datadog, Logtail, CloudWatch, or a self-hosted ELK stack. Log aggregators are not designed with GDPR data minimisation in mind. Retention policies are often set to "keep everything for 90 days" and left there. Search indexes mean the data is replicated across nodes. Audit trails for who accessed what are sparse.

Under GDPR, log data is personal data. There is no exception because it was incidental. If an email address appears in a log line, you have processed personal data, and you need a lawful basis to retain it. "We didn't mean to log it" is not a lawful basis.

Why AI tools generate this

This isn't a bug in the AI tool. It's a training distribution problem. The internet is full of development-time logging examples because developers write blog posts and Stack Overflow answers about debugging, not about production hardening. The most upvoted answer is almost always the one that solves the immediate problem without caveats. Full request logging solves the immediate problem.

Some AI assistants will add a note like "remember to remove this before production" but most don't, and even when they do, the note is easy to ignore. You were focused on the feature, not the middleware. The middleware looked fine. It ran fine. It logged what it was supposed to log.

The same pattern appears in AI-generated database query logging. ORMs like Prisma and Sequelize have verbose logging modes that can expose query parameters, which sometimes include email addresses or user IDs. An AI assistant helping you debug a slow query might turn that on and leave it on.

The fix is not complicated

Stripping sensitive fields before logging is straightforward. The harder part is knowing which fields to strip. Here's a starting point:

const REDACTED = '[REDACTED]';
const SENSITIVE_HEADERS = ['authorization', 'cookie', 'x-api-key'];
const SENSITIVE_BODY_FIELDS = ['password', 'token', 'email', 'creditCard'];

function sanitizeHeaders(headers) {
  const sanitized = { ...headers };
  for (const h of SENSITIVE_HEADERS) {
    if (sanitized[h]) sanitized[h] = REDACTED;
  }
  return sanitized;
}

function sanitizeBody(body) {
  if (!body || typeof body !== 'object') return body;
  const sanitized = { ...body };
  for (const field of SENSITIVE_BODY_FIELDS) {
    if (sanitized[field] !== undefined) sanitized[field] = REDACTED;
  }
  return sanitized;
}

app.use((req, res, next) => {
  console.log({
    method: req.method,
    path: req.path,
    headers: sanitizeHeaders(req.headers),
    body: sanitizeBody(req.body),
  });
  next();
});

This is better, but it's a denylist of fields you thought of today. If someone adds a creditCardNumber field to a form next month, it won't be caught. A more robust approach is to log only what you explicitly need, rather than sanitising from a full capture. Log the request path, the response status, the duration, and a correlation ID. That's usually enough to diagnose problems.

app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    console.log({
      method: req.method,
      path: req.path,
      status: res.statusCode,
      durationMs: Date.now() - start,
      correlationId: req.headers['x-request-id'],
    });
  });
  next();
});

No body. No Authorization header. No query string. If you need to debug a specific request, add temporary verbose logging behind a feature flag, scoped to your own test account, and pull it out when you're done.

The GDPR exposure

Article 5(1)(c) of GDPR is the data minimisation principle: collect only what is necessary. Logging full request bodies and headers is almost never necessary in production. If your logs contain personal data and you haven't documented a lawful basis, a retention period, and the appropriate technical controls, you are exposed.

The ICO in the UK and other supervisory authorities have issued fines for exactly this kind of incidental data collection. It doesn't matter that you didn't intend to build a PII database. The data is there, and that's what matters.

The more immediate risk, before any regulator gets involved, is a data breach. Log aggregators have APIs. Those APIs have credentials. Credentials get leaked. When they do, an attacker has a structured dump of everything your users ever sent you, timestamped and searchable.

Seaworthy flags this pattern automatically as part of its data exposure checks.

Este artículo fue generado por IA y resume fuentes públicamente disponibles.

De los creadores de Seaworthy

Seaworthy analiza tu repositorio en busca de los problemas que se tratan en artículos como este.

Brechas de seguridad, secretos expuestos y configuraciones erróneas detectados antes de desplegar. Gratis, sin cuenta necesaria.

$ npx seaworthycode