-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9e53c95
commit 2ea3235
Showing
3 changed files
with
223 additions
and
29 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
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 | ||
}; | ||
}); | ||
---- |
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,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. | ||
|
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 |
---|---|---|
@@ -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[] |