Skip to main content

Business information leak - Analytics

Need

Protection of sensitive business information from unauthorized access

Context

  • Usage of C# 7.0 for modern language features and enhancements
  • Usage of Microsoft.AspNetCore.Authentication for implementing authentication in ASP.NET Core applications
  • Usage of Microsoft.AspNetCore.Authentication.JwtBearer for JWT-based authentication in ASP.NET Core
  • Usage of Microsoft.AspNetCore.Hosting for hosting ASP.NET Core applications
  • Usage of Microsoft.AspNetCore.Http for handling HTTP requests and responses in ASP.NET Core applications
  • Usage of Microsoft.AspNetCore.Mvc for building web applications with ASP.NET Core
  • Usage of Microsoft.AspNetCore.Mvc.Versioning for API versioning in ASP.NET Core MVC
  • Usage of Microsoft.Extensions.Configuration for managing application configuration
  • Usage of Microsoft.Extensions.DependencyInjection for managing dependency injection in a .NET application
  • Usage of Microsoft.Extensions.Hosting for building and hosting .NET Core applications
  • Usage of Microsoft.Extensions.Options for managing and accessing configuration options in a .NET application
  • Usage of Microsoft.OpenApi.Models for working with OpenAPI specifications in .NET applications
  • Usage of Newtonsoft.Json for JSON serialization and deserialization

Description

Non compliant code

public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
});

services.AddSwaggerGenNewtonsoftSupport();

// Configuration for API Key
services.AddAuthentication()
.AddScheme<AuthenticationSchemeOptions, ApiKeyAuthenticationHandler>("ApiKey", options => { });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});

app.UseAuthentication();
}
}

public class ApiKeyAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue("ApiKey", out var apiKeyHeaderValues))
return Task.FromResult(AuthenticateResult.Fail("ApiKey was not provided."));

var providedApiKey = apiKeyHeaderValues.FirstOrDefault();

if (providedApiKey == null)
return Task.FromResult(AuthenticateResult.Fail("ApiKey was not provided."));

if (providedApiKey != "Your_Secret_ApiKey")
return Task.FromResult(AuthenticateResult.Fail("Invalid ApiKey provided."));

var claims = new List<Claim> { new Claim(ClaimTypes.Name, "ApiKeyUser") };
var identity = new ClaimsIdentity(claims, "ApiKey");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "ApiKey");

return Task.FromResult(AuthenticateResult.Success(ticket));
}
}

The code above represents an ASP.NET Core application that uses Swagger for API documentation. The API Key is being used for authentication.

The vulnerability lies in the way the API key is being handled. The API Key is hardcoded in the HandleAuthenticateAsync method of the ApiKeyAuthenticationHandler class. This is a bad practice because anyone who has access to the codebase can see the API Key. Furthermore, the Swagger UI is publicly accessible as the UseSwaggerUI middleware is used without any restriction.

This allows anyone with the hardcoded API Key to access the Swagger UI and potentially gain insights from the analytics data exposed by the API. This can lead to a business information leak, as sensitive data might be exposed.

Steps

  • Remove the Swagger URL that exposes the business insights from mobile app usage analytics.
  • Do not include the APIKey in the Swagger documentation or any other publicly accessible documentation.
  • Implement proper access control mechanisms to restrict access to the business insights.
  • Consider using a secure and encrypted channel (e.g., HTTPS) for transmitting the APIKey.
  • Ensure that the APIKey is securely stored and not hardcoded in the code.
  • Regularly rotate the APIKey to minimize the risk of unauthorized access.
  • Implement rate limiting and throttling mechanisms to prevent abuse and excessive usage of the API.
  • Monitor and log API usage to detect any suspicious or unauthorized access attempts.

Compliant code

public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication()
.AddScheme<AuthenticationSchemeOptions, ApiKeyAuthenticationHandler>("ApiKey", options => { });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
}
}

public class ApiKeyAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly IConfiguration _configuration;

public ApiKeyAuthenticationHandler(IConfiguration configuration)
{
_configuration = configuration;
}

protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue("ApiKey", out var apiKeyHeaderValues))
return Task.FromResult(AuthenticateResult.Fail("ApiKey was not provided."));

var providedApiKey = apiKeyHeaderValues.FirstOrDefault();

if (providedApiKey == null)
return Task.FromResult(AuthenticateResult.Fail("ApiKey was not provided."));

var storedApiKey = _configuration["StoredApiKey"];

if (providedApiKey != storedApiKey)
return Task.FromResult(AuthenticateResult.Fail("Invalid ApiKey provided."));

var claims = new List<Claim> { new Claim(ClaimTypes.Name, "ApiKeyUser") };
var identity = new ClaimsIdentity(claims, "ApiKey");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "ApiKey");

return Task.FromResult(AuthenticateResult.Success(ticket));
}
}

The updated code removes the Swagger documentation that was previously exposing the APIKey and business insights. The APIKey is no longer hardcoded in the code, instead it is retrieved from a secure configuration source (e.g., environment variables, secure configuration files, or a secure key vault).

The ApiKeyAuthenticationHandler now includes a constructor that takes an IConfiguration object, which is used to retrieve the stored APIKey. This ensures that the APIKey is securely stored and not exposed in the code.

The HandleAuthenticateAsync method checks if the provided APIKey matches the stored APIKey. If the APIKeys do not match, it returns an authentication failure.

This code should be used in conjunction with other security measures such as using a secure and encrypted channel (e.g., HTTPS) for transmitting the APIKey, implementing proper access control mechanisms, regularly rotating the APIKey, implementing rate limiting and throttling mechanisms, and monitoring and logging API usage.

References