winstonjs / winston

A logger for just about everything.
http://github.com/winstonjs/winston
MIT License
22.6k stars 1.8k forks source link

redacting secrets #1079

Closed sramam closed 6 years ago

sramam commented 6 years ago

I am interested in integrating a library that optionally redacts-secrets from log messages. Perhaps something like - https://www.npmjs.com/package/redact-secrets.

Wondering if there is an appetite for a PR of this nature?

One way to accomplish this today is to do something like this. However, this is harder to do when the output format is not an object.

   const redact = require('redact-secrets')('[REDACTED]');
   const winston = require('winston');

   winston.configure({
      transports: [
        new (winston.transports.File)({ 
          filename: 'somefile.log',
          json: false,
          prettyPrint: (json) => JSON.stringify(redact.map(json), null, 2)
        })
      ]
    });

This proposal would apply this internally before the log message is serialized, make it's application more reliable. Thoughts?

Sicria commented 6 years ago

An alternative to this is using a rewriter.

// Redact all sensitive information contained in the meta
logger.rewriters.push((level, msg, meta) => redact.map(meta));
indexzero commented 6 years ago

In winston@3.0.0 you can implement this using a custom format. Please consider upgrading.

const redact = require('redact-secrets')('[REDACTED]');
const { createLogger, format, transports } = require('winston');

const logger = createLogger({
  format: format.combine(
    // Redact any properties
    format(info => redact.map(info))(),
    // Then make it JSON
    format.json()
  )
  transports: [new transports.Console()]
}); 
fi0rini commented 3 years ago

This doesn't work for me for some reason, there is no output when adding the redact formatter

Adamlb commented 3 years ago

This doesn't work for me for some reason, there is no output when adding the redact formatter

It seems the traverse(info).map result is missing the Symbol(level) and Symbol(splat) keys from the info object.

re-adding them to the traverse result seems to make it work. I'm not sure on how to clean up the typing yet, but the following works

const sensitiveKeys = ['client_secret'];

const redact = format(info => {
  const result = traverse(info).map(function redactor() {
    if (this.key && sensitiveKeys.includes(this.key)) {
      this.update('[REDACTED]');
    }
  });

  const levelSym = Symbol.for('level');
  const splatSym = Symbol.for('splat');

  result[levelSym] = info[(levelSym as unknown) as string];
  result[splatSym] = info[(splatSym as unknown) as string];

  return result;
});

UPDATE: Found this issue in the traverse package https://github.com/substack/js-traverse/issues/65

romain-ortega commented 3 years ago

I ended up doing this, for those of you interested (winston: v3.3.3):

import traverse from "traverse";
import { klona } from "klona/full";

const sensitiveKeys = [
  /cookie/i,
  /passw(or)?d/i,
  /^pw$/,
  /^pass$/i,
  /secret/i,
  /token/i,
  /api[-._]?key/i,
];

function isSensitiveKey(keyStr) {
  if (keyStr) {
    return sensitiveKeys.some(regex => regex.test(keyStr));
  }
}

function redactObject(obj) {
  traverse(obj).forEach(function redactor() {
    if (isSensitiveKey(this.key)) {
      this.update("[REDACTED]");
    }
  });
}

function redact(obj) {
  const copy = klona(obj); // Making a deep copy to prevent side effects
  redactObject(copy);

  const splat = copy[Symbol.for("splat")];
  redactObject(splat); // Specifically redact splat Symbol

  return copy;
}
const logger = winston.createLogger({
  format: winston.format.combine(
    format(info => redact(info))(), // Prevent logging sensitive data
    ...
JuanOrtizOvied commented 11 months ago

@romain-ortega traverse replace de original obj