-
Notifications
You must be signed in to change notification settings - Fork 206
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fully implement the sanitization spec
- Loading branch information
1 parent
2e1793a
commit 9b67e9e
Showing
25 changed files
with
434 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Licensed to Elasticsearch B.V under | ||
// one or more agreements. | ||
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
// See the LICENSE file in the project root for more information | ||
|
||
using Elastic.Apm.Api; | ||
using Elastic.Apm.Helpers; | ||
using Elastic.Apm.Model; | ||
|
||
namespace Elastic.Apm.Filters | ||
{ | ||
/// <summary> | ||
/// Extracts cookies from the Cookie request header and sets the Cookie header to [REDACTED]. | ||
/// </summary> | ||
internal class RequestCookieExtractionFilter | ||
{ | ||
private static readonly WildcardMatcher[] CookieMatcher = new WildcardMatcher[] { new WildcardMatcher.VerbatimMatcher("Cookie", true) }; | ||
|
||
public static IError Filter(IError error) | ||
{ | ||
if (error is Error realError) | ||
HandleCookieHeader(realError.Context); | ||
return error; | ||
} | ||
|
||
public static ITransaction Filter(ITransaction transaction) | ||
{ | ||
if (transaction is Transaction { IsContextCreated: true }) | ||
HandleCookieHeader(transaction.Context); | ||
return transaction; | ||
} | ||
|
||
private static void HandleCookieHeader(Context context) | ||
{ | ||
if (context?.Request?.Headers is not null) | ||
{ | ||
string matchedKey = null; | ||
foreach (var key in context.Request.Headers.Keys) | ||
{ | ||
if (WildcardMatcher.IsAnyMatch(CookieMatcher, key)) | ||
{ | ||
var cookies = context.Request.Headers[key]; | ||
context.Request.Cookies = CookieHeaderParser.ParseCookies(cookies); | ||
matchedKey = key; | ||
} | ||
} | ||
|
||
if (matchedKey is not null) | ||
context.Request.Headers[matchedKey] = Consts.Redacted; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// Licensed to Elasticsearch B.V under | ||
// one or more agreements. | ||
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
// See the LICENSE file in the project root for more information | ||
|
||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Elastic.Apm.Api; | ||
using Elastic.Apm.Config; | ||
using Elastic.Apm.Helpers; | ||
|
||
namespace Elastic.Apm.Filters | ||
{ | ||
internal static class Sanitization | ||
{ | ||
public static void SanitizeHeadersInContext(Context context, IConfiguration configuration) | ||
{ | ||
if (context?.Request?.Headers is not null) | ||
RedactMatches(context?.Request?.Headers, configuration); | ||
|
||
if (context?.Request?.Cookies is not null) | ||
RedactMatches(context?.Request?.Cookies, configuration); | ||
|
||
if (context?.Response?.Headers is not null) | ||
RedactMatches(context?.Response?.Headers, configuration); | ||
|
||
if (context?.Message?.Headers is not null) | ||
RedactMatches(context?.Message?.Headers, configuration); | ||
|
||
static void RedactMatches(Dictionary<string, string> dictionary, IConfiguration configuration) | ||
{ | ||
foreach (var key in dictionary.Keys.ToArray()) | ||
{ | ||
if (WildcardMatcher.IsAnyMatch(configuration.SanitizeFieldNames, key)) | ||
dictionary[key] = Consts.Redacted; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// one or more agreements. | ||
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
// See the LICENSE file in the project root for more information | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Elastic.Apm.Helpers; | ||
|
||
internal static class CookieHeaderParser | ||
{ | ||
public static Dictionary<string, string> ParseCookies(string cookieHeader) | ||
{ | ||
// Implementation notes: | ||
// This method handles a cookie header value for both ASP.NET (classic) and | ||
// ASP.NET Core. As a result it must handle two possible formats. In ASP.NET | ||
// (classic) the string is the actual Cookie value as sent over the wire, conforming | ||
// to the HTTP standards. This uses the semicolon separator and a space between | ||
// entries. For ASP.NET Core, when we parse the headers, we convert from the | ||
// StringValues by calling ToString. This results in each entry being separated | ||
// by a regular colon and no space. | ||
|
||
if (string.IsNullOrEmpty(cookieHeader)) | ||
return null; | ||
|
||
var cookies = new Dictionary<string, string>(); | ||
|
||
#if NETFRAMEWORK | ||
// The use of `Span<T>` in NETFX can cause runtime assembly loading issues due to our friend, | ||
// binding redirects. For now, we take a slightly less allocation efficient route here, rather | ||
// than risk introducing runtime issues for consumers. Technically, this should be "fixed" in | ||
// NET472+, but during testing I surprisingly still reproduced an exception. Elastic.APM depends on | ||
// `System.Diagnostics.DiagnosticSource 5.0.0` which itself depends on `System.Runtime.CompilerServices.Unsafe` | ||
// which is where the exception occurs. 5.0.0 is marked as deprecated so we could look to prefer | ||
// a new version but we have special handling for the ElasticApmAgentStartupHook | ||
// zip file version. For now, we decided not to mess with this as it's hard to test all scenarios. | ||
|
||
var cookieValues = cookieHeader.Split(',', ';'); | ||
|
||
foreach (var cookieValue in cookieValues) | ||
{ | ||
var trimmed = cookieValue.Trim(); | ||
var parts = trimmed.Split('='); | ||
|
||
if (parts.Length == 2 && !string.IsNullOrEmpty(parts[0]) && !string.IsNullOrEmpty(parts[1])) | ||
{ | ||
cookies.Add(parts[0], parts[1]); | ||
} | ||
} | ||
|
||
return cookies; | ||
#else | ||
var span = cookieHeader.AsSpan(); | ||
|
||
while (span.Length > 0) | ||
{ | ||
var foundComma = true; | ||
var separatorIndex = span.IndexOfAny(',', ';'); | ||
|
||
if (separatorIndex == -1) | ||
{ | ||
foundComma = false; | ||
separatorIndex = span.Length; | ||
} | ||
|
||
var entry = span.Slice(0, separatorIndex); | ||
|
||
var equalsIndex = entry.IndexOf('='); | ||
|
||
if (equalsIndex > -1) | ||
{ | ||
var key = entry.Slice(0, equalsIndex); | ||
var value = entry.Slice(equalsIndex + 1); | ||
|
||
var keyString = key.ToString(); | ||
var valueString = value.ToString(); | ||
|
||
if (!string.IsNullOrEmpty(keyString) && !string.IsNullOrEmpty(valueString)) | ||
cookies.Add(keyString, valueString); | ||
} | ||
|
||
span = span.Slice(foundComma ? separatorIndex + 1 : span.Length); | ||
|
||
// skip any white space between the separator and the next entry | ||
while (span.Length > 0) | ||
{ | ||
if (span[0] != ' ') | ||
break; | ||
|
||
span = span.Slice(1); | ||
} | ||
} | ||
|
||
return cookies; | ||
#endif | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
src/integrations/Elastic.Apm.AspNetCore/WebRequestTransactionCreator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.