Skip to content

Commit

Permalink
Update RSPEC for .NET
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastien-andrivet-sonarsource committed Mar 28, 2024
1 parent 9e53c95 commit 2ea3235
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 29 deletions.
115 changes: 115 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,115 @@
== How to fix it in ASP.NET Core

=== Code examples

The following noncompliant code contains a hard-coded secret that can be exposed unintentionally.

==== Noncompliant code example

[source,csharp,diff-id=1,diff-type=noncompliant]
----
[ApiController]
[Route("login-config")]
public class LoginConfigController : ControllerBase
{
private readonly IConfiguration _config;
public LoginConfigController(IConfiguration config)
{
_config = config;
}
[HttpPost]
public IActionResult Post([FromBody] UserModel user)
{
var key = _config["Jwt:Key"] ?? "";
// Code to validate user omitted
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)); // Noncompliant (key in appsettings.json)
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var Sectoken = new JwtSecurityToken(
"sonarsource.com",
"sonarsource.com",
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
return Ok(new JwtSecurityTokenHandler().WriteToken(Sectoken));
}
}
----

==== Compliant solution

[source,csharp,diff-id=1,diff-type=compliant]
----
[ApiController]
[Route("login-env")]
public class LoginEnvController : ControllerBase
{
private readonly IConfiguration _config;
public LoginEnvController(IConfiguration config)
{
_config = config;
}
[HttpPost]
public IActionResult Post([FromBody] UserModel user)
{
var key = Environment.GetEnvironmentVariable("JWT_KEY") ?? "";
// Code to validate user omitted
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var Sectoken = new JwtSecurityToken(
"sonarsource.com",
"sonarsource.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 environement variable for the secret.

=== Going the extra mile

==== Use a secret vault

A secret vault should be used to generate and store the new secret. This will ensure the secret's security and prevent any further unexpected disclosure. The recommended way for .NET Core applications is to use Azure Key Vault:

[source,csharp]
----
var builder = WebApplication.CreateBuilder(args);
// Get the name of the key vault
var keyVaultName = Environment.GetEnvironmentVariable("AZURE_KEYVAULT") ?? "";
// 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>() ?? "";
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "sonarsource.com",
ValidAudience = "sonarsource.com",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey!)) // Compliant: key from Azure Key Vault
};
});
----
70 changes: 70 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,70 @@
== How to fix it in ASP.NET

=== Code examples

The following noncompliant code contains a hard-coded secret that can be exposed unintentionally.

==== Noncompliant code example

[source,csharp,diff-id=2,diff-type=noncompliant]
----
public class ConstLoginController : ApiController
{
private const string key = "SecretSecretSecretSecretSecretSecretSecretSecret";
public IHttpActionResult Post([FromBody] Login login)
{
// Code to validate user omitted
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)); // Noncompliant: hard-coded key in code
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var secToken = new JwtSecurityToken(
"sonarsource.com",
"sonarsource.com",
null,
expires: DateTime.Now.AddMinutes(128),
signingCredentials: credentials
);
var token = new JwtSecurityTokenHandler().WriteToken(secToken);
return Ok(token);
}
}
----

==== Compliant solution

[source,csharp,diff-id=2,diff-type=compliant]
----
public class ConstLoginController : ApiController
{
private const string key = "SecretSecretSecretSecretSecretSecretSecretSecret";
public IHttpActionResult Post([FromBody] Login login)
{
// Code to validate user omitted
var key = Environment.GetEnvironmentVariable("JWT_KEY");
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var secToken = new JwtSecurityToken(
"sonarsource.com",
"sonarsource.com",
null,
expires: DateTime.Now.AddMinutes(128),
signingCredentials: credentials
);
var token = new JwtSecurityTokenHandler().WriteToken(secToken);
return Ok(token);
}
}
----

=== How does this work?

Here, the compliant solution uses an environement variable for the secret.

67 changes: 38 additions & 29 deletions rules/S6781/csharp/rule.adoc
Original file line number Diff line number Diff line change
@@ -1,44 +1,53 @@
FIXME: add a description

// If you want to factorize the description uncomment the following line and create the file.
//include::../description.adoc[]
include::../../../shared_content/secrets/description.adoc[]

== Why is this an issue?

FIXME: remove the unused optional headers (that are commented out)
include::../../../shared_content/secrets/rationale.adoc[]

=== 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 section

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

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

//=== What is the potential impact?
== Resources

== How to fix it
//== How to fix it in FRAMEWORK NAME
=== Documentation

=== Code examples
* 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]

==== Noncompliant code example

[source,text,diff-id=1,diff-type=noncompliant]
----
FIXME
----
include::../../../shared_content/secrets/resources/standards.adoc[]

==== Compliant solution
ifdef::env-github,rspecator-view[]

[source,text,diff-id=1,diff-type=compliant]
----
FIXME
----
'''
== Implementation Specification
(visible only on this page)

//=== How does this work?
=== Message

//=== Pitfalls
JWT secret keys should not be disclosed.

//=== Going the extra mile
=== Highlight

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

//== Resources
//=== Documentation
//=== Articles & blog posts
//=== Conference presentations
//=== Standards
//=== External coding guidelines
//=== Benchmarks
'''
endif::env-github,rspecator-view[]

0 comments on commit 2ea3235

Please sign in to comment.