.NET Web 应用程序和 API 的安全最佳实践

科技   2025-01-09 05:40   山东  


由于网络应用程序和应用程序编程接口(API)是我们数字基础设施不可或缺的一部分,确保它们的安全性变得前所未有的重要。在数据泄露和网络攻击日益频发的当下,遵循保障应用程序安全的最佳实践至关重要。.NET 框架为开发人员提供了一套强大的工具,用于构建安全、健壮的网络应用程序和 API。本文探讨了.NET 中的关键安全实践,涵盖身份验证、授权、身份管理以及数据加密等方面,并为每个方面都提供了实用的代码示例。

身份验证与授权

保障网络应用程序和 API 的安全,首先要确保只有经过身份验证和授权的用户才能访问敏感资源。.NET 提供了多种方式来实现可靠的身份验证和授权。

JWT 身份验证

JSON 网络令牌(JSON Web Tokens,简称 JWT)通常用于对 API 请求进行身份验证。JWT 允许安全地传输用户信息,确保只有经过身份验证的用户才能访问特定端点。

示例:JWT 配置 以下示例展示了如何在 Program.cs 文件中配置 JWT 身份验证。

public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://authserver.com"; // 外部身份验证提供程序的 URL
options.Audience = "your_api"; // JWT 中预期的受众
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero // 令牌过期容差
};
});

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

services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

身份验证设置: ConfigureServices 方法注册了 JWT 承载身份验证方案。它设置了外部提供程序的 URL(Authority),指定了令牌预期的受众(Audience),并定义了令牌验证参数,例如验证颁发者、受众以及令牌过期情况,且不设置时钟偏差(时间容差)。

授权设置: AddAuthorization 方法配置了一项策略,要求用户具备“Admin”角色才能访问特定资源。

控制器与中间件: AddControllers 调用用于注册控制器,在 Configure 方法中,UseAuthentication 和 UseAuthorization 被添加到请求处理管道中,以确保强制执行身份验证和授权操作。最后,应用程序使用 MapControllers 来映射控制器端点。

OAuth2 和 OpenID Connect

OAuth2 和 OpenID Connect 被广泛用于管理用户身份验证和访问委托。在构建需要第三方集成或单点登录(Single Sign-On,简称 SSO)功能的应用程序时,这些协议至关重要。

在 Program.cs 中配置 OAuth2 和 OpenID Connect,以允许用户通过外部身份提供程序(例如谷歌、脸书)进行身份验证:

示例:OpenID Connect 配置 以下代码为一个 ASP.NET Core 应用程序配置了基于 Cookie 的身份验证和 OpenID Connect 身份验证。

public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddOpenIdConnect(options =>
{
options.Authority = "https://identityprovider.com"; // OpenID Connect 提供程序的 URL
options.ClientId = "your_client_id";
options.ClientSecret = "your_client_secret";
options.ResponseType = "code";
options.SaveTokens = true;
});
}

身份验证设置: 在 ConfigureServices 方法中,定义了身份验证方案:

  • DefaultAuthenticateScheme 和 DefaultSignInScheme 被设置为基于 Cookie 的身份验证方案,这意味着将使用 Cookie 来进行身份验证并存储用户会话信息。

  • DefaultChallengeScheme 被设置为 OpenID Connect,因此当需要进行身份验证时,应用程序将重定向到 OpenID Connect 提供程序进行登录。

OpenID Connect 设置: OpenID Connect 的设置指定了以下内容:

  • Authority:OpenID Connect 提供程序的 URL(例如,像 Azure Active Directory 或谷歌这样的身份提供程序的网址)。

  • ClientId 和 ClientSecret:这些是应用程序用于向提供程序进行身份验证的凭据。

  • ResponseType:被设置为“code”,意味着应用程序将使用授权码流程来进行身份验证。

  • SaveTokens:被设置为 true,这样身份验证令牌(如访问令牌和刷新令牌)会被保存以供后续使用。

高级身份管理与 IdentityServer

对于更复杂的身份验证和授权场景,IdentityServer 或 ASP.NET Core Identity 必不可少。IdentityServer 为管理用户身份验证和 API 访问提供了一个健壮的框架,支持 OAuth2、OpenID Connect 等多种协议。

IdentityServer4 针对 OAuth2 和 OpenID Connect 的设置

IdentityServer4 是一个强大的框架,用于处理 OAuth2 流程,包括客户端凭据、授权码以及隐式授权等类型。它使你能够高效地管理客户端应用程序和用户身份。

示例:在 Identity Server 中配置客户端和 API 作用域 以下代码定义了在身份服务器(如 IdentityServer4)中客户端和 API 作用域的配置,用于处理 OpenID Connect 和 OAuth 2.0 身份验证。

public static class Config
{
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "client",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.Code,
RedirectUris = { "https://localhost:5001/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:5001/signout-callback-oidc" },
AllowedScopes = { "openid", "profile", "api1" }
}
};

public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("api1", "My API")
};
}

客户端配置: Clients 属性返回一个客户端数组。在此示例中,定义了一个客户端:

  • ClientId:客户端的唯一标识符为“client”。

  • ClientSecrets:客户端使用一个经过 SHA-256 哈希处理的密钥(secret)向身份服务器进行身份验证。

  • AllowedGrantTypes:客户端被允许使用授权码流程(GrantTypes.Code),这是一种通过交换授权码来获取令牌的安全流程。

  • RedirectUris:身份验证完成后,客户端将被重定向到该 URI(https://localhost:5001/signin-oidc)。

  • PostLogoutRedirectUris:用户注销后,将被重定向到该 URI(https://localhost:5001/signout-callback-oidc)。

  • AllowedScopes:客户端被允许请求访问“openid”、“profile”和“api1”这些作用域,其中包括用户的 OpenID Connect 身份、个人资料数据以及对某个 API 的访问权限。

API 作用域配置: ApiScopes 属性定义了可用的 API 作用域。在此示例中: 定义了一个名为“api1”且描述为“My API”的 API 作用域。该作用域控制着客户端可以请求访问的 API 资源。

ASP.NET Core Identity

如果你需要对用户管理进行更细粒度的控制,ASP.NET Core Identity 是管理用户、角色和声明的首选解决方案。

ASP.NET Core Identity 可与 IdentityServer 结合使用,以应对复杂场景,例如集成外部身份验证提供程序、多因素身份验证(Multi-factor Authentication,简称 MFA)等情况。

以下代码为一个 ASP.NET Core 应用程序配置了身份和授权,设置了用户身份验证以及基于角色的访问控制。

public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();

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

身份设置: AddIdentity<ApplicationUser, IdentityRole>() 方法将 ASP.NET Core Identity 服务添加到应用程序中:

  • ApplicationUser:一个自定义用户类(大概是继承自内置的 IdentityUser 类),它代表系统中的用户。

  • IdentityRole:代表系统中的角色(例如“Admin”“User”等)。

  • AddEntityFrameworkStores<ApplicationDbContext>():配置 Identity 使用带有 ApplicationDbContext 的实体框架,以便将用户和角色数据存储在数据库中。

  • AddDefaultTokenProviders():添加默认的令牌提供程序,用于生成在密码重置、电子邮件确认等操作中使用的令牌。

授权设置: AddAuthorization 方法定义了一个自定义授权策略: 创建了一个名为“AdminOnly”的策略,要求用户具备“Admin”角色才能访问受此策略保护的资源。

###.NET 中的数据加密 加密敏感数据是保障网络应用程序安全的核心部分。在.NET 中,有内置的加密库可帮助保护传输中和存储状态下的数据安全。

加密传输中的数据(HTTPS)

对于传输中的数据,务必确保你的网络应用程序使用 HTTPS 来加密客户端与服务器之间的通信。

示例:在 ASP.NET Core 中强制使用 HTTPS 要强制使用 HTTPS,你可以配置应用程序将所有 HTTP 请求重定向到 HTTPS:

public void Configure(IApplicationBuilder app)
{
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

加密存储状态下的数据(AES)

对于加密存储在数据库或文件中的敏感数据,高级加密标准(Advanced Encryption Standard,简称 AES)是一种广泛使用且安全可靠的加密算法。.NET 提供了 System.Security.Cryptography 命名空间来处理加密操作。

示例:配置 AES 加密敏感数据 在此示例中,使用 AES 来加密敏感数据。对于存储加密密钥,可使用像 Azure Key Vault 这样的安全密钥管理解决方案来管理密钥和机密信息。

using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Encoding.UTF8.GetBytes("a very secure key!");
aesAlg.IV = Encoding.UTF8.GetBytes("initialization vct");

ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write("sensitive data to encrypt");
}
}

byte[] encrypted = msEncrypt.ToArray();
}
}

AES 算法创建: Aes.Create() 方法创建了一个 AES 加密算法的实例(aesAlg),它将用于加密过程。

密钥和初始化向量(IV)设置

  • aesAlg.Key:加密密钥通过字符串“a very secure key!”的字节来设置。该密钥对于加密以及后续解密数据至关重要。

  • aesAlg.IV:初始化向量(IV)通过字符串“initialization vct”的字节来设置。IV 为加密过程增加了随机性,确保相同数据多次加密会产生不同的输出结果。

加密器创建: CreateEncryptor 方法使用提供的密钥和 IV 创建了一个加密器对象(ICryptoTransform encryptor)。

用于加密数据的内存流: 创建了一个 MemoryStreammsEncrypt),用于在写入加密数据时将其临时保存在内存中。

加密用的加密流: 创建了一个 CryptoStreamcsEncrypt),它将内存流和加密器连接起来。使用 CryptoStreamMode.Write 模式将加密后的数据写入流中。

用于写入数据的流写入器: 创建了一个 StreamWriterswEncrypt),用于将明文(“sensitive data to encrypt”)写入 CryptoStream,在此处数据将被加密。

加密过程: 敏感数据通过 StreamWriter 写入,经 CryptoStream 加密,最终存储在 MemoryStream 中。

获取加密数据: 加密过程完成后,通过将 MemoryStream 的内容转换为字节数组(msEncrypt.ToArray())来获取加密后的数据。

安全保障是一项需要持续投入精力的工作,它要求注重细节并使用合适的工具。通过实施诸如 JWT、OAuth2 和 OpenID Connect 等可靠的身份验证和授权机制,并利用像 IdentityServer 和 ASP.NET Core Identity 这样强大的框架,你可以确保你的网络应用程序和 API 是安全的,并且只有授权用户才能访问。此外,采用数据加密的最佳实践(无论是针对传输中的数据还是存储状态下的数据)有助于保护敏感数据,并确保符合行业标准。

如果你喜欢我的文章,请给我一个赞!谢谢

架构师老卢
资深软件架构师, 分享编程、软件设计经验, 教授前沿技术, 分享技术资源(每天发布电子书),每天进步一点点...
 最新文章