在Asp.NET Core中如何优雅的管理用户机密数据


在Asp.NET Core中如何优雅的管理用户机密数据

背景

回顾

在软件开发过程中,使用配置文件来管理某些对应用程序运行中需要使用的参数是常见的作法。

在早期VB/VB.NET时代,经常使用.ini文件来进行配置管理;而在.NET FX开发中,我们则倾向于使用web.config文件,通过配置appsetting的配置节来处理;而在.NET Core开发中,我们有了新的基于json格式的appsetting.json文件。

无论采用哪种方式,其实配置管理从来都是一件看起来简单,但影响非常深远的基础性工作。尤其是配置的安全性,贯穿应用程序的始终,如果没能做好安全性问题,极有可能会给系统带来不可控的风向。

源代码比配置文件安全么?

有人以为把配置存放在源代码中,可能比存放在明文的配置文件中似乎更安全,其实是“皇帝的新装”。

在前不久,笔者的一位朋友就跟我说了一段故事:他说一位同事在离职后,直接将曾经写过的一段代码上传到github的公共仓库,而这段代码中包含了某些涉及到原企业的机密数据,还好被github的安全机制提前发现而及时终止了该行为,否则后果不堪设想。

于是,笔者顺手查了一下由于有意或无意泄露企业机密,造成企业损失的案例,发现还真不少。例如大疆前员工通过 Github 泄露公司源代码,被罚 20 万、获刑半年[1] 这起案件,也是一个典型的案例。

该员工离职后,将包含关键配置信息的源代码上传到github的公共仓库,被黑客利用,使得大量用户私人数据被黑客获取,该前员工最终被刑拘。

大疆前员工通过Github泄露公司源代码,被罚20万、获刑半年

图片来源: http://www.digitalmunition.com/WhyIWalkedFrom3k.pdf[2]

大部分IT公司都会在入职前进行背景调查,而一旦有案底,可能就已经与许多IT公司无缘;即便是成为创业者,也可能面临无法跟很多正规企业合作的问题。

小结

所以,安全性问题不容小觑,哪怕时间再忙,也不要急匆匆的就将数据库连接字符串或其他包含敏感信息的内容轻易的记录在源代码或配置文件中。在这个点上,一旦出现问题,往往都是非常严重的问题。

如何实现

在.NET FX时代,我们可以使用对web.config文件的关键配置节进行加密的方式,来保护我们的敏感信息,在.NET Core中,自然也有这些东西,接下来我将简述在开发环境和生产环境下不同的配置加密手段,希望能够给读者带来启迪。

开发环境

在开发环境下,我们可以使用visual studio 工具提供的用户机密管理器,只需0行代码,即可轻松完成关键配置节的处理。

机密管理器概述

根据微软官方文档[3] 的描述:

ASP.NET Core 机密管理器[4]工具提供了开发过程中在源代码外部保存机密的另一种方法 。若要使用机密管理器工具,请在项目文件中安装包 Microsoft.Extensions.Configuration.SecretManager 。如果该依赖项存在并且已还原,则可以使用 dotnet user-secrets 命令来通过命令行设置机密的值。这些机密将存储在用户配置文件目录中的 JSON 文件中(详细信息随操作系统而异),与源代码无关。

机密管理器工具设置的机密是由使用机密的项目的 UserSecretsId 属性组织的。因此,必须确保在项目文件中设置 UserSecretsId 属性,如下面的代码片段所示。默认值是 Visual Studio 分配的 GUID,但实际字符串并不重要,只要它在计算机中是唯一的。

<PropertyGroup><UserSecretsId>UniqueIdentifyingString</UserSecretsId>
</PropertyGroup> 

Secret Manager工具允许开发人员在开发ASP.NET Core应用程序期间存储和检索敏感数据。敏感数据存储在与应用程序源代码不同的位置。

由于Secret Manager将秘密与源代码分开存储,因此敏感数据不会提交到源代码存储库。但机密管理器不会对存储的敏感数据进行加密,因此不应将其视为可信存储。

敏感数据作为键值对存储在JSON文件中。最好不要在开发和测试环境中使用生产机密。查看引文[5]

存放位置

在windows平台下,机密数据的存放位置为:

%APPDATA%\Microsoft\UserSecrets\\secrets.json

而在Linux/MacOs平台下,机密数据的存放位置为:

 ~/.microsoft/usersecrets/<user_secrets_id>/secrets.json

在前面的文件路径中, `user_secrets_id`将替换UserSecretsId为.csproj

文件中指定的值。

在Windows环境下使用机密管理器

在windows下,如果使用Visual Studio2019作为主力开发环境,只需在项目右键单击,选择菜单【管理用户机密】,即可添加用户机密数据。

在管理用户机密数据中,添加的配置信息和传统的配置信息没有任何区别。

{ "ConnectionStrings": { "Default": "Server=xxx;Database=xxx;User ID=xxx;Password=xxx;" } }

我们同样也可以使用IConfiguration的方式、IOptions的方式,进行配置的访问。

在非Windows/非Visual Studio环境下使用机密管理器

完成安装dotnet-cli后,在控制台输入

dotnet user-secrets init

前面的命令将在UserSecretsId .csproj 文件的PropertyGroup中添加

.csproj

一个元素。 UserSecretsId是对项目是唯一的Guid值。

 <PropertyGroup>  <TargetFramework>netcoreapp3.1</TargetFramework><UserSecretsId>79a3edd0-2092-40a2-a04d-dcb46d5ca9ed</UserSecretsId> </PropertyGroup> 

设置机密

 dotnet user-secrets set "Movies:ServiceApiKey" "12345" 

列出机密

 dotnet user-secrets list

删除机密

 dotnet user-secrets remove "Movies:ConnectionString" 

清除所有机密

 dotnet user-secrets clear

生产环境

机密管理器为开发者在开发环境下提供了一种保留机密数据的方法,但在开发环境下是不建议使用的,如果想在生产环境下,对机密数据进行保存该怎么办?

按照微软官方文档的说法,推荐使用Azure Key Vault [6] 来保护机密数据,但。。我不是贵云的用户(当然,买不起贵云不是贵云太贵,而是我个人的问题[手动狗头])。

其次,与Azure Key Valut类似的套件,例如其他云,差不多都有,所以都可以为我们所用。

但。。如果您如果跟我一样,不想通过第三方依赖的形式来解决这个问题,那不如就用最简单的办法,例如AES加密。

使用AES加密配置节

该方法与平时使用AES对字符串进行加密和解密的方法并无区别,此处从略。

使用数据保护Api(DataProtect Api实现)

在平时开发过程中,能够动手撸AES加密是一种非常好的习惯,而微软官方提供的数据保护API则将这个过程进一步简化,只需调Api即可完成相应的数据加密操作。

关于数据保护api, Savorboard[7] 大佬曾经写过3篇博客讨论这个技术问题,大家可以参考下面的文章来获取信息。

ASP.NET Core 数据保护(Data Protection 集群场景)【上】[8]

ASP.NET Core 数据保护(Data Protection 集群场景)【中】[9]

ASP.NET Core 数据保护(Data Protection 集群场景)【下】[10]

(接下来我要贴代码了,如果没兴趣,请出门左拐,代码不能完整运行,查看代码来源[11]

首先,注入配置项

 public static IServiceCollection AddProtectedConfiguration(this IServiceCollection services, string directory){services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(directory)).UseCustomCryptographicAlgorithms(new ManagedAuthenticatedEncryptorConfiguration{EncryptionAlgorithmType = typeof(Aes),EncryptionAlgorithmKeySize = 256,ValidationAlgorithmType = typeof(HMACSHA256)});;return services;}

其次,实现对配置节的加/解密。(使用AES算法的数据保护机制)

public class ProtectedConfigurationSection : IConfigurationSection{private readonly IDataProtectionProvider _dataProtectionProvider;private readonly IConfigurationSection _p;private readonly Lazy<IDataProtector> _protector;public ProtectedConfigurationSection(IDataProtectionProvider dataProtectionProvider,IConfigurationSection p){_dataProtectionProvider = dataProtectionProvider;_p = p;_protector = new Lazy<IDataProtector>(() => dataProtectionProvider.CreateProtector(p.Path));}public IConfigurationSection GetSection(string key){return new ProtectedConfigurationSection(_dataProtectionProvider, _p.GetSection(key));}public IEnumerable<IConfigurationSection> GetChildren(){return _p.GetChildren().Select(x => new ProtectedConfigurationSection(_dataProtectionProvider, x));}public IChangeToken GetReloadToken(){return _p.GetReloadToken();}public string this[string key]{get => GetProtectedValue(_p[key]);set => _p[key] = _protector.Value.Protect(value);}public string Key => _p.Key;public string Path => _p.Path;public string Value{get => GetProtectedValue(_p.Value);set => _p.Value = _protector.Value.Protect(value);}private string GetProtectedValue(string value){if (value == null)return null;return _protector.Value.Unprotect(value);}}

再次,在使用前,先将待加密的字符串转换成BASE64纯文本,然后再使用数据保护API对数据进行处理,得到处理后的字符串。

private readonly IDataProtectionProvider _dataProtectorTokenProvider;
public TokenAuthController( IDataProtectionProvider dataProtectorTokenProvider)
{
}
[Route("encrypt"), HttpGet, HttpPost]
public string Encrypt(string p, string value)
{var protector = _dataProtectorTokenProvider.CreateProtector(p);return protector.Protect(value);
}

再替换配置文件中的对应内容。

{"ConnectionStrings": {"Default": "此处是加密后的字符串"}
}

然后我们就可以按照平时获取IOptions的方式来获取了。

问题

公众号【DotNET骚操作】号主【周杰】同学提出以下观点:

1、在生产环境下,使用AES加密,其实依然是一种不够安全的行为,充其量也就能忽悠下产品经理,毕竟几条简单的语句,就能把机密数据dump出来。

也许在这种情况下,我们应该优先考虑accessKeyId/accessSecret,尽量通过设置多级子账号,通过授权Api的机制来管理机密数据,而不是直接暴露类似于数据库连接字符串这样的关键配置信息。另外,应该定期更换数据库的密码,尽量将类似的问题可能造成的风险降到最低。数据保护api也提供的类似的机制,使得开发者能够轻松的管理机密数据的时效性问题。

2、配置文件放到CI/CD中,发布的时候在CI/CD中进行组装,然后运维只是负责管理CI/CD的账户信息,而最高机密数据,则由其他人负责配置。

嗯,我完全同意他的第二种做法,另外考虑到由于运维同样有可能会有意无意泄露机密数据,所以如果再给运维配备一本《刑法》,并让他日常补习【侵犯商业秘密罪】相关条款,这个流程就更加闭环了。

结语

本文简述了在.NET Core中,如何在开发环境下使用用户机密管理器、在生产环境下使用AES+IDataProvider的方式来保护我们的用户敏感数据。由于时间仓促,如有考虑不周之处,还请各位大佬批评指正。

References

[1] 大疆前员工通过 Github 泄露公司源代码,被罚 20 万、获刑半年: https://www.infoq.cn/article/RZzfel1m6-h8pSK8TTC9
[2]http://www.digitalmunition.com/WhyIWalkedFrom3k.pdf: http://www.digitalmunition.com/WhyIWalkedFrom3k.pdf
[3] 微软官方文档: https://docs.microsoft.com/zh-cn/dotnet/architecture/microservices/secure-net-microservices-web-applications/developer-app-secrets-storage
[4] 机密管理器: https://docs.microsoft.com/zh-cn/aspnet/core/security/app-secrets#secret-manager
[5] 查看引文: https://nvisium.com/blog/2019/05/02/Dev-Secrets-and-the-ASP-NET-Core-Secret-Manager.html
[6] Azure Key Vault : https://docs.microsoft.com/zh-cn/dotnet/architecture/microservices/secure-net-microservices-web-applications/azure-key-vault-protects-secrets
[7] Savorboard: https://home.cnblogs.com/u/savorboard/
[8] ASP.NET Core 数据保护(Data Protection 集群场景)【上】: https://www.cnblogs.com/savorboard/p/dotnetcore-data-protection.html
[9] ASP.NET Core 数据保护(Data Protection 集群场景)【中】: https://www.cnblogs.com/savorboard/p/dotnet-core-data-protection.html
[10] ASP.NET Core 数据保护(Data Protection 集群场景)【下】: https://www.cnblogs.com/savorboard/p/dotnetcore-data-protected-farm.html
[11] 查看代码: https://stackoverflow.com/questions/36062670/encrypted-configuration-in-asp-net-core

本文首发于溪源的个人博客:https://www.techq.xyz/2020/06/09/%E6%8A%80%E6%9C%AF/how-to-manage-user-secret-in-develop-and-production/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309328.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(三)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

别了,Docker Swarm !你好,K8s !

毫无疑问&#xff0c;Kubernetes已经成为容器编排事实标准。除了已经拥抱Kubernetes的Google、BAT、京东、奇虎360等巨头大厂外&#xff0c;更多的企业也都在向Kubernetes迁移。容器技术大势所趋&#xff0c;是互联网企业目前急需的技术人才之一&#xff0c;已成为运维工程师、…

【翻译】.NET 5 Preview5发布

今天&#xff0c;发布了.NET 5.0 Preview5。主要对它进行了一小部分新功能和性能的改进。.NET 5.0 Preview 4包含了一些计划和.NET 5.0要交付的内容。现在&#xff0c;大多数的功能都已经包含在里面&#xff0c;但是有许多功能还未到最终状态。预计这个版本在Preview 7中完善。…

构造前缀贪心+ 计蒜客 子矩阵求和

题目&#xff1a; 给出一个 nn 行 mm 列的矩阵&#xff0c;矩阵的每个位置有一个非负整数 a[i][j]&#xff0c;有 qq 次询问&#xff0c;每次询问求一个左上角为 (a,b)&#xff0c;右下角为 (c,d) 的子矩阵的所有数之和。 输入格式 第一行两个整数 n,m&#xff0c;表示矩阵的…

[跨平台系列三Docker篇]:ASP.NET Core应用

如果你是老张的忠实读者的话&#xff0c;如果是从博客园就开始看我的文章的话&#xff0c;如果后期也一直看我公众号的话&#xff0c;应该就知道其实我一直在根据一条无形的教学线路来讲解的&#xff0c;&#xff0c;如果你真的是想好好学的话&#xff0c;请好好看看我之前的文…

[壹刊]Azure AD(四)知识补充-服务主体

一&#xff0c;引言又到了新的一周了&#xff0c;也到了我新的分享的时间了&#xff0c;还记得上一周立得Flag&#xff0c;其中 “保证每周输出一篇文章” &#xff0c;让我特别“在意”&#xff08;这里用词不太恰当&#xff09;。主要是我的一个大学舍友&#xff0c;他突然问…

[JavaWeb-Servlet]Servlet_执行原理

执行原理&#xff1a; 1. 当服务器接受到客户端浏览器的请求后&#xff0c;会解析请求URL路径&#xff0c;获取访问的Servlet的资源路径2. 查找web.xml文件&#xff0c;是否有对应的<url-pattern>标签体内容。3. 如果有&#xff0c;则在找到对应的<servlet-class>全…

分享我在前后端分离项目中Gitlab-CI的经验

之前我分享了为ASP.NET Core后端搭建Gitlab-CI/CD实践&#xff0c;今天继续聊一聊为前后端分离搭建Gitlab-CI的额外经验。BeforeGitlab-ci是Gitlab提供的CI/CD特性&#xff0c;结合Gitlab简单友好的配置界面&#xff0c;能愉悦的在Gitlab界面查看管道执行流程&#xff0c;并自然…

lin-cms-dotnetcore.是如何方法级别的权限控制(API级别)的

方法级别的权限控制&#xff08;API级别&#xff09;Lin的定位在于实现一整套 CMS的解决方案&#xff0c;它是一个设计方案&#xff0c;提供了不同的后端&#xff0c;不同的前端&#xff0c;而且也支持不同的数据库目前官方团队维护 lin-cms-vue,lin-cms-spring-boot,lin-cms-k…

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(四)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

字符串相关

文章目录字符串基础字符串的存储标准库字符串匹配单串匹配多串匹配其他类型的字符串匹配问题字符串哈希Hash 的实现Hash 的分析与改进错误率多次询问子串哈希Hash 的应用字符串匹配允许 k次失配的字符串匹配最长回文子串最长公共子字符串确定字符串中不同子字符串的数量字典树 …

C#9.0 终于来了,您还学的动吗? 带上VS一起解读吧!

一&#xff1a;背景1. 讲故事好消息&#xff0c;.NET 5.0 终于在2020年6月10日发布了第五个预览版&#xff0c;眼尖的同学一定看到了在这个版本中终于支持了 C# 9.0&#xff0c;此处有掌声&#xff0c;太好了&#xff01;&#xff01;&#xff01;.Net5官方链接可以看到目前的C…

.NET Core 反射获取所有控制器及方法上特定标签

有个需求&#xff0c;就是在. NET Core中&#xff0c;我们想在项目 启动时&#xff0c;获取LinCmsAuthorizeAttribute这个特性标签所有出现的地方&#xff0c;把他的参数&#xff0c;放入一个集合并缓存起来&#xff0c;以便后面使用此数据用于权限验证。我们通过反射获取所有控…

[JavaWeb-Servlet]Servlet的体系结构

Servlet的体系结构 Servlet -- 接口|GenericServlet -- 抽象类|HttpServlet -- 抽象类* GenericServlet&#xff1a;将Servlet接口中其他的方法做了默认空实现&#xff0c;只将service()方法作为抽象* 将来定义Servlet类时&#xff0c;可以继承GenericServlet&#xff0c;实现…

将数据从 SQL Server 导入 Azure Storage Table

点击上方蓝字关注“汪宇杰博客”导语最近有个需求要将数据存储从 SQL Server 数据库切换到 Azure Storage 中的 Table。然而不管是 SSMS 还是 Azure Portal 都没有提供直接的导入功能&#xff0c;是不是又想自己写程序去导数据了&#xff1f;其实不用&#xff01;没有点过数据库…

[JavaWeb-HTTP]HTTP概念

HTTP&#xff1a; * 概念&#xff1a;Hyper Text Transfer Protocol 超文本传输协议* 传输协议&#xff1a;定义了&#xff0c;客户端和服务器端通信时&#xff0c;发送数据的格式* 特点&#xff1a;1. 基于TCP/IP的高级协议2. 默认端口号:803. 基于请求/响应模型的:一次请求对…

【完整目录】每天5分钟用C#学习数据结构

【基础知识】| 作者 / Edison Zhou这是恰童鞋骚年的第250篇原创内容不知不觉&#xff0c;每天5分钟学习数据结构就更新完了&#xff0c;本篇将该系列所有文章整理起来作为一个目录&#xff0c;方便你的快速阅读。1线性表线性表是最简单也是在编程当中使用最多的一种数据结构。例…

.NET 5 开发WPF - 美食应用登录UI设计

点击上方“Dotnet9”添加关注哦Demo演示&#xff1a;演示动画你的时间宝贵&#xff0c;不想看啰嗦的文字&#xff0c;可直接拉到文末下载源码&#xff01;1. 新建项目站长开发环境&#xff1a;VS 2019企业版 16.70.NET 5 Preview 5.NET 5 WPF 项目模板和 .NET Core 3.1 WPF 项目…

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(五)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

C#9.0 终于来了,带你一起解读 nint 和 Pattern matching 两大新特性玩法

一&#xff1a;背景1. 讲故事上一篇C#9.0 终于来了&#xff0c;您还学的动吗&#xff1f; 带上VS一起解读吧&#xff01;跟大家聊到了Target-typed new 和 Lambda discard parameters&#xff0c;看博客园和公号里的阅读量都达到了新高&#xff0c;甚是欣慰&#xff0c;不管大家…