当前位置: 移动技术网 > IT编程>开发语言>.net > 详解.NET Core中的数据保护组件

详解.NET Core中的数据保护组件

2018年10月08日  | 移动技术网IT编程  | 我要评论

又名盐城站长网,黄洋事件最新进展,k724

背景介绍

在 owasp(开放式 web 应用程序安全项目) 2013 年发布的报告中,将不安全的直接对象引用(insecure direct object reference)标记为 十大 web 应用程序风险之一, 其表现形式是对象的引用(例如数据库主键)被各种恶意攻击利用, 所以对于api返回的各种主键外键id, 我们需要进行加密。

.net core 的数据保护组件

.net core 中内置了一个idataprotectionprovider接口和一个idataprotector接口。其中idataprotectionprovider是创建保护组件的接口,idataprotector是数据保护的接口。开发人员可以实现这 2 个接口,创建数据保护组件。
内置的数据保护组件

.net core 中默认提供了一个数据保护组件, 下面我们来尝试使用这个默认组件来保护我们的数据。

例: 当前我们有一个movie类,代码如下, 我们期望当获取movie对象的时候,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方法中配置使用默认的数据保护组件。

public void configureservices(iservicecollection services)
 {
  services.addmvc();
  services.adddataprotection();
 }

这段代码会启用.net core默认的数据保护器。

然后我们创建一个moviescontroller, 并在构造函数中注入idataprotectionprovider对象, 然后使用这个provider对象创建一个实现idataprotector接口的数据保护器对象

[route("movies")]
 public class moviescontroller : controller
 {
  private readonly idataprotector protector;
 
  public moviescontroller(idataprotectionprovider provider)
  {
   this.protector = provider.createprotector("protect_my_query_string");
  }
 }

tips: 使用provider创建protector的时候,我们传入了一个参数"protect_my_query_string", 这个参数标明了这个保护器的用途,你也可以把它就当成这个保护器的名字。

注意: 不同用途的保护器不能解密对方的加密字符串。, 如果使用了保护器a去解密保护器b生成的字符串,会产生以下异常cryptographicexception: the payload was invalid.

然后我们在moviecontroller中添加2个api, 一个是获取所有movies对象的,一个是获取指定movie对象的

[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方法对id字段进行了加密
  • 相应的在获取单个movie对象的api中, 我们需要使用idataprotector接口的unprotect方法对id字段进行解密。

最终效果

首先我们调用/api/movies, 返回结果如下, id字段已经被正确加密了

[{
 "id": "cfdj8d9klbqbeippoqwll5ulr6ygyo6avkgi2tecqgzqshnwsxc9apddsnyyd1k5iynhjhzcrogd6w31se3w6twm8h9udlepn4fjps5ukkqua0pmv6a0zzhbqsnlgoissnj29g",
 "title": "泰坦尼克号"
}, {
 "id": "cfdj8d9klbqbeippoqwll5ulr6wkmuyyzflizy3cwomhcao-np2woy4czil3wzd2fwi7tsy119tdefq7yaeye4o2w-kmbffpgxntdzznv2qbcram7-ayen35g3pkfayha3x7aq",
 "title": "我是谁"
}, {
 "id": "cfdj8d9klbqbeippoqwll5ulr6x2axm6ulcwts2-uqsfziu8uqutz-oazil-49d5-cyyl5h4mfzh8vihhcbj60mmrzolzla9qvb8eip6gyrkeap4nhktbzgxw0qu5r3edm6_kg",
 "title": "蜘蛛侠"
}, {
 "id": "cfdj8d9klbqbeippoqwll5ulr6zdzeltpivlkrlcd_v6mr2ktzwsckfygms0-cqhfaou4duwgtx6d402_eknobaofucleddf4mruedqawe71dda805umhbavx2712i7ugyo5ma",
 "title": "钢铁侠"
}]

然后我们继续调用api, 查询钢铁侠的电影信息

/api/movies/cfdj8d9klbqbeippoqwll5ulr6zdzeltpivlkrlcd_v6mr2ktzwsckfygms0-cqhfaou4duwgtx6d402_eknobaofucleddf4mruedqawe71dda805umhbavx2712i7ugyo5ma

结果也正确的返回了。

[{"id":4,"title":"钢铁侠"}]

带过期时间的数据保护器(limited lifetime)

.net core默认还提供了一种带过期时间的数据保护器, 这种数据保护器许多使用场景,最常用的场景就是当为一个重置密码操作的token设置失效时间, 这样一旦超时的, token就不能解密成功, 从而我们就可以认定重置密码操作超时了。

.net core中, 我们可以使用idataprotector接口的totimelimiteddataprotector方法创建一个带过期时间的数据保护器。

这里我们还是使用默认还是继续以上面的例子为例, 代码修改如下

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);
 }

代码解释

  • 这里我们定义了一个itimelimiteddataprotector接口对象protector, 并在构造函数中使用totimelimiteddataprotector方法,将一个普通的数据保护器转换成了一个带过期时间的数据保护器
  • 在获取movie列表的api中, 我们依然使用protect方法来加密id字段, 与之前不同的是,这里我们加入了第二个timespan参数,这个参数表示了当前加密的有效时间只有10秒。

最终效果

现在我们重新运行项目,还是和之前一样先调用/api/movies方法来获取movies列表, 结果如下

[{
 "id": "cfdj8d9klbqbeippoqwll5ulr6yzbdbz931toh32vc6jqg8dwsrmilroxoffvih4qwzne43jwsvzbjzjifctykznizknvbr50rrizpw2fe9utpajezbhi-h32effm-f0coluaa",
 "title": "泰坦尼克号"
}, {
 "id": "cfdj8d9klbqbeippoqwll5ulr6zddvymvftzk9lkbijeyuontzoeu0sc2-qfty6quxir2s8f3a1r44f9yz3sd_cylzup-_4gfjaasmfe8_ngylrjmdsjn9lz0g4vox0wjljiga",
 "title": "我是谁"
}, {
 "id": "cfdj8d9klbqbeippoqwll5ulr6zl-m2jzv2hcetihjevkxvi2216nerplp43tojcxtj4s52ll68slyqntg2fhhwlsomfgvyy5g4gm5skfasmmge1jbr20xc2b_djwdlhwlixna",
 "title": "蜘蛛侠"
}, {
 "id": "cfdj8d9klbqbeippoqwll5ulr6waozkchtg0lvgys3if_0_ead30a2yv8rjnagwlxudcskso3kys58hqdqaphw_khwnpd-hjdfl3hfpa8lowhyk901oc6zusxwzxflljavrefa",
 "title": "钢铁侠"
}]

等待10秒钟后,我们继续调用api, 查询钢铁侠的电影信息

/api/movies/cfdj8d9klbqbeippoqwll5ulr6waozkchtg0lvgys3if_0_ead30a2yv8rjnagwlxudcskso3kys58hqdqaphw_khwnpd-hjdfl3hfpa8lowhyk901oc6zusxwzxflljavrefa

返回了错误信息cryptographicexception: the payload expired at 9/29/2018 11:25:05 am +00:00. 这说明当前加密的有效期已过, 不能正确解密了。

tips: 使用action filter解密参数

在之前的代码中,我们在获取单个movie的方法中,我们手动调用了unprotected方法来解密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来改进这部分代码。

首先我们创建一个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实现了iactionfilter接口, 并实现了onactionexecuting和onactionexecuted方法
  • 在decryptreferencefilter类中,我们注入了默认的数据保护器提供器,并在构造函数中初始化了一个数据保护器
  • 在onactionexecuting中我们从routedata中获取到未解密的id字段, 然后将其解密之后,替换了之前未解密的id字段,这样modelbinder就会使用解密后的字符串来绑定模型。

最终修改

最后我们修改一下获取单个movie的api, 代码如下:

[httpget("{id}")]
 [decryptreference]
 public iactionresult get(int id)
 {
  var model = getmovies();

  var outputmodel = model.where(item => item.id == id);

  return ok(outputmodel);
 }

我们在获取单个movie的方法上添加了decryptreference特性。

运行代码之后,代码和之前的效果一样。

源码地址:

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网