所有文章 > API安全 > C#中的API安全实践开发指南

C#中的API安全实践开发指南

概述:强大的 API 安全性的重要性怎么强调都不为过。在这个网络威胁猖獗的时代,保护我们的 API 端点不仅是必需品,也是我们的责任。让我们剖析这些关键的安全措施,并巧妙地实施它们。让我们讨论以下 12 个主题,以使我们的 API 更安全:使用 HTTPS 使用 OAuth2 使用速率限制 使用 API 版本控制 输入验证 使用分级 API 密钥 授权 白名单 OWASP API 安全风险 使用 API 网关 错误处理 输入验证 使用 HTTPS问题陈述:您的 API 通过 Internet 传输敏感数据,并且当前使用不安全的 HTTP。

强大的 API 安全性的重要性怎么强调都不为过。在这个网络威胁猖獗的时代,保护我们的 API 端点不仅是必需品,也是我们的责任。让我们剖析这些关键的安全措施,并巧妙地实施它们。

让我们讨论以下 12 个主题,以使我们的 API 更安全:

  1. 使用 HTTPS
  2. 使用 OAuth2
  3. 使用速率限制
  4. 使用 API 版本控制
  5. 输入验证
  6. 使用分级 API 密钥
  7. 授权
  8. 白名单
  9. OWASP API 安全风险
  10. 使用 API 网关
  11. 错误处理
  12. 输入验证

1、使用 HTTPS

问题陈述:您的 API 通过 Internet 传输敏感数据,并且当前使用不安全的 HTTP。如何保护传输中的数据?

解决方案:实现HTTPS对客户端和服务器之间的通信进行加密。

C# 示例:

public class SecureApiController : ApiController  
{
// Use attribute to enforce HTTPS
[RequireHttps]
public HttpResponseMessage GetSensitiveData()
{
// Fetch sensitive data logic
var sensitiveData = new { /* ... */ };
return Request.CreateResponse(HttpStatusCode.OK, sensitiveData);
}
}

// Custom attribute to enforce HTTPS
public class RequireHttpsAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
{
ReasonPhrase = "HTTPS Required"
};
}
else
{
base.OnAuthorization(actionContext);
}
}
}

始终使用 HTTPS 来保护客户端和服务器之间的通信。在 ASP.NET Core 中,可以在以下位置强制执行 HTTPS:Startup.cs

public void ConfigureServices(IServiceCollection services)  
{
services.AddHttpsRedirection(options =>
{
options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect;
options.HttpsPort = 443;
});
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
}

2、使用 OAuth2

问题陈述:您的 API 需要保护提供个人用户数据的资源服务器。您需要确保只有经过身份验证和授权的客户端才能访问此数据。

解决方案:实现 OAuth2(一种授权协议),以向客户端提供安全的受限访问令牌。

C# 示例:

// OAuth2 configuration in Startup.cs  
public void ConfigureAuth(IAppBuilder app)
{
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/Authorize"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};

// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}

实现 OAuth 2.0 授权框架。它支持安全的委托访问,允许客户端获取有限的访问令牌来验证 API 请求。在 ASP.NET Core 中,可以使用 Microsoft Identity 平台:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)  
.AddMicrosoftIdentityWebApi(Configuration, "AzureAd");

services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdminRole", policy =>
{
policy.RequireRole("Admin");
});
});

3、使用速率限制

问题陈述:您的 API 流量过大,导致性能下降。您需要实现速率限制来控制流量。

解决方案:使用中间件根据 IP、用户或操作组强制实施速率限制规则。

C# 示例:

// Middleware for rate limiting  
public class RateLimitingMiddleware : OwinMiddleware
{
public RateLimitingMiddleware(OwinMiddleware next) : base(next) { }

public override async Task Invoke(IOwinContext context)
{
if (RateLimitReached(context))
{
context.Response.StatusCode = (int)HttpStatusCode.TooManyRequests;
return;
}

await Next.Invoke(context);
}

private bool RateLimitReached(IOwinContext context)
{
// Implement your rate limiting logic here based on the context
// For instance, check the IP address and limit the number of requests per minute
return false;
}
}

实施速率限制以限制客户端在给定时间窗口内可以发出的请求数。您可以根据客户端 IP、用户 ID、API 路由等各种因素定义速率限制。下面是使用 AspNetCoreRateLimit 的示例:

public void ConfigureServices(IServiceCollection services)  
{
services.AddOptions();
services.AddMemoryCache();
services.Configure\<ClientRateLimitOptions>(options =>
{
options.GeneralRules = new List\<RateLimitRule>
{
new RateLimitRule
{
Endpoint = "\*",
Period = "1m",
Limit = 30,
}
};
});
services.AddSingleton<IClientPolicyStore, MemoryCacheClientPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
}

public void Configure(IApplicationBuilder app)
{
app.UseClientRateLimiting();
}

4、使用 API 版本控制

问题陈述:您的 API 需要在不破坏现有客户端的情况下进行发展。如何在保持向后兼容性的同时引入新功能?

解决方案:在 API 路由中实现版本控制,以允许客户端指定它们设计用于使用的版本。

C# 示例:

// Web API Route configuration  
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "VersionedApi",
routeTemplate: "api/v{version}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}

public class UsersController : ApiController
{
[HttpGet]
public string GetV1(int id)
{
// Version 1 specific processing
return "Data from version 1";
}

[HttpGet, Route("api/v2/users/{id}")]
public string GetV2(int id)
{
// Version 2 specific processing
return "Data from version 2";
}
}

实施 API 版本控制以保持向后兼容性。在 API 路由中包含版本指示符(如“v1”),也可以在请求/响应标头中包含版本指示符。ASP.NET Core 通过软件包支持此功能:Microsoft.AspNetCore.Mvc.Versioning

services.AddApiVersioning(options =>  
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new UrlSegmentApiVersionReader();
});

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class UsersController : ControllerBase
{
// Controller implementation
}

5、输入验证

问题:在未经适当验证的情况下接受来自客户端的不受信任的输入可能会引入 SQL 注入或跨站点脚本 (XSS) 等安全漏洞。

解决方案:始终在服务器端验证和清理输入。使用数据注释和属性进行基本验证:[ApiController]

public class LoginModel  
{
[Required]
[EmailAddress]
public string Email { get; set; }

[Required]
[StringLength(100, MinimumLength = 6)]
public string Password { get; set; }
}

[HttpPost("login")]
public IActionResult Login([FromBody] LoginModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

// Authenticate user
}

API 网关级别实现输入验证,以确保仅处理有效请求。

public class ValidateModelAttribute : ActionFilterAttribute  
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}

// Usage in a Controller
public class MyModel
{
[Required]
public string Property1 { get; set; }

// Other properties and validation attributes
}

public class MyApiController : ApiController
{
[ValidateModel]
public IHttpActionResult Post(MyModel model)
{
// Proceed knowing the model is valid
ProcessData(model);
return Ok();
}

private void ProcessData(MyModel model)
{
// Processing logic
}
}

6、使用分级 API 密钥

问题:对所有客户端使用单个 API 密钥无法提供精细控制,也无法根据需要撤销对特定客户端的访问权限。

解决方案:实现具有不同访问权限的分级 API 密钥系统。每个客户端都获得与特定角色或范围关联的唯一密钥。

public class ApiKey  
{
public int Id { get; set; }
public string Key { get; set; }
public string ClientName { get; set; }
public List<string> Scopes { get; set; }
}

public class AuthorizationMiddleware
{
private readonly RequestDelegate _next;

public AuthorizationMiddleware(RequestDelegate next)
{
_next = next;
}

public async Task Invoke(HttpContext context, IApiKeyRepository apiKeyRepository)
{
string apiKey = context.Request.Headers["X-API-KEY"];

if (apiKey == null)
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("API key is missing.");
return;
}

ApiKey key = await apiKeyRepository.GetApiKey(apiKey);

if (key == null)
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Invalid API key.");
return;
}

if (!key.Scopes.Contains(context.Request.Path.ToString()))
{
context.Response.StatusCode = 403;
await context.Response.WriteAsync("Not authorized to access this resource.");
return;
}

await _next(context);
}
}

实现具有不同访问权限的分级 API 密钥。

public class ApiKeyHandler : DelegatingHandler  
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Validate API key
if (!ValidateApiKey(request.Headers, out var apiKey))
{
return request.CreateResponse(HttpStatusCode.Forbidden, "Invalid API Key");
}

// Check access level of API key and set user's role
SetUserRoleBasedOnApiKey(apiKey);

// Continue down the pipeline
return await base.SendAsync(request, cancellationToken);
}

private bool ValidateApiKey(HttpRequestHeaders headers, out string apiKey)
{
// Logic to validate API key
apiKey = /* ... */;
return true;
}

private void SetUserRoleBasedOnApiKey(string apiKey)
{
// Logic to set user role based on API key level
}
}

7、授权

问题:如果没有适当的授权检查,经过身份验证的用户可能会访问他们不应该被允许访问的资源。

解决方案:在允许请求继续之前,实现基于角色的访问控制 (RBAC) 并检查每个 API 端点上的用户权限。

[Authorize(Roles = "Admin")]  
[HttpDelete("users/{id}")]
public async Task<IActionResult> DeleteUser(int id)
{
// Delete user logic

return NoContent();
}

在更复杂的场景中,您可能需要实现基于属性的访问控制 (ABAC) 或基于策略的授权。

API 中实施授权检查,以区分用户的不同访问权限级别。

[Authorize(Roles = "Admin, Viewer")]  
public class DataController : ApiController
{
public IHttpActionResult GetData()
{
// Only users with role "Admin" or "Viewer" can access data
var data = GetDataFromService();
return Ok(data);
}

[Authorize(Roles = "Admin")]
public IHttpActionResult UpdateData(MyDataModel model)
{
// Only users with role "Admin" can update data
UpdateDataService(model);
return Ok();
}

// Separate methods to get and update data
private object GetDataFromService() { /*...*/ }
private void UpdateDataService(MyDataModel model) { /*...*/ }
}

8、白名单

问题:某些 API 端点可能设计为仅接受一组有限的预定义参数值。允许任意输入可使攻击者绕过验证或注入恶意数据。

解决方案:使用白名单(或白名单)显式定义敏感参数的允许值。

[HttpGet("articles")]  
public IActionResult GetArticles([FromQuery] string category)
{
string[] allowedCategories = { "science", "technology", "business" };

if (!allowedCategories.Contains(category))
{
return BadRequest("Invalid category.");
}

// Fetch and return articles in the specified category
}

实现仅允许来自已知和受信任 IP 地址的请求的 IP 允许列表。

public class IPAllowlistHandler : DelegatingHandler  
{
private readonly string[] _trustedIPs;

public IPAllowlistHandler(string[] trustedIPs)
{
_trustedIPs = trustedIPs ?? throw new ArgumentNullException(nameof(trustedIPs));
}

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var context = ((HttpContextBase)request.Properties["MS_HttpContext"\]);
var requestIP = context.Request.UserHostAddress;

if (!_trustedIPs.Contains(requestIP))
{
return Task.FromResult(request.CreateResponse(HttpStatusCode.Forbidden, "Access denied from this IP address"));
}

return base.SendAsync(request, cancellationToken);
}
}

9、OWASP API 安全风险

问题陈述:您的 API 受到各种安全威胁和漏洞的影响。您如何确保它们免受 OWASP 识别的主要安全风险的影响?

解决方案:根据 OWASP API 安全前 10 名列表定期审核和更新您的 API,该列表详细说明了 Web 应用程序面临的最关键安全风险。

C# 示例:

// Example of checking for broken user authentication, which is a common OWASP risk  
public class AuthenticationMiddleware : OwinMiddleware
{
public AuthenticationMiddleware(OwinMiddleware next) : base(next) {}

public override async Task Invoke(IOwinContext context)
{
if (!UserIsAuthenticated(context))
{
context.Response.StatusCode = 401; // Unauthorized
await context.Response.WriteAsync("User authentication failed.");
return;
}

await Next.Invoke(context);
}

private bool UserIsAuthenticated(IOwinContext context)
{
// Implement your authentication logic here
// Make sure it's in line with OWASP recommendations
return true; // Placeholder for actual authentication check
}
}

1️0、使用 API 网关

问题:随着微服务和 API 端点数量的增加,管理身份验证、速率限制和监控等方面可能会变得复杂且容易出错。

解决方案:使用 API Gateway 作为所有客户端请求的单一入口点。它可以处理请求路由、组合和协议转换等常见任务。常用选项包括 Azure API 管理、Amazon API Gateway 或使用 Ocelot 构建自己的 API。

// Configure API Gateway routes  
var routes = new List<RouteConfiguration>
{
new RouteConfiguration
{
RouteId = "users-route",
UpstreamPathTemplate = "/api/users/{everything}",
DownstreamPathTemplate = "/api/users/{everything}",
DownstreamScheme = "https",
DownstreamHostAndPorts = new List<DownstreamHostAndPort>
{
new DownstreamHostAndPort
{
Host = "users-service",
Port = 443
}
}
},
// Additional route configurations
};

var config = new OcelotPipelineConfiguration
{
Routes = routes
};



// Configure authentication middleware
services.AddAuthentication()
.AddJwtBearer("users-service", options =>
{
// JWT bearer configuration for users service
})
.AddJwtBearer("products-service", options =>
{
// JWT bearer configuration for products service
});

await ocelotBuilder.AddOcelot(config)
.AddDelegatingHandler\<AuthenticationDelegatingHandler>()
.Build()
.StartAsync();

API Gateway 实现为微服务的单一入口点。它可以处理跨领域问题,如身份验证、SSL 终止和速率限制。

public class ApiGatewayHandler : DelegatingHandler  
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Pre-processing: authentication, logging, etc.
AuthenticateRequest(request);

// Route to the appropriate service
var response = RouteToService(request);

// Post-processing: modify response, add headers, etc.
return await ProcessResponse(response);
}

private void AuthenticateRequest(HttpRequestMessage request)
{
// Authentication logic
}

private Task<HttpResponseMessage> RouteToService(HttpRequestMessage request)
{
// Logic to route to specific services
// This is a placeholder for actual routing logic
return Task.FromResult(new HttpResponseMessage());
}

private async Task<HttpResponseMessage> ProcessResponse(HttpResponseMessage response)
{
// Response processing logic
return response;
}
}

1️1、错误处理

问题:向客户端公开详细的错误消息可能会泄露有关 API 内部的敏感信息,从而可能帮助攻击者。

解决方案:实施全局错误处理策略,以在 API 中一致地捕获和处理异常。将一般的、不敏感的错误消息返回给客户端,同时在服务器端记录详细的错误信息以进行调试。

public class ErrorDetails  
{
public int StatusCode { get; set; }
public string Message { get; set; }
}

public class GlobalExceptionFilter : IExceptionFilter
{
private readonly ILogger<GlobalExceptionFilter> _logger;

public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
{
_logger = logger;
}

public void OnException(ExceptionContext context)
{
int statusCode = StatusCodes.Status500InternalServerError;
string message = "An unexpected error occurred.";

if (context.Exception is ArgumentException)
{
statusCode = StatusCodes.Status400BadRequest;
message = "Invalid request data.";
}
else if (context.Exception is UnauthorizedAccessException)
{
statusCode = StatusCodes.Status401Unauthorized;
message = "Authentication required.";
}
// Handle other specific exception types

_logger.LogError(context.Exception, "Unhandled exception occurred.");

context.Result = new ObjectResult(new ErrorDetails
{
StatusCode = statusCode,
Message = message
})
{
StatusCode = statusCode
};

context.ExceptionHandled = true;
}
}

// Register the global exception filter
services.AddControllers(options =>
{
options.Filters.Add<GlobalExceptionFilter>();
});

创建一个自定义错误处理程序,该处理程序在不公开敏感详细信息的情况下返回描述性和有用的错误消息。

public class GlobalExceptionHandler : ExceptionHandler  
{
public override void Handle(ExceptionHandlerContext context)
{
// Log the exception details for internal use
LogException(context.Exception);

// Provide a friendly error message to the client
var result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An unexpected error occurred. Please try again later."),
ReasonPhrase = "Critical Exception"
};

context.Result = new ErrorMessageResult(context.Request, result);
}

private void LogException(Exception exception)
{
// Implement logging logic
}
}

public class ErrorMessageResult : IHttpActionResult
{
private readonly HttpRequestMessage _request;
private readonly HttpResponseMessage _httpResponseMessage;

public ErrorMessageResult(HttpRequestMessage request, HttpResponseMessage httpResponseMessage)
{
_request = request;
_httpResponseMessage = httpResponseMessage;
}

public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_httpResponseMessage);
}
}

// Register in WebApiConfig
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());

1️2、输入验证

问题:在未经适当验证的情况下接受来自客户端的不受信任的输入可能会引入 SQL 注入或跨站点脚本 (XSS) 等安全漏洞。

解决方案:始终在服务器端验证和清理输入。使用数据注释和属性进行基本验证:[ApiController]

public class CreateUserModel  
{
[Required]
[StringLength(50)]
public string Username { get; set; }

[Required]
[EmailAddress]
public string Email { get; set; }

[Required]
[StringLength(100, MinimumLength = 6)]
public string Password { get; set; }
}

[HttpPost]
public IActionResult CreateUser([FromBody] CreateUserModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

// Create user logic

return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}

对于更复杂的验证方案,请考虑使用专用的验证库,如 FluentValidation:

public class CreateUserValidator : AbstractValidator<CreateUserModel>  
{
public CreateUserValidator()
{
RuleFor(x => x.Username)
.NotEmpty()
.MaximumLength(50);

RuleFor(x => x.Email)
.NotEmpty()
.EmailAddress();

RuleFor(x => x.Password)
.NotEmpty()
.Length(6, 100);
}
}

[HttpPost]
public IActionResult CreateUser([FromBody] CreateUserModel model)
{
var validator = new CreateUserValidator();
var validationResult = validator.Validate(model);

if (!validationResult.IsValid)
{
return BadRequest(validationResult.Errors);
}

// Create user logic

return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}

请记住,输入验证不是灵丹妙药。它应与其他安全措施(如参数化查询、输出编码和内容安全策略)结合使用,以构建针对注入攻击的全面防御。

在 API 网关级别实现输入验证,以确保仅处理有效请求。

public class ValidateModelAttribute : ActionFilterAttribute  
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}

// Usage in a Controller
public class MyModel
{
[Required]
public string Property1 { get; set; }

// Other properties and validation attributes
}

public class MyApiController : ApiController
{
[ValidateModel]
public IHttpActionResult Post(MyModel model)
{
// Proceed knowing the model is valid
ProcessData(model);
return Ok();
}

private void ProcessData(MyModel model)
{
// Processing logic
}
}

1️3、使用安全编码实践

问题:不安全的编码做法可能会引入攻击者可以利用的漏洞,从而危及 API 的安全性。

解决方案:遵循安全编码准则和最佳做法,以最大程度地降低漏洞风险:

  • 验证和清理所有用户输入
  • 使用参数化查询来防止 SQL 注入
  • 避免在 URL 或查询参数中使用敏感数据
  • 使用密钥保管库或环境变量安全地存储机密
  • 实施适当的访问控制和授权检查
  • 在任何地方使用安全通信通道 (HTTPS)
  • 使依赖项保持最新状态并监视漏洞

1️4、定期执行安全测试

问题:如果您不主动查找安全漏洞,它们可能无法检测到,从而使您的 API 暴露在潜在攻击之下。

解决方案:将安全测试纳入开发生命周期:

  • 进行代码审查以识别潜在的安全问题
  • 使用 SonarQube 或 Roslyn Analyzers 等工具执行静态代码分析
  • 使用动态应用程序安全测试 (DAST) 工具扫描运行时漏洞
  • 执行渗透测试以模拟真实世界的攻击并发现弱点
  • 定期监控 API 是否存在可疑活动或异常情况
  • 使用漏洞赏金计划或雇用道德黑客来识别漏洞

通过将安全测试作为开发过程的常规部分,您可以主动识别和解决漏洞,以免被恶意行为者利用。

1️5、实施日志记录和监控

问题:如果没有适当的日志记录和监视,您可能会错过关键的安全事件或无法检测到正在进行的攻击。

解决方案:对 API 实施全面的日志记录和监视:

  • 记录所有相关的安全事件,例如身份验证尝试、授权失败和输入验证错误
  • 使用集中式日志记录解决方案从 API 的所有组件收集和分析日志
  • 监控 API 的性能和使用模式,以检测异常或潜在攻击
  • 设置关键安全事件的警报和通知
  • 定期查看日志并监控可疑活动
  • 使用安全信息和事件管理 (SIEM) 工具关联和分析安全数据

通过实施强大的日志记录和监控,您可以了解 API 的安全状况,及早检测威胁,并快速响应以减轻任何事件的影响。

请记住,API 安全是一项多方面的工作,需要整体方法。通过结合安全编码实践、定期测试以及全面的日志记录和监控,您可以构建能够抵御各种威胁的 API。

在继续开发和改进 API 的过程中,请始终将安全性放在首位。随时了解最新的安全最佳实践、工具和技术。与安全社区互动,参加会议和研讨会,并不断对自己和您的团队进行有关 API 安全的教育。

通过在整个 API 开发生命周期中优先考虑安全性,您可以创建不仅功能强大、性能高,而且安全可靠且值得信赖的 API。🔒✨

本文章转载微信公众号@架构师老卢

#你可能也喜欢这些API文章!