ASP.NET 安全问题的思索

这两天在自己的努力工作之下终于将早就应当完成的安全中心基本完成,也提供了基本的B/S界面。但当这个问题才完成我又发现了自己开发程序中出现的新的问题,问题基本是这样的:

    对于程序中的安全我将其封装为一个基本的类库来进行处理的,这个库就是XXX.Security,这个库中包括了处理安全所需要基本的接口和接口的基本实现。而现在开发完成了安全中心,这个安全中心就是这个基础XXX.Security中接口的其中一个实现。

   现在就需实现这个扩展的XXX.Security,其实实现这个扩展的安全程序集并不复杂,三五两下就搞好了。但我的想法是不能让外部程序来伪造其中的身份标识IIdentity,所以我这里将SecurityImpls程序信中的Identity定义内部类,这样外部就不可能会创建出这个IIdentity接口的对象,并且在招待SecurityImpls的各种接口之前先验证这个IIdentity接口是否为内部Iidentity的标识,这样有效的保证了这个Iidentity决不会是外部伪造的,这种方式用于处理C/S的Forms应用程序绝对没有问题,直接将SecurityImpls中验证产生的IIdentity对象进行保存就可能了并且也能够很好的进行工作。

  但当这种模式用来处理B/S模式时就产生了很大的问题,主要是B/S模式不能将这个IIdentity进行保存,而如果将这个IIdentity以某一种方式进行串行化后再写入到Cookie之中,那么又通过怎么样方式来重建这个Identity呢。因为这个IIdentity对象是内部类,外部只能够通过IIdentity接口来进行访问。这里我想了可能有以下方式来处理这个问题:

   一,在Cookie之中保存这个Identity的用户名和密码,验证类型等信息,在Application_Authenticate方式之中调用用户名和密码进行重新的验证,并在当前线程之中保存这个新生成的IIdentity对象。

  二,使内部IIdentity实现串行化的接口,再读取后由SecurityImpls之中的相关中重新生成返回新的IIdentity,并设置相应值。

  三,在SecurityImpls中取消对IIdentity的检查,只检查相应的标识值,通过标识值来进行授权检查。

 第一种方式可能有效的保证访问的安全性,但第次交互都进行这样的检查可能会产生性能上的问题,并且如果当前用户修改了密码,那么还必须重新登陆操作,并且如果这个验证过程需要很长的时候可能就更加的不可靠了。

 第二种方式就让客户代码有了制造伪造IIdentity的可能性。

 第三种方式方式就直接伪造的过程都不需要了。

那么这样才能够有效的解决这个问题呢,我想先想一些必须满足的条件再根据这些条件的设定来思考怎样的解决方案,这样比较能够解决这个问题:

   1,第一次的重新验证是不能够受的,这样会让其可能成为整个系统的性能瓶颈。

   2,没有任何的安全看起来是绝对的安全,所以想要设计一个完美的安全是不可能的。

   3,应当对采用Security来完成的应用程序充分的信任,要相信它是不会做坏事的,要是它想做也只是删除自己的数据而已。

   4,不应当增加ASP.NET在使用整个Security时的复杂度,太复杂的东西是最不可靠的。

   5, 不应当增加已有Security之中的整体结构,小范围内的修改是不可必免的。

通过以上原则可能看出,采用第二种方式来进行处理是一个看起来唯一可行的办法,而原来用于提供用户验证的Security接口是:

Code

而对于身份标识的串行和反串行化最应当了解的就是这个IAuthorizationProvider接口,如何让这个接口来支持新的操作相关操作呢,我想有两种方式一种方式是添加直接的接口来进行支持接口可能定义如下:

  public interface IAuthorizationProvider
{
    IPrincipal Authenticate(
object credence);
    
string AuthenticateToString(object credence);

}

 这种方式虽然将IAuthorizationProvider的问题处理了,但这里还有IAuthorizationProvider,原有的IAuthroizationProvider接口定义如下:

public interface IAuthorizationProvider

{

   bool CheckAuthorizate(IIdentity identity,params string[] activityNames);

   AuthorizationType[] QueryAuthorizate(IIdentity identity,params string[] activityNames);

  IActivityAuthorization[] Authorizate(IIdentity identity,params string[] activityNames);

}

 通过以上方式则必须添加以下接口方法:

public interface IAuhtorizationProvider

{

   .... // 原有接口。

  bool CheckAuthorizate(string identity,params string[] activityNames);

  AuthorizationType[] QueryAuthorizate(string identity,params string[] activityNames);

  IActivityAuthorization[] Authorizate(string identity ,params string[] activityNames);

}

 这样就上对于内部Identity串行化的问题影响到了整个Security两个核心接口。

这里还可能采用第二种方式,在IAuthenticationProvider之中添加两个新的接口方法,将对于串行化的问题限定在IAuthenticationProvider接口内部,更好的实现内聚性并最少的减少对代码的变化。

public interface IAuthorizationProvider

{

   IPrincipal Authenticate(object credence);

   string To(IPrincipal principal);

   IPrincipal Form(string principal);

 

}

提供两个新的接口方法来支持向string的转换,这两个方法只有在处理ASP.NET的安全问题或需要进行串行化传送时才需要使用,而开发WinForm程序则直接使用IPrincipal方式。

同时为了让ASP.NET程序在使用时能够以更好的方式来进行处理和显示像用户名这样的内容,因此对于新生成的string还作出以下规定格式上的规定:

<userName>:<authType>:<customData>。<userName>

<userName>当前用户名

<authType>指验证的方式

<customData>部分由IAuthorizationProvider根据内部Identity自动生成。

 先就想到这里,先将代码写上,出现问题再修改。

 (新)

经过昨天下午的重要思考我发现解决客户应用程序的伪造凭证这个问题通过使用内部的IIdentity实现类来解决是根本不能够行的,因为客户应用程序可以根据设置一个与实现类相同的运行环境来再调用IAuthenticationProvider来创建这个IIdentity,再将这个IIdentity来调用另一个实际使用的IAuthenticationProvider这样就完成了整个伪造的过程,并且有效的避开内部IIdentity创建的约束。

通过这种方式可以看出采用内部IIdentity对象对于避开伪造凭证是无效的,而且采用内部凭证在使用时也会带来非常的不便。

 因此如果想你对授权的凭证进行处理,还是只能通过IIdentity的Name属性来进行设置,而通过采用相同的Name设置格式,格式定义如下:

[UserName]:[DisplayName]:[CustomeData]

[UserName]:用户名

[DisplayName]:显示名

[CustomData]:内部设置的凭证

在IAuthenticationProvider之中不需要对Iidentity的类型进行检查,只需要直接将Iidentity对象直接使用即可。而在当前的安全中心之中CustomeData用来存放第一次的登陆验证,每一次登陆后生成一个GUID作为登陆的验证。这样就算伪造了用户凭证也在用户下次登陆后变为无效凭证,同时保证同一用户只有在一个客户端使用。

 

posted on 2008-10-25 10:15  蓝色游骑兵  阅读(473)  评论(0编辑  收藏  举报

导航