在开发Web应用程序时,保护用户数据的安全是非常重要的。ASP.NET Core提供了一个强大的数据保护API,可以帮助开发者在应用程序中实现数据的加密和解密。本文将介绍如何在ASP.NET Core应用程序中使用数据保护API来保护敏感数据。
首先,需要创建一个ASP.NET Core项目。然后,需要更新Startup类,以便添加MVC和数据保护服务。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDataProtection();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvcWithDefaultRoute();
}
接下来,需要创建一个模型,用于表示应用程序中的数据。例如,可以创建一个表示电影的模型。
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
public int ReleaseYear { get; set; }
public string Summary { get; set; }
}
然后,需要创建一个控制器,并注入IDataProtectionProvider作为依赖项。这样就可以在控制器中使用数据保护API了。
[Route("movies")]
public class MoviesController : Controller
{
private readonly IDataProtector protector;
public MoviesController(IDataProtectionProvider provider)
{
this.protector = provider.CreateProtector("protect_my_query_string");
}
...
}
在控制器中,需要添加一个方法来检索数据,然后加密对象引用(这里以Id属性为例)。还需要添加一个方法来接收加密的对象引用,然后解密它。
[HttpGet]
public IActionResult Get()
{
var model = GetMovies();
// 模拟调用仓库
var outputModel = model.Select(item =>
new
{
Id = this.protector.Protect(item.Id.ToString()),
item.Title,
item.ReleaseYear,
item.Summary
});
return Ok(outputModel);
}
[HttpGet("{id}")]
public IActionResult Get(string id)
{
var originalId = int.Parse(this.protector.Unprotect(id));
var model = GetMovies();
// 模拟调用仓库
var outputModel = model.Where(item => item.Id == originalId);
return Ok(outputModel);
}
运行示例时,访问/movies将显示加密的引用。
OWASP 2013将不安全的直接对象引用列为十大风险之一。如果对象引用(例如数据库记录的主键)可以被操纵以进行恶意攻击,那么就存在这种风险。
上面的例子展示了一种可能的预防方法,即通过加密内部引用,可以隐藏数据库/应用程序结构的内部细节。可以使用多种方式进行加密,这里使用的是ASP.NET Core中新的数据保护API。
使用两个关键的抽象来加密数据,即IDataProtectionProvider和IDataProtector。使用提供程序通过调用其CreateProtector()方法来创建一个保护器。这个方法接受一个字符串密钥(称为目的字符串)。一旦有了保护器,就可以使用它的Protect()方法来加密和Unprotect()方法来解密数据。
它是一个确保不同保护器(加密消费者)之间隔离的密钥,即,只要它们使用不同的目的字符串,由保护器A加密的数据就不能被保护器B读取。
尝试解密已被修改的数据将抛出CryptographicException。
还可以加密只能在有限时间内解密的数据(例如,创建密码重置链接的令牌)。为了实现这一点,使用ITimeLimitedDataProtector并指定保护数据的时间周期:
private readonly ITimeLimitedDataProtector protector;
public MoviesController(IDataProtectionProvider provider)
{
this.protector = provider.CreateProtector("protect_my_query_string")
.ToTimeLimitedDataProtector();
}
[HttpGet]
public IActionResult Get()
{
var model = GetMovies();
// 模拟调用仓库
var outputModel = model.Select(item =>
new
{
Id = this.protector.Protect(item.Id.ToString(),
TimeSpan.FromSeconds(10)),
item.Title,
item.ReleaseYear,
item.Summary
});
return Ok(outputModel);
}
可以创建一个动作过滤器来解密传入的加密引用,这可以在应用程序中重用:
[HttpGet("{id}")]
[DecryptReference]
public IActionResult Get(int id)
{
var model = GetMovies();
// 模拟调用仓库
var outputModel = model.Where(item => item.Id == id);
return Ok(outputModel);
}
public class DecryptReferenceFilter : IActionFilter
{
private readonly IDataProtector protector;
public DecryptReferenceFilter(IDataProtectionProvider provider)
{
this.protector = provider.CreateProtector("protect_my_query_string");
}
public void OnActionExecuting(ActionExecutingContext context)
{
object param = context.RouteData.Values["id"].ToString();
var id = int.Parse(this.protector.Unprotect(param.ToString()));
context.ActionArguments["id"] = id;
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
public class DecryptReferenceAttribute : TypeFilterAttribute
{
public DecryptReferenceAttribute() : base(typeof(DecryptReferenceFilter)) { }
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDataProtection()
.SetApplicationName("Fiver.Security")
.PersistKeysToFileSystem(new DirectoryInfo(@"C:\MyKeys"))
.SetDefaultKeyLifetime(TimeSpan.FromDays(7))
// 7天是最低
.UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
// 默认
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
// 默认
});
}