.NET 7 API中间件实现参数脱敏:调试时隐藏敏感信息可行吗?
.NET 7 API 参数脱敏与自动还原方案
针对你的需求——调试时隐藏敏感参数明文,业务逻辑可自动还原,同时避免中间件导致的API不可用问题,以下是两种可行的实现方案:
方案一:自定义敏感属性+模型字段加密(简单直接)
通过在模型中封装敏感字段,对外暴露脱敏视图,内部存储加密后的明文,确保调试时无法查看真实内容。
1. 定义敏感字段标记属性
[AttributeUsage(AttributeTargets.Property)] public class SensitiveDataAttribute : Attribute { }
2. 改造请求模型
将敏感字段的getter返回脱敏字符串,setter存储加密后的内容,同时提供解密方法供业务逻辑调用:
public class LoginRequestMobile { private string _encryptedPhone; private string _encryptedPassword; private readonly IEncryptionService _encryptionService; // 构造函数注入加密服务(需在Program.cs注册) public LoginRequestMobile(IEncryptionService encryptionService) { _encryptionService = encryptionService; } [SensitiveData] public string PhoneNumber { get => "******"; set => _encryptedPhone = _encryptionService.Encrypt(value); } [SensitiveData] public string Password { get => "******"; set => _encryptedPassword = _encryptionService.Encrypt(value); } // 供业务逻辑获取明文的方法 public string GetDecryptedPhone() => _encryptionService.Decrypt(_encryptedPhone); public string GetDecryptedPassword() => _encryptionService.Decrypt(_encryptedPassword); }
3. 实现加密服务
以AES加密为例,实现基础的加解密逻辑:
public interface IEncryptionService { string Encrypt(string plainText); string Decrypt(string cipherText); } public class AesEncryptionService : IEncryptionService { private readonly byte[] _key; private readonly byte[] _iv; public AesEncryptionService(IConfiguration config) { // 从配置文件读取密钥(切勿硬编码) _key = Encoding.UTF8.GetBytes(config["Encryption:Key"]); _iv = Encoding.UTF8.GetBytes(config["Encryption:IV"]); } public string Encrypt(string plainText) { using var aes = Aes.Create(); aes.Key = _key; aes.IV = _iv; var encryptor = aes.CreateEncryptor(aes.Key, aes.IV); using var ms = new MemoryStream(); using var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write); using var sw = new StreamWriter(cs); sw.Write(plainText); sw.Flush(); cs.FlushFinalBlock(); return Convert.ToBase64String(ms.ToArray()); } public string Decrypt(string cipherText) { using var aes = Aes.Create(); aes.Key = _key; aes.IV = _iv; var decryptor = aes.CreateDecryptor(aes.Key, aes.IV); using var ms = new MemoryStream(Convert.FromBase64String(cipherText)); using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); using var sr = new StreamReader(cs); return sr.ReadToEnd(); } }
4. 注册服务
在Program.cs中注入加密服务:
builder.Services.AddScoped<IEncryptionService, AesEncryptionService>();
5. 业务逻辑中使用
public async Task<ApiOperationResult<Token>> Login(LoginRequestMobile userLogin) { // 获取解密后的明文 var phone = userLogin.GetDecryptedPhone(); var password = userLogin.GetDecryptedPassword(); // 后续验证、生成Token逻辑 }
方案二:自定义JsonConverter(适配JSON请求场景)
如果你的API主要接收JSON请求,使用JsonConverter可以在反序列化阶段自动加密敏感字段,序列化(包括调试视图)时返回脱敏内容,无需修改模型的属性封装。
1. 定义敏感属性(同方案一)
[AttributeUsage(AttributeTargets.Property)] public class SensitiveDataAttribute : Attribute { }
2. 实现JsonConverter
public class SensitiveDataConverter : JsonConverter<string> { private readonly IEncryptionService _encryptionService; public SensitiveDataConverter(IEncryptionService encryptionService) { _encryptionService = encryptionService; } // 反序列化:将请求中的明文加密后存入模型 public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var plainText = reader.GetString(); return string.IsNullOrEmpty(plainText) ? plainText : _encryptionService.Encrypt(plainText); } // 序列化:调试/日志输出时返回脱敏内容 public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) { writer.WriteStringValue("******"); } }
3. 配置模型与服务
在请求模型的敏感字段上标记转换器:
public class LoginRequestMobile { [SensitiveData] [JsonConverter(typeof(SensitiveDataConverter))] public string PhoneNumber { get; set; } [SensitiveData] [JsonConverter(typeof(SensitiveDataConverter))] public string Password { get; set; } }
在Program.cs中注册加密服务和JsonConverter:
builder.Services.AddScoped<IEncryptionService, AesEncryptionService>(); builder.Services.AddControllers() .AddJsonOptions(options => { // 注入加密服务到转换器 var encryptionService = builder.Services.BuildServiceProvider().GetRequiredService<IEncryptionService>(); options.JsonSerializerOptions.Converters.Add(new SensitiveDataConverter(encryptionService)); });
4. 业务逻辑使用
public async Task<ApiOperationResult<Token>> Login(LoginRequestMobile userLogin, IEncryptionService encryptionService) { var phone = encryptionService.Decrypt(userLogin.PhoneNumber); var password = encryptionService.Decrypt(userLogin.Password); // 业务逻辑 }
关键注意事项
- 密钥安全:加密密钥必须通过配置文件、密钥管理服务存储,禁止硬编码到代码中。
- 避免中间件陷阱:之前中间件导致API不可用,大概率是直接修改了原始请求体的字节流,破坏了JSON格式。上述方案在序列化/模型绑定层面处理,不会损坏请求结构。
- 调试防护:两种方案均确保调试时查看模型属性只能看到脱敏字符串,加密后的内容无法直接识别,彻底阻断开发者获取明文的路径。
内容的提问来源于stack exchange,提问作者Taha İbrahim Orhan
相关产品推荐
相关产品推荐

