From 4778ea0b65e1f22f3d85cfa8bad0e1b29f87b7d3 Mon Sep 17 00:00:00 2001 From: Justin Wind Date: Fri, 29 Oct 2021 13:41:40 -0700 Subject: [PATCH] obscure authorization header value when logging --- CHANGELOG.md | 4 ++++ lib/common.js | 36 +++++++++++++++++++++++++++++++++++- test/lib/common.js | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33468ed..8e0e95d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ Releases and notable changes to this project are documented here. ## [Unreleased] +### Added + +- obscure sensitive request header data when logging + ## [v1.2.1] - 2021-10-22 ### Added diff --git a/lib/common.js b/lib/common.js index ff68e9d..21070b8 100644 --- a/lib/common.js +++ b/lib/common.js @@ -158,19 +158,51 @@ const pick = (obj, props) => { /** * Return a subset of a request object, suitable for logging. + * Obscures sensitive header values. * @param {http.ClientRequest} req */ const requestLogData = (req) => { - return pick(req, [ + const data = pick(req, [ 'method', 'url', 'httpVersion', 'headers', 'trailers', ]); + scrubHeaderObject(data); + return data; }; +/** + * Remove sensitive header data. + * @param {Object} data + * @param {Object} data.headers + */ +const scrubHeaderObject = (data) => { + if (data && data.headers && 'authorization' in data.headers) { + data.headers = Object.assign({}, data.headers, { + authorization: obscureAuthorizationHeader(data.headers['authorization']), + }); + } +} + + +/** + * Hide sensitive part of an Authorization header. + * @param {String} authHeader + * @returns {String} + */ +const obscureAuthorizationHeader = (authHeader) => { + if (!authHeader) { + return authHeader; + } + const space = authHeader.indexOf(' '); + // This blurs entire string if no space found, because -1. + return authHeader.slice(0, space + 1) + '*'.repeat(authHeader.length - (space + 1)); +} + + /** * Return a subset of a response object, suitable for logging. * @param {http.ServerResponse} res @@ -272,10 +304,12 @@ module.exports = { mergeEnum, nop, nullLogger, + obscureAuthorizationHeader, pick, requestId, requestLogData, responseLogData, + scrubHeaderObject, setOptions, splitFirst, unfoldHeaderLines, diff --git a/test/lib/common.js b/test/lib/common.js index a04bf61..bacd6be 100644 --- a/test/lib/common.js +++ b/test/lib/common.js @@ -141,6 +141,46 @@ describe('common', function () { }); }); // requestLogData + describe('obscureAuthorizationHeader', function () { + it('obscures basic data', function () { + const authHeader = 'Basic Zm9vOmJhcg=='; + const expected = 'Basic ************'; + const result = common.obscureAuthorizationHeader(authHeader); + assert.strictEqual(result, expected); + }); + it('obscures all of other types', function () { + const authHeader = 'someWeirdAuth'; + const expected = '*************'; + const result = common.obscureAuthorizationHeader(authHeader); + assert.strictEqual(result, expected); + }); + it('does nothing when empty', function () { + const authHeader = undefined; + const expected = undefined; + const result = common.obscureAuthorizationHeader(authHeader); + assert.strictEqual(result, expected); + }); + }); // obscureAuthorizationHeader + + describe('scrubHeaderObject', function () { + it('', function () { + const data = { + headers: { + 'foo': 'bar', + 'authorization': 'Basic Zm9vOmJhcg==', + }, + }; + const expected = { + headers: { + 'foo': 'bar', + 'authorization': 'Basic ************', + }, + }; + common.scrubHeaderObject(data); + assert.deepStrictEqual(data, expected); + }); + }); // scrubHeaderObject + describe('responseLogData', function () { it('gives data', function () { const res = { -- 2.43.2