Review secrets passed to logging methods
Matches methods that call a logging API (ILogger / LoggerExtensions, Serilog, NLog, log4net, Trace, Debug, Console) and, in the same body, read a string field or property getter named like a password, API key, private key, token, client secret, connection string or credential. Logs are stored longer, replicated more widely and protected less than the secret store; a connection string or bearer token in a log line is one of the most frequent real-world credential leaks.
This is CWE-532 Insertion of Sensitive Information into Log File (review: the rule cannot tell whether the value is part of the message).
Flagged Code
This is CWE-532 Insertion of Sensitive Information into Log File (review: the rule cannot tell whether the value is part of the message).
Flagged Code
private readonly string _connectionString;
void OpenDatabase(ILogger logger) {
logger.LogInformation("Opening {Db}", _connectionString); // flagged: secret logged
using var connection = new SqlConnection(_connectionString);
connection.Open();
}
Fixed Codeprivate readonly string _connectionString;
void OpenDatabase(ILogger logger) {
logger.LogInformation("Opening database {Db}", DatabaseName); // non-secret value
using var connection = CreateConnection();
connection.Open();
}
SqlConnection CreateConnection() => new SqlConnection(_connectionString); // logs nothingHow to fix
Never pass the secret to the logger: log its presence or a redacted form (last 4 characters, a hash), and use a destructuring policy that masks sensitive properties when logging whole objects. Suppress the issue when the secret is read for another purpose in the method and demonstrably not part of the log call.
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Review secrets passed to logging methods</Name>
// <Id>ND3127:ReviewSecretsPassedToLoggingMethods</Id>
warnif count > 0
// Third-party members are part of the code model only when the application uses them.
let loggingMembers = ThirdParty.Methods.Where(m =>
(m.ParentType.FullNameEqualsAny(
"Microsoft.Extensions.Logging.LoggerExtensions", "Microsoft.Extensions.Logging.ILogger",
"Serilog.ILogger", "Serilog.Log", "NLog.Logger", "NLog.ILogger", "log4net.ILog", "Common.Logging.ILog",
"System.Diagnostics.Trace", "System.Diagnostics.Debug", "System.Diagnostics.TraceSource", "System.Diagnostics.EventLog") &&
m.SimpleName.StartsWithAny("Log", "Write", "Trace", "Debug", "Info", "Warn", "Error", "Fatal", "Verbose", "Critical")) ||
(m.ParentType.FullName == "System.Console" && m.SimpleName.StartsWith("Write")))
.ToHashSetEx()
where loggingMembers.Any()
// Fields and property getters whose name designates a secret (hashed forms and setting names excluded).
let isSecretName = new Func<string, bool>(n =>
n.ToLower().ContainsAny("password", "passwd", "pwd", "secret", "apikey", "api_key", "privatekey", "private_key",
"accesstoken", "access_token", "authtoken", "auth_token", "refreshtoken", "refresh_token",
"clientsecret", "client_secret", "connectionstring", "connection_string", "credential") &&
!n.ToLower().ContainsAny("hash", "salt", "length", "policy", "regex", "pattern", "name", "header", "expir", "required", "attempt", "changed", "reset", "enabled", "type", "kind", "count", "provider"))
from m in Application.Methods.UsingAny(loggingMembers)
let secretFieldsRead = m.FieldsUsed.Where(f => !f.IsGeneratedByCompiler && f.FieldType != null && f.FieldType.FullName == "System.String" && isSecretName(f.SimpleName)).ToArray()
let secretGettersCalled = m.MethodsCalled.Where(mc => mc.IsPropertyGetter && mc.ReturnType != null && mc.ReturnType.FullName == "System.String" && isSecretName(mc.SimpleName)).ToArray()
where secretFieldsRead.Length > 0 || secretGettersCalled.Length > 0
select new {
m,
secretFieldsRead,
secretGettersCalled,
loggingMembersUsed = m.MethodsCalled.Intersect(loggingMembers).ToArray(),
Debt = 15.ToMinutes().ToDebt(),
Severity = Severity.Medium
}
//<Expl>
// The *{0}* both logs (through *{3}*) and reads the secret-named value(s)
// {1}{2}. If the secret ends up in the log message, it is exposed to
// everyone who can read the logs, log aggregators and support bundles.
//</Expl>
//<Desc>
// Matches methods that call a logging API (*ILogger* / *LoggerExtensions*,
// Serilog, NLog, log4net, *Trace*, *Debug*, *Console*) and, in the same
// body, read a string field or property getter named like a password, API
// key, private key, token, client secret, connection string or credential.
// Logs are stored longer, replicated more widely and protected less than
// the secret store; a connection string or bearer token in a log line is
// one of the most frequent real-world credential leaks.
//
// This is CWE-532 *Insertion of Sensitive Information into Log File*
// (review: the rule cannot tell whether the value is part of the message).
//
// **Flagged Code**
// ```csharp
// private readonly string _connectionString;
//
// void OpenDatabase(ILogger logger) {
// logger.LogInformation("Opening {Db}", _connectionString); // flagged: secret logged
// using var connection = new SqlConnection(_connectionString);
// connection.Open();
// }
// ```
//
// **Fixed Code**
// ```csharp
// private readonly string _connectionString;
//
// void OpenDatabase(ILogger logger) {
// logger.LogInformation("Opening database {Db}", DatabaseName); // non-secret value
// using var connection = CreateConnection();
// connection.Open();
// }
//
// SqlConnection CreateConnection() => new SqlConnection(_connectionString); // logs nothing
// ```
//</Desc>
//<HowToFix>
// Never pass the secret to the logger: log its presence or a redacted
// form (last 4 characters, a hash), and use a destructuring policy that
// masks sensitive properties when logging whole objects. Suppress the
// issue when the secret is read for another purpose in the method and
// demonstrably not part of the log call.
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
Security: all 39 items
- Software Composition Analysis (SCA) ND3120
- Transitive Software Composition Analysis (SCA) ND3121
- Don't hard-code secrets in constants ND3114
- Review credentials built from hard-coded values ND3115
- Don't use insecure deserializers ND3110
- Don't use weak hash algorithms (MD5, SHA1) ND3109
- Don't use DES/3DES/RC2 weak cipher algorithms ND3102
- Don't use weak password-based key derivation ND3123
- Don't use the ECB encryption mode ND3132
- Don't disable certificate validation ND3103
- Don't hard-code TLS/SSL protocol versions ND3122
- Review commands vulnerable to SQL injection ND3107
- Review data adapters vulnerable to SQL injection ND3108
- Review raw SQL built from composed strings ND3128
- Review LDAP filters composed from strings ND3137
- Don't enable external XML entity resolution (XXE) ND3111
- Review processes started with a composed arguments string ND3124
- Review web endpoints building file paths from their parameters ND3129
- Review custom archive extraction (Zip Slip) ND3135
- Review assemblies and types loaded from runtime-provided names ND3125
- Review web endpoints without [Authorize] nor [AllowAnonymous] ND3118
- Review state-changing endpoints opened to anonymous callers ND3130
- Review state-changing MVC actions without anti-forgery validation ND3119
- Review HTML-encoding bypasses ND3133
- Review explicitly disabled request validation ND3134
- Review redirects to a URL that is not validated as local ND3126
- Review request-influenced outbound HTTP calls (SSRF) ND3136
- Review insecure ASP.NET Core CORS policies and unguarded developer exception page ND3116
- Review cookies created without HttpOnly and Secure flags ND3117
- Review regular expressions without a match timeout (ReDoS) ND3112
- Review secrets compared with non-constant-time equality ND3113
- Review secrets passed to logging methods ND3127
- Review security operations that log nothing ND3131
- Don't use System.Random for security purposes ND3101
- Don't use CoSetProxyBlanket and CoInitializeSecurity ND3100
- Review publicly visible event handlers ND3104
- Pointers should not be publicly visible ND3105
- Seal methods that satisfy non-public interfaces ND3106
- Restrict sensitive APIs to reviewed components ND3138