Skip to content

Commit

Permalink
Filter sensitive headers using rewrite appender.
Browse files Browse the repository at this point in the history
  • Loading branch information
lukeelmers committed Feb 17, 2021
1 parent 6187036 commit 4372efa
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 80 deletions.
48 changes: 0 additions & 48 deletions src/core/server/http/logging/get_response_log.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,54 +148,6 @@ describe('getEcsResponseLog', () => {
expect(result.http.response.status_code).toBe(400);
});

describe('filters sensitive headers', () => {
test('redacts Authorization and Cookie headers by default', () => {
const req = createMockHapiRequest({
headers: { authorization: 'a', cookie: 'b', 'user-agent': 'hi' },
response: { headers: { 'content-length': 123, 'set-cookie': 'c' } },
});
const result = getEcsResponseLog(req, logger);
expect(result.http.request.headers).toMatchInlineSnapshot(`
Object {
"authorization": "[REDACTED]",
"cookie": "[REDACTED]",
"user-agent": "hi",
}
`);
expect(result.http.response.headers).toMatchInlineSnapshot(`
Object {
"content-length": 123,
"set-cookie": "[REDACTED]",
}
`);
});

test('does not mutate original headers', () => {
const reqHeaders = { authorization: 'a', cookie: 'b', 'user-agent': 'hi' };
const resHeaders = { headers: { 'content-length': 123, 'set-cookie': 'c' } };
const req = createMockHapiRequest({
headers: reqHeaders,
response: { headers: resHeaders },
});
getEcsResponseLog(req, logger);
expect(reqHeaders).toMatchInlineSnapshot(`
Object {
"authorization": "a",
"cookie": "b",
"user-agent": "hi",
}
`);
expect(resHeaders).toMatchInlineSnapshot(`
Object {
"headers": Object {
"content-length": 123,
"set-cookie": "c",
},
}
`);
});
});

describe('ecs', () => {
test('specifies correct ECS version', () => {
const req = createMockHapiRequest();
Expand Down
19 changes: 2 additions & 17 deletions src/core/server/http/logging/get_response_log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,6 @@ import { EcsEvent, Logger } from '../../logging';
import { getResponsePayloadBytes } from './get_payload_size';

const ECS_VERSION = '1.7.0';
const FORBIDDEN_HEADERS = ['authorization', 'cookie', 'set-cookie'];
const REDACTED_HEADER_TEXT = '[REDACTED]';

// We are excluding sensitive headers by default, until we have a log filtering mechanism.
function redactSensitiveHeaders(
headers?: Record<string, string | string[]>
): Record<string, string | string[]> {
const result = {} as Record<string, string | string[]>;
if (headers) {
for (const key of Object.keys(headers)) {
result[key] = FORBIDDEN_HEADERS.includes(key) ? REDACTED_HEADER_TEXT : headers[key];
}
}
return result;
}

/**
* Converts a hapi `Request` into ECS-compliant `LogMeta` for logging.
Expand Down Expand Up @@ -66,15 +51,15 @@ export function getEcsResponseLog(request: Request, log: Logger): LogMeta {
mime_type: request.mime,
referrer: request.info.referrer,
// @ts-expect-error Headers are not yet part of ECS: https://github.com/elastic/ecs/issues/232.
headers: redactSensitiveHeaders(request.headers),
headers: request.headers,
},
response: {
body: {
bytes,
},
status_code,
// @ts-expect-error Headers are not yet part of ECS: https://github.com/elastic/ecs/issues/232.
headers: redactSensitiveHeaders(responseHeaders),
headers: responseHeaders,
// responseTime is a custom non-ECS field
responseTime: !isNaN(responseTime) ? responseTime : undefined,
},
Expand Down
16 changes: 1 addition & 15 deletions src/core/server/logging/appenders/rewrite/policies/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,7 @@ export { RewritePolicy };
*/
export type RewritePolicyConfig = MetaRewritePolicyConfig;

const REDACTED_HEADER_TEXT = '[REDACTED]';

const defaultPolicy: MetaRewritePolicyConfig = {
type: 'meta',
mode: 'update',
properties: [
{ path: 'http.request.headers.authorization', value: REDACTED_HEADER_TEXT },
{ path: 'http.request.headers.cookie', value: REDACTED_HEADER_TEXT },
{ path: 'http.response.headers.set-cookie', value: REDACTED_HEADER_TEXT },
],
};

export const rewritePolicyConfigSchema = schema.oneOf([metaRewritePolicyConfigSchema], {
defaultValue: defaultPolicy,
});
export const rewritePolicyConfigSchema = schema.oneOf([metaRewritePolicyConfigSchema]);

export const createRewritePolicy = (config: RewritePolicyConfig): RewritePolicy => {
switch (config.type) {
Expand Down
31 changes: 31 additions & 0 deletions src/core/server/logging/logging_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ const ROOT_CONTEXT_NAME = 'root';
*/
const DEFAULT_APPENDER_NAME = 'default';

/**
* Sensitive `LogMeta` paths that we want to censor by default.
*/
const SENSITIVE_META_PATHS = [
'http.request.headers.authorization',
'http.request.headers.cookie',
'http.response.headers.set-cookie',
];

/**
* Replacement text to use when censoring SENSITIVE_META_PATHS.
*/
const REDACTED_META_TEXT = '[REDACTED]';

const levelSchema = schema.oneOf(
[
schema.literal('all'),
Expand Down Expand Up @@ -153,7 +167,24 @@ export class LoggingConfig {
} as AppenderConfigType,
],
[
// By default, we transparently rewrite `LogMeta` sent to the
// console appender to remove potentially sensitive keys.
// This can be overridden in a logger's configuration.
'console',
{
type: 'rewrite',
appenders: ['stdout'],
policy: {
type: 'meta',
mode: 'update',
properties: SENSITIVE_META_PATHS.map((path) => ({ path, value: REDACTED_META_TEXT })),
},
} as AppenderConfigType,
],
[
// This is our actual console appender which the rewrite appender
// sends its records to.
'stdout',
{
type: 'console',
layout: { type: 'pattern', highlight: true },
Expand Down

0 comments on commit 4372efa

Please sign in to comment.