ASP.NET Core – Data Protection & Azure Storage + Azure Key Vault
前言
以前就写过很多篇了
Asp.net core 学习笔记 ( Data protection )
Asp.net core 学习笔记 Secret 和 Data Protect Azure key-vault & Storage Account 第 2 篇
Azure 入门系列 (第五篇 Azure Storage)
这篇作为最新最完整的版本呗.
注意
由于本篇会涉及到 Key Vault, 所以建议先阅读上一篇 ASP.NET Core – User Secrets & Azure Key Vault
参考
Docs – Get started with the Data Protection APIs in ASP.NET Core
Docs – Configure ASP.NET Core Data Protection
What & Why Data Protection?
项目中许多地方需要用到加密技术, 比如 identity cookie 为了优化性能, 我们会把 permission 放入 user cookie 中.
但我们又要确保 user 不可以伪造 cookie 自行添加 permission, 这时就需要给 cookie value 做一个对称加密, 来防止 user 自行修改了.
Data Protection 是 ASP.NET Core 封装的对称加密功能, 底层用的是 AES 算法,
为什么需要封装呢?
因为对称加密需要一个密码 (AKA Key), 而这个 Key 最好要加密, 而且最好能 rotating (定期换新的), 同时不要和程序放在一块,
为了要解决这些安全隐患, 实现起来就变得麻烦多了, 所以 ASP.NET Core 自然是需要替我们封装这些繁琐的实现咯.
Data Protection Get Started
创建项目和 add package
dotnet new console -o DataProtectionConsole
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.AspNetCore.DataProtection
program.cs
setup service provider
var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection();
var serviceProvider = serviceCollection.BuildServiceProvider();
get service and create protector
var dataProtectionProvider = serviceProvider.GetRequiredService<IDataProtectionProvider>();
var dataProtector = dataProtectionProvider.CreateProtector(purpose: "Protect something");
purpose 类似一种分组管理方式. 不同的 purpose 互相是不能加密解密的哦
加密
var value = "something";
var protectedValue = dataProtector.Protect(value);
Console.WriteLine(protectedValue); // CfDJ8NB2rn7O5ZZMspmM-QDkaxhMqnhj--a6CHu-cb1griw4wTEKx_OtQIOF96avvIBJP2zG1ItO-4HTE1-bokCsxvLxRJbmk-E_Pwevlcc0LiWOw96dLfDOSQVnOmecc4DWHQ
加密的时候, 不需要提供任何 Key 或 密码, 因为 Data Protection 已经封装了 Key 的管理. (下面会详细讲解)
注: 每一次 protect 出来的乱码都是不一样的哦, 即使内容完全一样, 出来的乱发也不同, 但是最终都可以 unprotect 回去.
解密
var unProtectedValue = dataProtector.Unprotect(protectedValue);
Console.WriteLine(unProtectedValue); // something
Where Is the Key?
对称加密的 Key 在哪里呢? Windows 下的路径是
C:\Users\your-username\AppData\Local\ASP.NET\DataProtection-Keys\key-guid-id.xml
key-guid-id.xml 内容
<?xml version="1.0" encoding="utf-8"?>
<key id="7eae76d0-e5ce-4c96-b299-8cf900e46b18" version="1">
<creationDate>2023-03-08T09:12:40.0799225Z</creationDate>
<activationDate>2023-03-10T06:31:16.4098058Z</activationDate>
<expirationDate>2023-06-06T09:12:39.8251348Z</expirationDate>
<descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
<descriptor>
<encryption algorithm="AES_256_CBC" />
<validation algorithm="HMACSHA256" />
<encryptedSecret decryptorType="Microsoft.AspNetCore.DataProtection.XmlEncryption.DpapiXmlDecryptor, Microsoft.AspNetCore.DataProtection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" xmlns="http://schemas.asp.net/2015/03/dataProtection">
<encryptedKey xmlns="">
<!-- This key is encrypted with Windows DPAPI. -->
<value>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAARY09aZD0aECmsN7kb/ruLwAAAAACAAAAAAAQZgAAAAEAACAAAAAiAQnencq6XArJWM5MY+Ntm6LvsdDyMFV0FZe5Luf5ygAAAAAOgAAAAAIAACAAAAAoJ/CtKpmbBh7ppGI1X/FeQQxfTkDSWIJPsvGWkBeMnlABAABeFPs03di3J20sBT45vWk2duy7INBH293oOIlDQoURB6xnHDZtS4lIHZWxWNRi/ZLO0vFUXo/4Qwr7VPY3GHRpHyyd25CCzVul68G0o1kqJ1S4M2KMep07yq2Zv07e64JVi6BaXGIsr2arypw2fhaRCAMyLtZsuytihuXkEWtlF4Us6A3/o9C5x6NYApR+NUO9UvHRB5BptkRxlaPggrvRtadrtGl3ewD7RfQyqGP6H23TT/aCbyZudOb+V9aYg0K+G0ujaU8+ntMFpxFNCp6qmXnvbpjwDZCbLc5w3wiHO5BdBK/O8JvPSvzqxwj4GHPWdTSCupKs6lZR7CKgY09nPCqDW+xa2hBSHzPFv1y5yKFaLsKoydYtvWW38+mCsY5bkV7JNfdzN7IRe277C4Gsjmewj6pqVeSqQEgL9pGUxNSyLY/C5nE0W9KPbjpOowZAAAAAZBBvj0lXrenuKM588c9mMMqgU2uCSgeOKscy092nQYKSqj8APXlrW62r4OTbiriukFOp4abCw2ik3L7pWwjpKQ==</value>
</encryptedKey>
</encryptedSecret>
</descriptor>
</descriptor>
</key>
1. 它有 expiration date, 过期后 Data Protection 会创建一个新的 xml file. (默认时效是 90 days, 可以调, 最低是 7 days)
serviceCollection.AddDataProtection().SetDefaultKeyLifetime(TimeSpan.FromDays(7));
2. <!-- This key is encrypted with Windows DPAPI. -->
在 Windows 下, Key 默认会使用 Windows DPAPI 进行加密.
自定义 xml 存放路径和加密 Key 的方式
自定义 xml 存放路径
serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(AppContext.BaseDirectory + @"..\..\..\DataProtection-Keys"));
通过 PersistKeysToFileSystem 可以指定 xml 存放的位置.
注: 一旦设置了 PersistKeysToFileSystem, Key 就不自动加密了.
我们可以设置 ProtectKeysWithDpapi 让它用回 default 的 Windows Dpapi 来加密 Key
serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(AppContext.BaseDirectory + @"..\..\..\DataProtection-Keys"))
.ProtectKeysWithDpapi();
自定义加密 Key 方式
除了用 Windows Dpapi, 也可以用 certificate .pfx 来加密.
var rootPath = $@"{AppContext.BaseDirectory}..\..\..\";
var certificatePath = rootPath + "certificate.pfx"; if (!System.IO.File.Exists(certificatePath))
{
// create certificate
using var algorithm = RSA.Create(keySizeInBits: 2048);
var subject = new X500DistinguishedName($"CN=DataProtectionKey");
var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
var newCertificate = request.CreateSelfSigned(
notBefore: DateTimeOffset.UtcNow,
notAfter: DateTimeOffset.UtcNow.AddYears(2)
);
var pfxBytes = newCertificate.Export(X509ContentType.Pfx, "my password");
File.WriteAllBytes(certificatePath, pfxBytes);
} var rowData = File.ReadAllBytes(certificatePath);
var certificate = new X509Certificate2(rowData, "my password"); var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(AppContext.BaseDirectory + @"..\..\..\DataProtection-Keys"))
.ProtectKeysWithCertificate(certificate);
Key Rotation
密码要定期换才安全.
serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(AppContext.BaseDirectory + @"..\..\..\DataProtection-Keys"))
.ProtectKeysWithCertificate(certificate)
.UnprotectKeysWithAnyCertificate(certificate1, certificate2);
通过 UnprotectKeysWithAnyCertificate 可以插入过期的 Key, 这些用于 unprotect 以前加密的 protected value. 而 ProtectKeysWithCertificate 则负责加密解密最新的 protected value.
保存 xml 到 Azure Storage
既然我们可以自定义存放路径, 那在 Production 时就应该进来将程序和密码分开保存.
Azure Storage 是一个不错的选择.
创建 Storage Account
az storage account create --name "stgdataprotection" --resource-group "Stooges" --location "Southeast Asia" --sku "Standard_GRS" --kind "StorageV2"
get storage account connection string
az storage account show-connection-string --name "stgdataprotection" --resource-group "Stooges"
connection string 很长, 权限很大, 属于 secret level 不要外泄哦.
创建 container
az storage container create --name "security" --connection-string "your connection string"
create blob and upload key.xml
az storage blob upload --file "C:\keatkeat\my-projects\asp.net core\7.0\DataProtection\DataProtectionConsole\key.xml" --container-name "security" --name "data-protection\key.xml" --connection-string "your connection string"
这个 key.xml 是一个框来的. 和本地不同, 往后每一次新的 Key, Data Protection 不会自动创建新的 xml, 而是不断的往这个 key.xml 里面添加新的 Key
key.xml 长这样.
<?xml version="1.0" encoding="utf-8"?>
<repository></repository>
后来长这样
<?xml version="1.0" encoding="utf-8"?>
<repository>
<key id="xxx">...</key>
<key id="yyy">...</key>
<key id="zzz">...</key>
</repository>
Connect Data Protection to Azure Storage
add package
Azure.Extensions.AspNetCore.DataProtection.Blobs
program.cs
serviceCollection.AddDataProtection()
.PersistKeysToAzureBlobStorage(
connectionString: "your connection string",
containerName: "security",
blobName: "data-protection/key.xml"
)
.ProtectKeysWithDpapi();
提醒: connection string 是敏感信息. 记得使用 Users Secret 来管理.
使用 Azure Key Vault Key 作为 Data Protection 加密的 Key
用来加密 Data Protection Key 的 certificate 也是敏感信息, 虽然放在 store 里面已经有一定的安全了, 但是我们也可以选择使用 Azure Key Vault 服务来管理 Key.
创建 Key Vault
az keyvault create --name "StgDataProtection-KV" --resource-group "Stooges" --location "Southeast Asia"
如果有做 Secret 了也可以公用, 不需要做 2 个哦.
创建 Key
az keyvault key create --name "DataProtectionKey" --vault-name "StgDataProtection-KV" --kty RSA
让 VM 有权限访问 Key
az keyvault set-policy --name "StgDataProtection-KV" --object-id "your vm object id" --key-permissions all
为了方便我直接 allow 所有 permission, 具体 Data Protection 需要用到多少我也不清楚, az keyvault set-policy --help 可以查看所有 permission
add package
dotnet add package Azure.Extensions.AspNetCore.DataProtection.Keys
dotnet add package Azure.Identity
program.cs
serviceCollection.AddDataProtection()
.PersistKeysToAzureBlobStorage(
connectionString: "connection string",
containerName: "security",
blobName: "data-protection/key.xml"
)
.ProtectKeysWithAzureKeyVault(
new Uri("https://stgdataprotection-kv.vault.azure.net/keys/DataProtectionKey/411111aaf3174ec4b5ac4111afd9add8"),
new DefaultAzureCredential()
);
通过 CLI 可以获取到 Key 的 URI
az keyvault key show --name "DataProtectionKey" --vault-name "StgDataProtection-KV" --query 'key.kid' -o tsv
另外, 非 Azure VM 也是可以用 Client App 的方式访问 Key, 上一篇教过了, 这篇就不再给例子了.
Key Rotation
Azure Key 支持 ratation. ProtectKeysWithAzureKeyVault 内部会替我们处理好 UnprotectKeysWithAnyCertificate, 我们直接开启 rotation 就可以了.
rotation.json
{
"lifetimeActions": [
{
"action": "Rotate",
"timeAfterCreate": "P7D",
"timeBeforeExpiry": null
}
]
}
Azure CLI
az keyvault key rotation-policy update --vault-name "StgDataProtection-KV" --name "DataProtectionKey" --value "C:\Users\keatk\Desktop\rotation.json"
注: --value 可以放 .json 路径也可以直接放 json value. 如果放路劲的话, 确保一定不要有空格, 不然它会以为你放的是 value, 那就解析错误了.
查看
az keyvault key rotation-policy show --vault-name "StgDataProtection-KV" --name "DataProtectionKey"
set expiryTime
by default Key 是永远都可以用的, 如果你希望让它有过期的概念可以这样 set
{
"lifetimeActions": [
{
"action": "Rotate",
"timeAfterCreate": "P7D",
"timeBeforeExpiry": null
},
{
"action": "Notify",
"timeAfterCreate": null,
"timeBeforeExpiry": "P30D"
}
],
"attributes": {
"expiryTime": "P2Y"
}
}
注意哦, 一旦过期了就表示无法解密之前加密的 value 了哦. 通常是不需要让它过期的. 因为我们做了 rotation 已经很安全了.
Key 和 Key 傻傻分不清
Data Protection 的 Key (简称 DP Key) 是用来对数据做对称加密的. 这个 DP Key 要定期换, 有 rotation 概念, Data Protection 会负责.
DP Key 本身也需要被加密, 通常使用非对称加密. 所有又需要一个 certificate. 而这个 certificate 也需要有 rotation 概念, 只要是密码都需要定期换才安全.
我们可以用 Azure Key Vault 的 Key (简称 AZ Key) 作为这个 certification.
所以 AZ Key 用非对称加密来加密 DP Key, 而 DP Key 用对称加密来加密 value.
它们两个 Key 都有 rotation 概念.
Azure Key Vault 小迷糊, 解答
参考: Azure Key Vault keys, secrets and certificates overview
Azure Key Vault 有 3 个主要 services. 分别是
保存 Secret, 保存 Key, 保存 Certification
Secret 通常用于保存各做小密码
Key 通常用于保存非对称加密/签名 的 Key
Certificate 在 Key 的基础上做了一些自动 renew 的功能, 本质上和 Key 的用途是一样的.
Azure VM connect to Key Vault 是不需要任何密码在本机上的 (它 build-in 了 connect 的方式, 所以这个方案可以算是最安全的)
非 Azure VM connect to Key Vault 则需要先 connect to Client App, 而 connect Client App 最少需要一个 certificate 在本机 store 里. 所以比起 Azure VM 多了一点点点的隐患.
程序其余要地方, 要用到小密码, Key, Certificate 的时候, 统统使用 Key Vault 就对了.
具体例子
Secret 看这篇 ASP.NET Core – User Secrets & Azure Key Vault
Key 看本篇 ASP.NET Core – Data Protection & Azure Storage + Azure Key Vault
Certificate 看这篇 Azure – Key Vault Certificate
ASP.NET Core – Data Protection & Azure Storage + Azure Key Vault的更多相关文章
- 基于ASP.NET Core Data Protection生成验证token
ASP.NET Core Data Protection 不仅提供了非对称加密能力,而且提供了灵活的秘钥存储方式以及一致的加解密接口(Protect与Unprotect).Session中用到了它,C ...
- 集群环境下,你不得不注意的ASP.NET Core Data Protection 机制
引言 最近线上环境遇到一个问题,就是ASP.NET Core Web应用在单个容器使用正常,扩展多个容器无法访问的问题.查看容器日志,发现以下异常: System.Security.Cryptogra ...
- ASP.NET Core 2.0中的Azure Blob存储
问题 如何在ASP.NET Core中使用Azure Blob存储 解 创建一个类库并添加NuGet包 - WindowsAzure.Storage 添加一个类来封装设置, publicclass A ...
- 【Azure DevOps系列】使ASP.NET Core应用程序托管到Azure Web App Service
使用Azure DevOps Project设置ASP.NET项目 我们需要先在Azure面板中创建一个Azure WebApp服务,此处步骤我将省略,然后点击部署中心如下图所示: 此处我选择的是Az ...
- 消除 ASP.NET Core 告警 "No XML encryptor configured. Key may be persisted to storage in unencrypted form"
在 ASP.NET Core 中如果在 DataProtection 中使用了 PersistKeysToFileSystem 或 PersistKeysToFileSystem services.A ...
- ASP.NET Core 数据保护(Data Protection 集群场景)【下】
前言 接[中篇],在有一些场景下,我们需要对 ASP.NET Core 的加密方法进行扩展,来适应我们的需求,这个时候就需要使用到了一些 Core 提供的高级的功能. 本文还列举了在集群场景下,有时候 ...
- ASP.NET Core 数据保护(Data Protection)【上】
前言 上一篇博客记录了如何在 Kestrel 中使用 HTTPS(SSL), 也是我们目前项目中实际使用到的. 数据安全往往是开发人员很容易忽略的一个部分,包括我自己.近两年业内也出现了很多因为安全问 ...
- ASP.NET Core 数据保护(Data Protection)【中】
前言 上篇主要是对 ASP.NET Core 的 Data Protection 做了一个简单的介绍,本篇主要是介绍一下API及使用方法. API 接口 ASP.NET Core Data Prote ...
- 翻译 - ASP.NET Core 托管和部署 - 在 Linux 上使用 Nginx 托管 ASP.NET Core 网站
翻译自 https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-5.0 本文介 ...
- Asp.net core 学习笔记 ( identity server 4 JWT Part )
更新 : id4 使用这个 DbContext 哦 dotnet ef migrations add identity-server-init --context PersistedGrantDbCo ...
随机推荐
- 第二部分:关键技术领域的开源实践【Linux服务器自动化运维】
Linux运维可能会遇到多种问题,这些问题可能源于技术挑战.资源配置.安全性.管理复杂性等多个方面.以下是一些常见的Linux运维问题: 技能要求: Linux系统通常需要较高的技术水平和经验来进行有 ...
- 如何做好一场NPS调研?
我们在工作中经常遇到的一个词,那就是"产品NPS调研".当部分项目缺少专业的用研人员时,设计师.产品经理则经常会接受上级的要求,投身于NPS调研工作. 笔者也曾在2022年的某天突 ...
- 2024秋招字节跳动朝夕光年UE4客户端开发实习生岗笔试题目
20240117更新 2024年秋招笔试题目,没想到时隔几个月字节跳动游戏业务就要寄了,本文仅供参考,请大佬多多指教 Q1字符串处理 Q2 杯子问题 桌子上有4109+1个饮料杯,这些饮料杯的编号依次 ...
- 使用GSAP制作动画视频
GSAP 3Blue1Brown给我留下了深刻印象.利用动画制作视频,内容简洁,演示清晰.前两天刚好碰到一件事,我就顺便学习了一下怎么用代码做动画. 以javascrip为例,有两个动画引擎,GSAP ...
- UE 实现鼠标点选模型
楔子 在孪生的场景中,点击三维对象是常用的操作.比如点击模型显示相关属性和图片,点击摄像头模型播放视频,点击楼宇展开楼层等等. 因此点选模型是属于数字孪生最必要的基础能力. 准备知识 UE蓝图介绍 本 ...
- django 设置外键的时候,related_name的值大写还是小写,规则怎样
django 设置外键的时候,related_name的值大写还是小写,规则怎样 在Django中,related_name参数用于定义反向关系的名称,即通过外键字段反向查询关联模型的对象.relat ...
- [oeasy]python0080_设置RGB颜色_24bit_24位真彩色_颜色设置
RGB颜色 回忆上次内容 上次 首先了解了 索引颜色 \33[38;5;XXXm 设置 前景为索引色 \33[48;5;XXXm 设置 背景为索引色 RGB每种颜色 可选0-5 总共 6 级 想用 精 ...
- Java JVM——13. 垃圾回收相关算法
1.生存还是死亡? 在堆里存放着几乎所有的 Java 对象实例,在 GC 执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象.只有被标记为己经死亡的对象,GC 才会在执行垃圾回 ...
- PHP转Go系列 | 推荐一个强大的Go语言工具函数库
大家好,我是码农先森. 从 PHP 转到 Go 的朋友,常常会因为没有便捷的工具函数而感到苦恼.PHP 写的多了就会形成路径依赖,在写 Go 的时候时不时就会想到 PHP 强大的数组函数.当然写 Go ...
- 【Scala】09 偏函数 PartialFunction
更像是策略函数 可拆分成一个部分,是若干个函数的组合 package cn object HelloScala { def main(args: Array[String]): Unit = { // ...