詳解.NET Core中的數(shù)據(jù)保護(hù)組件
背景介紹
在 OWASP(開(kāi)放式 Web 應(yīng)用程序安全項(xiàng)目) 2013 年發(fā)布的報(bào)告中,將不安全的直接對(duì)象引用(Insecure Direct Object Reference)標(biāo)記為 十大 Web 應(yīng)用程序風(fēng)險(xiǎn)之一, 其表現(xiàn)形式是對(duì)象的引用(例如數(shù)據(jù)庫(kù)主鍵)被各種惡意攻擊利用, 所以對(duì)于Api返回的各種主鍵外鍵ID, 我們需要進(jìn)行加密。
.NET Core 的數(shù)據(jù)保護(hù)組件
.NET Core 中內(nèi)置了一個(gè)IDataProtectionProvider接口和一個(gè)IDataProtector接口。其中IDataProtectionProvider是創(chuàng)建保護(hù)組件的接口,IDataProtector是數(shù)據(jù)保護(hù)的接口。開(kāi)發(fā)人員可以實(shí)現(xiàn)這 2 個(gè)接口,創(chuàng)建數(shù)據(jù)保護(hù)組件。
內(nèi)置的數(shù)據(jù)保護(hù)組件
.NET Core 中默認(rèn)提供了一個(gè)數(shù)據(jù)保護(hù)組件, 下面我們來(lái)嘗試使用這個(gè)默認(rèn)組件來(lái)保護(hù)我們的數(shù)據(jù)。
例: 當(dāng)前我們有一個(gè)Movie類,代碼如下, 我們期望當(dāng)獲取Movie對(duì)象的時(shí)候,Id字段是加密的。
public class Movie { public Movie(int id, string title) { Id = id; Title = title; } public int Id { get; set; } public string Title { get; set; } }
首先我們需要在Startup.cs中ConfigureService方法中配置使用默認(rèn)的數(shù)據(jù)保護(hù)組件。
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddDataProtection(); }
這段代碼會(huì)啟用.NET Core默認(rèn)的數(shù)據(jù)保護(hù)器。
然后我們創(chuàng)建一個(gè)MoviesController, 并在構(gòu)造函數(shù)中注入IDataProtectionProvider對(duì)象, 然后使用這個(gè)Provider對(duì)象創(chuàng)建一個(gè)實(shí)現(xiàn)IDataProtector接口的數(shù)據(jù)保護(hù)器對(duì)象
[Route("movies")] public class MoviesController : Controller { private readonly IDataProtector protector; public MoviesController(IDataProtectionProvider provider) { this.protector = provider.CreateProtector("protect_my_query_string"); } }
TIPS: 使用Provider創(chuàng)建Protector的時(shí)候,我們傳入了一個(gè)參數(shù)"protect_my_query_string", 這個(gè)參數(shù)標(biāo)明了這個(gè)保護(hù)器的用途,你也可以把它就當(dāng)成這個(gè)保護(hù)器的名字。
注意: 不同用途的保護(hù)器不能解密對(duì)方的加密字符串。, 如果使用了保護(hù)器A去解密保護(hù)器B生成的字符串,會(huì)產(chǎn)生以下異常CryptographicException: The payload was invalid.
然后我們?cè)贛ovieController中添加2個(gè)Api, 一個(gè)是獲取所有Movies對(duì)象的,一個(gè)是獲取指定Movie對(duì)象的
[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 orignalId = int.Parse(this.protector.Unprotect(id)); var model = GetMovies(); var outputModel = model.Where(item => item.Id == orignalId); return Ok(outputModel); }
代碼解釋
- 在獲取Movie列表的api中,我們使用了IDataProtector接口的Protect方法對(duì)Id字段進(jìn)行了加密
- 相應(yīng)的在獲取單個(gè)Movie對(duì)象的api中, 我們需要使用IDataProtector接口的Unprotect方法對(duì)Id字段進(jìn)行解密。
最終效果
首先我們調(diào)用/api/movies, 返回結(jié)果如下, id字段已經(jīng)被正確加密了
[{ "id": "CfDJ8D9KlbQBeipPoQwll5uLR6ygyO6avkgI2teCQGZQShNwsxC9ApDdsnyYd1K5IyNHjhZcRoGd6W31se3W6TWM8H9UdLEPn4fJpS5uKkqUa0PMV6a0ZZHBQSnlGoisSnj29g", "title": "泰坦尼克號(hào)" }, { "id": "CfDJ8D9KlbQBeipPoQwll5uLR6wkMUYyzflIzy3CwoMhcaO-np2WOy4czIL3WZd2FWi7Tsy119tDeFq7yAeye4o2W-KmbffpGXnTDZzNv2QbCrAm7-AyEN35g3pkfAYHa3X7aQ", "title": "我是誰(shuí)" }, { "id": "CfDJ8D9KlbQBeipPoQwll5uLR6x2AXM6ulCwts2-uQSfzIU8UquTz-OAZIl-49D5-CYYl5H4mfZH8VihhCBJ60MMrZOlZla9qvb8EIP6GYRkEap4nhktbzGxW0Qu5r3edm6_Kg", "title": "蜘蛛俠" }, { "id": "CfDJ8D9KlbQBeipPoQwll5uLR6zDZeLtPIVlkRLCd_V6Mr2kTzWsCkfYgmS0-cqhFAOu4dUWGtx6d402_eKnObAOFUClEDdF4mrUeDQawE71DDa805umhbAvX2712i7UgYO5MA", "title": "鋼鐵俠" }]
然后我們繼續(xù)調(diào)用api, 查詢鋼鐵俠的電影信息
/api/movies/CfDJ8D9KlbQBeipPoQwll5uLR6zDZeLtPIVlkRLCd_V6Mr2kTzWsCkfYgmS0-cqhFAOu4dUWGtx6d402_eKnObAOFUClEDdF4mrUeDQawE71DDa805umhbAvX2712i7UgYO5MA
結(jié)果也正確的返回了。
[{"id":4,"title":"鋼鐵俠"}]
帶過(guò)期時(shí)間的數(shù)據(jù)保護(hù)器(Limited Lifetime)
.NET Core默認(rèn)還提供了一種帶過(guò)期時(shí)間的數(shù)據(jù)保護(hù)器, 這種數(shù)據(jù)保護(hù)器許多使用場(chǎng)景,最常用的場(chǎng)景就是當(dāng)為一個(gè)重置密碼操作的Token設(shè)置失效時(shí)間, 這樣一旦超時(shí)的, Token就不能解密成功, 從而我們就可以認(rèn)定重置密碼操作超時(shí)了。
.NET Core中, 我們可以使用IDataProtector接口的ToTimeLimitedDataProtector方法創(chuàng)建一個(gè)帶過(guò)期時(shí)間的數(shù)據(jù)保護(hù)器。
這里我們還是使用默認(rèn)還是繼續(xù)以上面的例子為例, 代碼修改如下
private readonly ITimeLimitedDataProtector protector; public MoviesController(IDataProtectionProvider provider) { this.protector = provider.CreateProtector("protect_my_query_string") .ToTimeLimitedDataProtector(); } [HttpGet] public IActionResult Get() { var model = GetMovies(); // simulate call to repository 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); }
代碼解釋
- 這里我們定義了一個(gè)ITimeLimitedDataProtector接口對(duì)象protector, 并在構(gòu)造函數(shù)中使用ToTimeLimitedDataProtector方法,將一個(gè)普通的數(shù)據(jù)保護(hù)器轉(zhuǎn)換成了一個(gè)帶過(guò)期時(shí)間的數(shù)據(jù)保護(hù)器
- 在獲取Movie列表的api中, 我們依然使用Protect方法來(lái)加密Id字段, 與之前不同的是,這里我們加入了第二個(gè)TimeSpan參數(shù),這個(gè)參數(shù)表示了當(dāng)前加密的有效時(shí)間只有10秒。
最終效果
現(xiàn)在我們重新運(yùn)行項(xiàng)目,還是和之前一樣先調(diào)用/api/movies方法來(lái)獲取Movies列表, 結(jié)果如下
[{ "id": "CfDJ8D9KlbQBeipPoQwll5uLR6yzbDbZ931toH32VC6Jqg8DWsrmiLrOxOFFViH4QWZne43jwSVzBjzJIfctYKZniZKNVbr50RRIZpW2fe9UtPajEzBhI-H32Effm-F0ColUaA", "title": "泰坦尼克號(hào)" }, { "id": "CfDJ8D9KlbQBeipPoQwll5uLR6zDDVymvftZK9lKBIjEyuoNTzOEu0SC2-qfTy6quXir2S8f3A1r44f9Yz3Sd_cyLZUp-_4gfJAasMfE8_ngYLrJmdsjN9LZ0g4vox0WJLjiGA", "title": "我是誰(shuí)" }, { "id": "CfDJ8D9KlbQBeipPoQwll5uLR6zL-M2jzv2HCeTiHjevkXvI2216NERplp43TOjCXtj4S52ll68sLyQNtG2FhhWlsOmFGvYY5G4gm5SKfASMMgE1jBr20xc2b_djWdLhWLIxnA", "title": "蜘蛛俠" }, { "id": "CfDJ8D9KlbQBeipPoQwll5uLR6wAoZKCHTG0lvgYS3If_0_eAD30a2YV8RjNagwLXUdCSKsO3kyS58hqDqAPHw_KHwNpd-hjDFl3hFPa8LOWHyk901oc6ZuSxwzxFlljaVreFA", "title": "鋼鐵俠" }]
等待10秒鐘后,我們繼續(xù)調(diào)用api, 查詢鋼鐵俠的電影信息
/api/movies/CfDJ8D9KlbQBeipPoQwll5uLR6wAoZKCHTG0lvgYS3If_0_eAD30a2YV8RjNagwLXUdCSKsO3kyS58hqDqAPHw_KHwNpd-hjDFl3hFPa8LOWHyk901oc6ZuSxwzxFlljaVreFA
返回了錯(cuò)誤信息CryptographicException: The payload expired at 9/29/2018 11:25:05 AM +00:00. 這說(shuō)明當(dāng)前加密的有效期已過(guò), 不能正確解密了。
Tips: 使用Action Filter解密參數(shù)
在之前的代碼中,我們?cè)讷@取單個(gè)Movie的方法中,我們手動(dòng)調(diào)用了Unprotected方法來(lái)解密id屬性
[HttpGet("{id}")] public IActionResult Get(string id) { var orignalId = int.Parse(this.protector.Unprotect(id)); var model = GetMovies(); // simulate call to repository var outputModel = model.Where(item => item.Id == orignalId); return Ok(outputModel); }
下面我們改用Action Filter來(lái)改進(jìn)這部分代碼。
首先我們創(chuàng)建一個(gè)DecryptReferenceFilter, 代碼如下:
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)) { } }
代碼解釋
- 這里DecryptReferenceFilter實(shí)現(xiàn)了IActionFilter接口, 并實(shí)現(xiàn)了OnActionExecuting和OnActionExecuted方法
- 在DecryptReferenceFilter類中,我們注入了默認(rèn)的數(shù)據(jù)保護(hù)器提供器,并在構(gòu)造函數(shù)中初始化了一個(gè)數(shù)據(jù)保護(hù)器
- 在OnActionExecuting中我們從RouteData中獲取到未解密的id字段, 然后將其解密之后,替換了之前未解密的id字段,這樣ModelBinder就會(huì)使用解密后的字符串來(lái)綁定模型。
最終修改
最后我們修改一下獲取單個(gè)Movie的api, 代碼如下:
[HttpGet("{id}")] [DecryptReference] public IActionResult Get(int id) { var model = GetMovies(); var outputModel = model.Where(item => item.Id == id); return Ok(outputModel); }
我們?cè)讷@取單個(gè)Movie的方法上添加了DecryptReference特性。
運(yùn)行代碼之后,代碼和之前的效果一樣。
源碼地址:http://xiazai.jb51.net/201809/yuanma/id_protector_jb51.rar
- vue3.0搭配.net core實(shí)現(xiàn)文件上傳組件
- ASP.NET Core MVC學(xué)習(xí)之視圖組件(View Component)
- .NET Core 3.0之創(chuàng)建基于Consul的Configuration擴(kuò)展組件
- .net core高吞吐遠(yuǎn)程方法如何調(diào)用組件XRPC詳解
- .net core如何利用ConcurrentTest組件對(duì)方法進(jìn)行壓力測(cè)試詳解
- 詳解.Net Core中的日志組件(Logging)
- 詳解.Net core2.0日志組件Log4net、Nlog簡(jiǎn)單性能測(cè)試
- .Net Core學(xué)習(xí)教程之在Mvc中簡(jiǎn)單的使用日志組件
- 基于.net的分布式系統(tǒng)限流組件示例詳解
- .NET程序集引用COM組件MSScriptControl遇到問(wèn)題的解決方法
- .NET 開(kāi)源配置組件 AgileConfig的使用簡(jiǎn)介
相關(guān)文章
ASP.NET數(shù)據(jù)庫(kù)緩存依賴實(shí)例分析
這篇文章主要介紹了ASP.NET數(shù)據(jù)庫(kù)緩存依賴,以實(shí)例的形式分析總結(jié)了數(shù)據(jù)庫(kù)緩存依賴的原理與用法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10ASP.NET 回發(fā)密碼框清空問(wèn)題處理方法
這篇文章主要介紹了ASP.NET 回發(fā)密碼框清空問(wèn)題處理方法,需要的朋友可以參考下2014-03-03排除JQuery通過(guò)HttpGet調(diào)用WebService返回Json時(shí)“parserror”錯(cuò)誤
排除JQuery通過(guò)HttpGet調(diào)用WebService返回Json時(shí)“parserror”錯(cuò)誤的解決方法。2011-10-10ASP.NET服務(wù)器控件開(kāi)發(fā)(1)封裝html
在我們的項(xiàng)目開(kāi)發(fā)中,由于ASP.NET的服務(wù)器控件功能有限,所以我們經(jīng)常會(huì)自己定義特定的服務(wù)器控件,來(lái)滿足開(kāi)發(fā)中特定的業(yè)務(wù)要求??梢?jiàn)知道如何開(kāi)發(fā)ASP.NET服務(wù)器控件是非常有必要的2015-12-12詳解ASP.NET Core和ASP.NET Framework共享身份驗(yàn)證
本篇文章主要介紹了詳解ASP.NET Core和ASP.NET Framework共享身份驗(yàn)證 ,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12ASP.NET Core MVC/WebApi基礎(chǔ)系列2
這篇文章主要介紹了.NET Core當(dāng)中的模型綁定系統(tǒng)、模型綁定原理、自定義模型綁定、混合綁定、ApiController特性本質(zhì)。2019-04-04VS2012下QT creator登錄對(duì)話框設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了VS2012下QT creator登錄對(duì)話框的設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06asp.net fileupload控件上傳文件與多文件上傳
這篇文章主要介紹了asp.net fileupload控件上傳文件的方法,fileupload控件多文件上傳,以及fileupload上傳時(shí)實(shí)現(xiàn)文件驗(yàn)證的方法,需要的朋友可以參考下2014-11-11