Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create rule S6781: JWT secret keys should not be disclosed #3838

Merged
merged 5 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions rules/S6781/csharp/how-to-fix/net-core.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
== 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.

[source,csharp,diff-id=101,diff-type=noncompliant]
----
[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.

[source,csharp]
----
[ApiController]
[Route("login-example")]
public class LoginExampleController : ControllerBase
{
private const string key = "SecretSecretSecretSecretSecretSecretSecretSecret";

[HttpPost]
public IActionResult Post([FromBody] LoginModel login)
loris-s-sonarsource marked this conversation as resolved.
Show resolved Hide resolved
{
// 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);
}
}
----

==== Compliant solution

[source,csharp,diff-id=101,diff-type=compliant]
----
[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.

[source,csharp]
----
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,
};
});
----
96 changes: 96 additions & 0 deletions rules/S6781/csharp/how-to-fix/net-framework.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
== 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.

[source,csharp,diff-id=201,diff-type=noncompliant]
----
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.

[source,csharp]
----
public class LoginExampleController : ApiController
{
private const string key = "SecretSecretSecretSecretSecretSecretSecretSecret";
loris-s-sonarsource marked this conversation as resolved.
Show resolved Hide resolved

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

[source,csharp,diff-id=201,diff-type=compliant]
----
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.
2 changes: 2 additions & 0 deletions rules/S6781/csharp/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
41 changes: 41 additions & 0 deletions rules/S6781/csharp/rule.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
include::../../../shared_content/secrets/description.adoc[]

== Why is this an issue?

include::../../../shared_content/secrets/rationale.adoc[]

=== What is the potential impact?

include::../impact.adoc[]

// How to fix it section

include::./how-to-fix/net-core.adoc[]

include::./how-to-fix/net-framework.adoc[]

== Resources

=== Documentation

* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.identitymodel.tokens.jwt.jwtsecuritytoken?view=msal-web-dotnet-latest[JwtSecurityToken Class Class]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.identitymodel.tokens.symmetricsecuritykey?view=dotnet-plat-ext-8.0[SymmetricSecurityKey Class]

include::../../../shared_content/secrets/resources/standards.adoc[]

ifdef::env-github,rspecator-view[]

'''
== Implementation Specification
(visible only on this page)

=== Message

JWT secret keys should not be disclosed.

=== Highlight

The call to create a new instance of `SymmetricSecurityKey`.

'''
endif::env-github,rspecator-view[]
12 changes: 12 additions & 0 deletions rules/S6781/impact.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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.
57 changes: 56 additions & 1 deletion rules/S6781/metadata.json
Original file line number Diff line number Diff line change
@@ -1,2 +1,57 @@
{
}
"title": "JWT secret keys should not be disclosed",
"type": "VULNERABILITY",
"code": {
"impacts": {
"SECURITY": "HIGH"
},
"attribute": "TRUSTWORTHY"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "30min"
},
"tags": [
"cwe",
"cert"
],
"extra": {
"replacementRules": [

]
},
"defaultSeverity": "Blocker",
"ruleSpecification": "RSPEC-6781",
"sqKey": "S6781",
"scope": "All",
"securityStandards": {
"CWE": [
798,
259
],
"OWASP": [
"A3"
],
"CERT": [
"MSC03-J."
],
"OWASP Top 10 2021": [
"A7"
],
"PCI DSS 3.2": [
"6.5.10"
],
"PCI DSS 4.0": [
"6.2.4"
],
"ASVS 4.0": [
"2.10.4",
"6.4.1"
]
},
"defaultQualityProfiles": [
"Sonar way"
],
"quickfix": "unknown"
}
Loading
Loading