ASP.NET Core中的数据保护
在这篇文章中,我将介绍ASP.NET Core 数据保护系统:它是什么,为什么我们需要它,以及它如何工作。
为什么我们需要数据保护系统?
数据保护系统是ASP.NET Core使用的一组加密api。加密必须由不受信任的第三方处理的数据。
这方面的典型例子是身份验证cookie。cookie是在请求之间持久化状态的一种方法。你不希望每次向服务器请求时都必须提供用户名和密码,这将非常麻烦!
相反,只需向服务器提供一次凭据。服务器验证你的详细信息,并发出一个cookie,表明“他不需要提供任何其他证明,相信他。”对于后续的请求,可以简单地提供该cookie,而不必提供凭据。浏览器自动发送cookie与后续请求,这是web为用户实现流畅登录体验的方式。

cookie是非常敏感的东西。任何包含cookie的请求都将被视为原始用户发送的请求,就像原始用户在每个请求中都提供了用户名和密码一样。浏览器中有很多保护措施(例如CORS)来阻止攻击者访问这些cookie。
然而,这不仅仅是阻止别人得到你的cookie。作为一个网站所有者,你也不希望用户篡改他们自己的cookie。
身份验证cookie通常不仅仅包含经过身份验证的用户的ID或名称。它们通常包含各种附加的要求。它包含了用户的详细信息。它们可以是真实数据,如名称、电子邮件或电话号码,但它们也可以是与权限相关的声明,如是否是管理员或是否可以编辑。
对于每个请求,通常都需要这些,以确定是否允许用户采取操作。通常包含在发送给用户的身份验证cookie中,而不是每次请求都必须从数据库加载。

这使得应用程序更加容易——一旦通过从cookie中提取用户主体对用户进行身份验证,应用程序就会确切地知道用户拥有哪些权限。这意味着应用程序必须信任cookie。如果cookie是明文发送的,那么用户只需编辑这些值,就会暴露应用程序中明显的安全漏洞。
ASP.NET Core数据保护系统正是为了这个目的而使用的。它对敏感数据(如身份验证cookie)进行加密和解密。通过在响应中返回身份验证cookie之前对其进行加密,应用程序知道该cookie没有被篡改,并可以信任其值。
数据保护系统如何工作?
数据保护系统试图解决一个棘手的问题:如何保护暴露给攻击者的敏感数据,理想情况下不向开发人员暴露任何密钥,同时遵循加密的最佳实践。
数据保护系统使用对称密钥加密来保护数据。使用包含随机数据的密钥加密数据,使用相同的密钥解密数据。

在一个典型的ASP.NET Core应用程序可能有几种不同类型的不相关数据需要加密。例如,除了身份验证cookie之外,可能还需要加密跨站点请求伪造令牌(CSRF)或密码重置令牌。
你可以将相同的键用于所有这些不同的目的,但这可能会带来问题。例如,如果密码重置令牌不能“意外地”(更有可能是恶意地)用作身份验证令牌,那就好得多。
ASP.NET Core数据保护系统实现了这一目标。数据保护系统有一个不能直接使用的父密钥。必须从父密钥派生子密钥,这些子密钥用于加密和解密数据。

使用相同的purpose字符串从父密钥派生密钥总是会给出相同的密钥,因此如果你拥有父密钥并且知道purpose字符串,那么总是可以解密已加密的数据。如果密钥是用不同的purpose导出的,那么尝试解密数据将会失败。这样可以保持数据隔离,这对安全性更好。
在大多数情况下,你不必直接与数据保护系统交互来创建密钥或加密数据。这是由ASP.NET Core核心框架及其附带的库处理的。它们确保在你的应用程序中为每个不同的purpose使用唯一的字符串。如果你愿意,你可以创建自己的保护程序并加密其他数据(见下文),但这不是ASP.NET Core的日常运行所必需的。
我是一个.net框架开发者——这听起来很像<machineKey>?
数据保护系统是ASP.NET Core的一个新功能。,但是保护身份验证令牌的需要并不是新的,那么我们以前用的是什么呢?答案是<machineKey>。<machineKey>元素的使用方式与ASP.NET Core数据保护系统非常相似,配置密钥和密码学套件,用于通过身份验证系统加密数据(以及其他地方)。不幸的是,使用这个密钥时有些复杂,因为它通常是从machine.config读取的。必须在运行应用程序的机器上配置。在集群中运行时,必须确保这些键保持同步,否则可能会产生问题!
在.net Framework 4.5中,我们可以替换<machineKey>元素及其使用的整个加密管道。
如何管理数据保护密钥?
如果对安全性有所了解,那么你可能已经听到应该定期轮换密码、机密和证书的说法。如果你的某个秘密被泄露了,这可以在一定程度上减少影响。这就是为什么签发HTTPS证书的寿命越来越短的原因。
根据你从框架和工具获得的支持,更换秘钥和证书可能会很痛苦,特别是在过渡时期,可能需要同时支持新旧秘钥。
鉴于数据保护对于保护ASP.NET Core是至关重要的。你不会惊讶秘钥轮换是数据保护系统的默认设置。默认情况下,数据保护的生命周期为90天,但通常不必为此担心。当旧密钥快要过期时,数据保护系统会自动创建新的密钥。所有可用密钥的集合称为密匙环。

在这篇文章中,我不会深入密钥管理的细节。请注意,秘钥轮换是自动发生的,只要你不删除任何旧的键(或显式地撤销它们),那么加密的数据仍然可以使用过期的键检索。过期的密钥不能用于加密新数据。
我是否也可以保护其他数据,还是仅用于身份验证cookie?
数据保护系统由ASP.NET Core隐含地使用并处理认证令牌的加密和解密。它也被ASP.NET Core用来保护密码重置和MFA令牌。你不需要为这种保护做任何事情——框架自己处理保护。
如果你有自己想要加密的临时数据,可以直接使用数据保护api。我将在后面的文章中详细介绍,但以下内容展示了中如何使用IDataProtectionProvider服务(默认在ASP.NET Core apps)加密和解密一些数据:
public class MyClass
{
// The IDataProtectionProvider is registered by default in ASP.NET Core
readonly IDataProtectionProvider _rootProvider;
public MyClass(IDataProtectionProvider rootProvider)
{
_rootProvider = rootProvider;
} public void RunSample()
{
// Create a child key using the purpose string
string purpose = "Contoso.MyClass.v1";
IDataProtector protector = provider.CreateProtector(purpose); // Get the data to protect
Console.Write("Enter input: ");
string input = Console.ReadLine();
// Enter input: Hello world! // protect the payload
string protectedPayload = _protector.Protect(input);
Console.WriteLine($"Protect returned: {protectedPayload}");
//PRINTS: Protect returned: CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ // unprotect the payload
string unprotectedPayload = _protector.Unprotect(protectedPayload);
Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
//PRINTS: Unprotect returned: Hello world
}
}
一般来说,这并不是你想要做的事情。我个人只在处理密码重置和类似的令牌时需要它。
有什么是我不应该使用数据保护的吗?
重要的一点是,数据保护系统并不是真正用于通用加密。我们希望你加密的东西,就其本质而言,有一个有限的生命周期,如身份验证令牌和密码重置令牌。
理论上,你可以对希望加密和长期存储的数据(例如在数据库中)使用数据保护系统。数据保护密钥每90天过期一次(默认情况下),但是你仍然可以使用过期的密钥解密数据。
如果数据保护密钥因为某种原因被删除,真正的危险就来了。不建议这样做,但意外总是会发生的。如果使用正确,删除数据保护密钥对大多数应用程序的影响相对较小——用户将不得不再次登录,以前发出的密码重置键将无效——这很烦人,但不会造成灾难。
另一方面,如果你已经用数据保护系统加密了敏感数据,然后将其存储在数据库中,那么就有大问题了。那些数据都不见了,被毁了。绝对不值得冒这个险!相反,你应该使用.NET Core中的专用加密库,以及为此目的创建的特定证书或密钥。
如何在我的ASP.NET Core中配置数据保护?
通常,你与数据保护系统交互的唯一地方是配置它的时候,如果没有正确地配置它,你可能会暴露于安全漏洞中,或者无法解密身份验证cookie。
一方面,数据保护系统需要易于配置和维护,因为复杂性和维护开销通常会导致bug或不良实践。但ASP.NET Core还需要在各种环境下运行:Windows、Linux、macOS;在Azure, AWS;在高端服务器和树莓派上。每个平台都有不同的内置加密机制和可用特性,而.NET Core需要在所有这些平台上都是安全的。
为了解决这个问题,数据保护系统使用了一种通用的“插件”式架构。基本上有两个不同的可插入区域:
- 密钥环持久性位置:密钥应该存储在哪里?
- 持久性加密:密钥是否应该在静止时加密,如果是,如何加密。
ASP.NET Core试图在默认情况下将这些设置为合理的选项。例如,在Windows(非azure应用程序)机器上,将持久化到%LOCALAPPDATA%\ASP.net\数据加密密钥。
不幸的是,一旦你开始在生产环境中运行应用程序并对应用程序进行扩展,大多数默认设置都将不起作用。相反,你可能需要研究一种可选的配置方法。
总结
在这篇文章中,我提供了ASP.NET Core数据保护系统的高级概述。我描述了数据保护系统的动机以及它背后的一些设计原则。
欢迎关注我的公众号,如果你有喜欢的外文技术文章,可以通过公众号留言推荐给我。

ASP.NET Core中的数据保护的更多相关文章
- 为什么我的会话状态在ASP.NET Core中不工作了?
原文:Why isn't my session state working in ASP.NET Core? Session state, GDPR, and non-essential cookie ...
- ASP.NET Core中的Startup
原文:链接 Startup.cs的作用: 配置各服务和HTTP请求管道. Startup类: ASP.NET Core中使用按惯例Startup命名的类Startup.cs: (可选)包括Config ...
- 在Asp.NET Core中如何优雅的管理用户机密数据
在Asp.NET Core中如何优雅的管理用户机密数据 背景 回顾 在软件开发过程中,使用配置文件来管理某些对应用程序运行中需要使用的参数是常见的作法.在早期VB/VB.NET时代,经常使用.ini文 ...
- ASP.NET Core 中的那些认证中间件及一些重要知识点
前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...
- Asp.net Core中使用Session
前言 2017年就这么悄无声息的开始了,2017年对我来说又是特别重要的一年. 元旦放假在家写了个Asp.net Core验证码登录, 做demo的过程中遇到两个小问题,第一是在Asp.net Cor ...
- 在ASP.NET Core中使用百度在线编辑器UEditor
在ASP.NET Core中使用百度在线编辑器UEditor 0x00 起因 最近需要一个在线编辑器,之前听人说过百度的UEditor不错,去官网下了一个.不过服务端只有ASP.NET版的,如果是为了 ...
- ASP.NET Core中的依赖注入(1):控制反转(IoC)
ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...
- ASP.NET Core中的依赖注入(2):依赖注入(DI)
IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...
- ASP.NET Core中的依赖注入(3): 服务的注册与提供
在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...
随机推荐
- 题解 CF830D Singer House
\(\texttt{Solution}\) 首先考虑 \(\texttt{dp}\) 维护题目要求的深度为 \(i\), 每个节点最多经过一次的不同有向路径数量 \(f_i\). 明显的,只维护这个东 ...
- 【WC2014】紫荆花之恋(替罪羊重构点分树 & 平衡树)
Description 若带点权.边权的树上一对 \((u, v)\) 为 friend,那么需要满足 \(\text{dist}(u, v) \le r_u + r_v\),其中 \(r_x\) 为 ...
- 【Codeforces 1037H】Security(SAM & 线段树合并)
Description 给出一个字符串 \(S\). 给出 \(Q\) 个操作,给出 \(L, R, T\),求字典序最小的 \(S_1\),使得 \(S^\prime\) 为\(S[L..R]\) ...
- 笔记-CF643E Bear and Destroying Subtrees
CF643E Bear and Destroying Subtrees 设 \(f_{i,j}\) 表示节点 \(i\) 的子树深度为 \(\le j\) 的概率,\(ch_i\) 表示 \(i\) ...
- STL——容器(Set & multiset)的大小
1. set.size(); //返回容器中元素的数目 2. set.empty();//判断容器是否为空 empty() 是由一下代码实现的,可以发现,当长度为0时返回 false,以此判断容器为 ...
- antDesign中排序sorter的坑
antd中sorter是写在columns中的一个配置,结果为一个回调函数 如图,这是我项目中使用sorter的小例子,参数a,b分别为列表第0项数据和第1项数据.回调函数中return一个值,按照什 ...
- 图像处理论文详解 | Deformable Convolutional Networks | CVPR | 2017
文章转自同一作者的微信公众号:[机器学习炼丹术] 论文名称:"Deformable Convolutional Networks" 论文链接:https://arxiv.org/a ...
- PHP 直接使用html输出excel
1 <?php 2 header("Cache-Control:public"); 3 header("Pragma:public"); 4 5 head ...
- linux重启后nginx服务无法启动
查看ngin.conf pid的内容 例如: pid /usr/local/nginx/logs/nginx.pid 根据以上配置内容来做,检查/usr/local/nginx/logs/是否存在,如 ...
- c# 递归 yield关键字的用法
1.yield实现的功能 yield return: 先看下面的代码,通过yield return实现了类似用foreach遍历数组的功能,说明yield return也是用来实现迭代器的功能的. u ...