org.sonar.plugins.csharp.S6781.html Maven / Gradle / Ivy
Secret leaks often occur when a sensitive piece of authentication data is stored with the source code of an application. Considering the source
code is intended to be deployed across multiple assets, including source code repositories or application hosting servers, the secrets might get
exposed to an unintended audience.
Why is this an issue?
In most cases, trust boundaries are violated when a secret is exposed in a source code repository or an uncontrolled deployment environment.
Unintended people who don’t need to know the secret might get access to it. They might then be able to use it to gain unwanted access to associated
services or resources.
The trust issue can be more or less severe depending on the people’s role and entitlement.
What is the potential impact?
If a JWT secret key leaks to an unintended audience, it can have serious security implications for the corresponding application. The secret key is
used to encode and decode JWTs when using a symmetric signing algorithm, and an attacker could potentially use it to perform malicious actions.
For example, an attacker could use the secret key to create their own authentication tokens that appear to be legitimate, allowing them to bypass
authentication and gain access to sensitive data or functionality.
In the worst-case scenario, an attacker could be able to execute arbitrary code on the application by abusing administrative features, and take
over its hosting server.
How to fix it in ASP.NET Core
Code examples
Noncompliant code example
Secrets stored in appsettings.json
can be read by anyone with access to the file.
[ApiController]
[Route("login-example")]
public class LoginExampleController : ControllerBase
{
private readonly IConfiguration _config;
public LoginExampleController(IConfiguration config)
{
_config = config;
}
[HttpPost]
public IActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var key = _config["Jwt:Key"] ??
throw new ApplicationException("JWT key is not configured.");
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)); // Noncompliant
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var Sectoken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
var token = new JwtSecurityTokenHandler().WriteToken(Sectoken);
return Ok(token);
}
}
Secrets that are hard-coded into the application can be read by anyone with access to the source code or can be decompiled from the application
binaries.
[ApiController]
[Route("login-example")]
public class LoginExampleController : ControllerBase
{
private const string key = "SecretSecretSecretSecretSecretSecretSecretSecret";
[HttpPost]
public IActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)); // Noncompliant
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var Sectoken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
var token = new JwtSecurityTokenHandler().WriteToken(Sectoken);
return Ok(token);
}
}
Compliant solution
[ApiController]
[Route("login-example")]
public class LoginExampleController : ControllerBase
{
[HttpPost]
public IActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var key = Environment.GetEnvironmentVariable("JWT_KEY") ??
throw new ApplicationException("JWT key is not configured.");
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var Sectoken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
var token = new JwtSecurityTokenHandler().WriteToken(Sectoken);
return Ok(token);
}
}
How does this work?
Here, the compliant solution uses an environment variable to hold the secret. Environment variables are easy to change and are not easily
accessible outside of the application.
Going the extra mile
Use a secret vault
Secret vaults provide secure methods for storing and accessing secrets. They protect against the unexpected disclosure of the secrets they
store.
Microsoft recommends using Azure Key Vault with .NET Core applications.
var builder = WebApplication.CreateBuilder(args);
// Get the name of the key vault
var keyVaultName = Environment.GetEnvironmentVariable("AZURE_KEYVAULT") ??
throw new ApplicationException("Azure Key Vault location is not configured.");
// Add Azure Key Vault in the configuration
builder.Configuration.AddAzureKeyVault(new Uri($"https://{keyVaultName}.vault.azure.net/"), new EnvironmentCredential());
// Get the JWT secret from Azure Key Vault
var jwtKey = builder.Configuration.GetSection("JWT-KEY").Get<string>() ??
throw new ApplicationException("JWT key is not configured.");
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters{
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey!)),
ValidateIssuerSigningKey = true,
ValidIssuer = "example.com",
ValidateIssuer = true,
ValidAudience = "example.com",
ValidateAudience = true,
ValidateLifetime = true,
};
});
How to fix it in ASP.NET
Code examples
Noncompliant code example
Secrets stored in web.config
can be read by anyone with access to the file.
public class LoginExampleController : ApiController
{
public IHttpActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var key = ConfigurationManager.AppSettings["key"] ??
throw new ApplicationException("JWT key is not configured.");
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var secToken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials
);
var token = new JwtSecurityTokenHandler().WriteToken(secToken);
return Ok(token);
}
}
Secrets that are hard-coded into the application can be read by anyone with access to the source code or can be decompiled from the application
binaries.
public class LoginExampleController : ApiController
{
private const string key = "SecretSecretSecretSecretSecretSecretSecretSecret";
public IHttpActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var secToken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials
);
var token = new JwtSecurityTokenHandler().WriteToken(secToken);
return Ok(token);
}
}
Compliant solution
public class LoginExampleController : ApiController
{
public IHttpActionResult Post([FromBody] LoginModel login)
{
// Code to validate the login information is omitted
var key = Environment.GetEnvironmentVariable("JWT_KEY") ??
throw new ApplicationException("JWT key is not configured.");
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var secToken = new JwtSecurityToken(
"example.com",
"example.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials
);
var token = new JwtSecurityTokenHandler().WriteToken(secToken);
return Ok(token);
}
}
How does this work?
Here, the compliant solution uses an environment variable to hold the secret. Environment variables are easy to change and are not easily
accessible outside of the application.
Resources
Documentation
- Microsoft Learn - JwtSecurityToken
Class Class
- Microsoft Learn - SymmetricSecurityKey
Class
Standards
- OWASP - Top 10 2021 Category A7 - Identification and
Authentication Failures
- OWASP - Top 10 2017 Category A3 - Sensitive Data
Exposure
- CWE - CWE-798 - Use of Hard-coded Credentials
- CWE - CWE-259 - Use of Hard-coded Password
- STIG Viewer - Application Security and
Development: V-222642 - The application must not contain embedded authentication data.