Skip to content

Commit 104f965

Browse files
committed
No longer parse cookies but ensure they are still redacted
1 parent adf9732 commit 104f965

File tree

12 files changed

+453
-250
lines changed

12 files changed

+453
-250
lines changed

docs/configuration.asciidoc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,7 @@ See https://www.owasp.org/index.php/Information_exposure_through_query_strings_i
469469
*`Cookie` header sanitization:*
470470

471471
The `Cookie` header is automatically redacted for incoming HTTP request transactions. Each name-value pair from the
472-
Cookie header is parsed by the agent and sent to the APM Server. Before the name-value pairs are recorded, they are
473-
sanitized based on the `SanitizeFieldNames` configuration. Cookies with sensitive data in
472+
cookie list is parsed by the agent and sanitized based on the `SanitizeFieldNames` configuration. Cookies with sensitive data in
474473
their value can be redacted by adding the cookie's name to the comma-separated list.
475474

476475
[options="header"]

src/Elastic.Apm/Api/Request.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ public class Request
2727
/// </summary>
2828
public Dictionary<string, string> Headers { get; set; }
2929

30-
/// <summary>
31-
/// This field is sanitized by a filter
32-
/// </summary>
33-
public Dictionary<string, string> Cookies { get; set; }
34-
3530
[JsonProperty("http_version")]
3631
[MaxLength]
3732
public string HttpVersion { get; set; }
@@ -47,8 +42,6 @@ internal Request DeepCopy()
4742
var newItem = (Request)MemberwiseClone();
4843
if (Headers != null)
4944
newItem.Headers = Headers.ToDictionary(entry => entry.Key, entry => entry.Value);
50-
if (Cookies != null)
51-
newItem.Cookies = Cookies.ToDictionary(entry => entry.Key, entry => entry.Value);
5245

5346
newItem.Socket = Socket?.DeepCopy();
5447
newItem.Url = Url?.DeepCopy();
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System;
6+
using System.Collections;
7+
using System.Collections.Generic;
8+
using Elastic.Apm.Api;
9+
using Elastic.Apm.Config;
10+
using Elastic.Apm.Helpers;
11+
using Elastic.Apm.Model;
12+
#if NET6_0_OR_GREATER
13+
using System.Buffers;
14+
#endif
15+
16+
namespace Elastic.Apm.Filters
17+
{
18+
/// <summary>
19+
/// Redacts items from the cookie list of the Cookie request header.
20+
/// </summary>
21+
internal class CookieHeaderRedactionFilter
22+
{
23+
private const string CookieHeader = "Cookie";
24+
25+
public static IError Filter(IError error)
26+
{
27+
if (error is Error e && e.Context is not null)
28+
HandleCookieHeader(e.Context?.Request?.Headers, e.Configuration.SanitizeFieldNames);
29+
return error;
30+
}
31+
32+
public static ITransaction Filter(ITransaction transaction)
33+
{
34+
if (transaction is Transaction { IsContextCreated: true })
35+
HandleCookieHeader(transaction.Context?.Request?.Headers, transaction.Configuration.SanitizeFieldNames);
36+
return transaction;
37+
}
38+
39+
// internal for testing
40+
internal static void HandleCookieHeader(Dictionary<string, string> headers, IReadOnlyList<WildcardMatcher> sanitizeFieldNames)
41+
{
42+
if (headers is not null)
43+
{
44+
// Realistically, this should be more than enough for all sensible scenarios
45+
// e.g. Cookies | cookies | COOKIES
46+
const int maxKeys = 4;
47+
48+
#if NET6_0_OR_GREATER
49+
var matchedKeys = ArrayPool<string>.Shared.Rent(maxKeys);
50+
var matchedValues = ArrayPool<string>.Shared.Rent(maxKeys);
51+
#else
52+
var matchedKeys = new string[maxKeys];
53+
var matchedValues = new string[maxKeys];
54+
#endif
55+
var matchedCount = 0;
56+
57+
foreach (var header in headers)
58+
{
59+
if (matchedCount == maxKeys)
60+
break;
61+
62+
if (header.Key.Equals(CookieHeader, StringComparison.OrdinalIgnoreCase))
63+
{
64+
matchedKeys[matchedCount] = header.Key;
65+
matchedValues[matchedCount] = CookieHeaderRedacter.Redact(header.Value, sanitizeFieldNames);
66+
matchedCount++;
67+
}
68+
}
69+
70+
var replacedCount = 0;
71+
72+
foreach (var headerKey in matchedKeys)
73+
{
74+
if (replacedCount == matchedCount)
75+
break;
76+
77+
if (headerKey is not null)
78+
{
79+
headers[headerKey] = matchedValues[replacedCount];
80+
replacedCount++;
81+
}
82+
}
83+
84+
#if NET6_0_OR_GREATER
85+
ArrayPool<string>.Shared.Return(matchedKeys);
86+
ArrayPool<string>.Shared.Return(matchedValues);
87+
#endif
88+
}
89+
}
90+
}
91+
}

src/Elastic.Apm/Filters/RequestCookieExtractionFilter.cs

Lines changed: 0 additions & 53 deletions
This file was deleted.

src/Elastic.Apm/Filters/Sanitization.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ public static void SanitizeHeadersInContext(Context context, IConfiguration conf
1818
if (context?.Request?.Headers is not null)
1919
RedactMatches(context?.Request?.Headers, configuration);
2020

21-
if (context?.Request?.Cookies is not null)
22-
RedactMatches(context?.Request?.Cookies, configuration);
23-
2421
if (context?.Response?.Headers is not null)
2522
RedactMatches(context?.Response?.Headers, configuration);
2623

src/Elastic.Apm/Helpers/CookieHeaderParser.cs

Lines changed: 0 additions & 110 deletions
This file was deleted.

0 commit comments

Comments
 (0)