ESFramework 开发手册(10) -- 安全机制

      在分布式通信系统中,安全无疑是非常重要的。

      ESFramework/ESPlus作为应用层的开发框架,在这里我们只讨论应用层的安全问题,因为如果黑客是在网络层或链路层进行攻击,位于应用层的系统几乎是无能为力的。从应用层来说,安全的重要性主要体现在以下几个方面:

(1) 防止恶意用户使用格式不正确的消息来试探服务端。

(2) 防止通信的消息被恶意用户截获,或者,即使被恶意用户截获,也无法破解其内容。

(3) 防止恶意用户在未成功登录前,就向服务器发送格式正确的伪装消息。

(4) 防止恶意用户使用巨大数量的空连接来消耗服务器的资源。

      ESFramework内置了一些安全机制,以对上述的安全性提供一些保障,下面我们一一说明。

 

1.消息格式验证

      ESFramework定义了通信消息的总体格式,ESPlus则定义了消息的详细格式。

      当网络引擎(无论是服务端的还是客户端的)从网络上接收到一批二进制数据时,会尝试去解析它。如果解析时发现,这批二进制数据的格式不是我们定义好的消息的格式时,将会认为其是非法消息。此时,网络引擎将会丢弃非法数据,并关闭对应的连接(如果引擎是基于TCP协议的),然后再触发INetEngine接口的InvalidMsgReceived事件。

        /// <summary>    
        /// 当接收到不完整或无法解析的数据时触发该事件    
        /// </summary>    
        event CbGeneric<UserAddress, MessageInvalidType> InvalidMsgReceived;

      事件的参数UserAddress说明了非法消息来源于哪个用户地址;而MessageInvalidType参数则说明了非法消息的类型:

            public enum MessageInvalidType    
            {        
                /// <summary>        
                /// 正常消息。        
                /// </summary>        
                Valid = 0,         
                
                /// <summary>        
                /// 消息尺寸溢出。        
                /// </summary>        
                MessageSizeOverflow,        
                
                /// <summary>        
                /// 无效的消息头        
                /// </summary>        
                InvalidHeader,        
                
                /// <summary>        
                /// 无效的标识符        
                /// </summary>        
                InvalidToken,        
                
                /// <summary>        
                /// 数据包长度不够        
                /// </summary>        
                DataLacked,        
                
                /// <summary>        
                /// 无效的客户端类型        
                /// </summary>        
                InvalidClientType    
            }

      从该枚举可以看出,网络引擎收到的数据无法解析的原因有几种:消息尺寸超过规定的大小,消息头无效、消息的标识符无效等。

 

2.消息加密

      对于一些关键的信息,是绝对不允许以明文的形式在网络上进行传送的。所以,消息在发送之前,必须进行加密。

      如果是直接使用原始的ESFramework引擎,我们就可以实现IMessageTransformer接口,然后将实现类的实例挂接在骨架流程的对应位置,以对进出的消息进行加密和解密动作,比如,像这样:

            public class MessageEncryptor : IMessageTransformer    
            {        
                public IMessage CaptureBeforeSendMessage(IMessage msg)        
                {            
                    //加密消息            
                    return Encrypt(msg);        
                }        
                
                public IMessage CaptureReceivedMessage(IMessage msg)        
                {            
                    //解密消息            
                    return Decrypt(msg);        
                }    
            }

    在使用ESPlus提供的Rapid引擎中,内部组装骨架流程时,是没有使用加密组件的,但是,我们仍然可以在发送自定义信息时,保证信息的安全。还记得我们是使用ICustomizeOutter发送自定义信息的,以Send方法为例:

            /// <summary>    
            /// 向服务器发送信息。    
            /// </summary>   
            /// <param name="informationType">自定义信息类型</param>   
            /// <param name="info">信息</param>   
            void Send(int informationType, byte[] info);

      在调用Send方法之前,我们可以先将要发送的内容info进行加密,然后再发送加密后的结果。

      而在接收方,会调用ICustomizeHandler的HandleInformation方法来处理接收到的信息:

            /// <summary>  
            /// 处理来自客户端的自定义信息。  
            /// </summary>  /// <param name="sourceUserID">发送该信息的用户ID</param>  
            /// <param name="informationType">自定义信息类型</param>  
            /// <param name="info">信息</param>  
            void HandleInformation(string sourceUserID, int informationType, byte[] info);

      在实现HandleInformation方法时,我们可以先解密info,然后再进行正常的业务处理。

      无论是挂接IMessageTransformer组件,还是在发送或接收自定义信息时,手动加解密信息,都要注意一点,那就是加解密都是要消耗CPU和内存资源的,对于那些高频通信的消息来说,这个开销是绝不可忽视的。所以,我们应该尽可能的只加密那些极其重要的消息/信息(根据MessageType或InfomationType来进行区分),而不是将所有的消息/信息一视同仁。

 

3.验证第一个消息

      有的恶意用户,在破解了消息的格式之后,会尝试在不登录的情况下,向服务器发送其他类型的请求消息。ESFramework支持在服务端对每个连接上收到的第一个消息进行验证,如果验证通不过,则将关闭对应的连接。

      验证第一个消息的组件的接口是IFirstMessageVerifier:

          public interface IFirstMessageVerifier  
          {      
              /// <summary>      
              /// 当每个TCP连接建立成功后,将会验证从该连接上接收到的第一个消息。如果通不过验证,服务端TCP引擎将会关闭对应的连接。       
              /// </summary>      /// <param name="firstMessage">TCP连接上的第一个消息</param>      
              /// <param name="address">TCP连接的客户端地址</param>      
              /// <returns>验证是否通过</returns>      
              bool VerifyFirstMessage(UserAddress address , IMessage firstMessage);  
          }

      我们可以实现这个接口,并将其注入到ITcpServerEngine对应的属性上。当VerifyFirstMessage方法返回false时,表示验证失败,此时,服务端网络引擎将会关闭对应的TCP连接。

      ESPlus提供的Rapid引擎,其内部已经实现了IFirstMessageVerifier接口来为我们验证第一个消息,它主要是保证第一个消息必须是Logon消息,再结合登陆账号密码验证,就可以解决恶意用户在未登录的情况下,就进行其它类型的业务请求。

 

4.绑定连接

      当Logon消息中的帐号密码通过服务端的验证之后,服务端会将该帐号与对应的TCP连接绑定起来,构成一个完整的Session。如果该连接上接收到的后续消息中,只要发现消息头中的UserID与该TCP连接绑定的帐号不一致,则认为该消息为非法消息,此时,服务端网络引擎将会关闭对应的TCP连接。如此,可以防止用一个帐号登录成功后,再用另一个帐号来请求服务。

 

5.空连接

      到这里,我们已经解决了本章开始提出的前三个问题,这就保证了恶意用户无法向服务器发送恶意的消息了。但是,恶意用户在应用层还可以做一件事情,就是消耗服务端的TCP连接。对于每个已成功建立的TCP连接,服务端都要为其分配一定的资源并对其进行管理。如果恶意的用户和服务器之间建立很多空闲的连接,对服务器资源的消耗也是不可忽视的。

      ESFramework提供的服务端引擎ITcpServerEngine支持及时地关闭上述的恶意用户的空闲连接。ITcpServerEngine有一个ExpiredSpanInSecs属性,该属性的含义是:某个TCP连接连上后,如果在ExpiredSpanInSecs时间内服务端网络引擎都接收不到来自该连接的任何数据,则将关闭该连接。

      一般正常的TCP连接建立成功后,客户端会立即向服务器发送Logon消息进行登录。所以,这个机制是可以成立的。我们可以将ExpiredSpanInSecs设为一个有效值(如3s),来减轻空连接的影响。之所以说是“减轻”,而不是“消除”,是因为在应用层系统中,无法完全规避这个问题,就按照3秒钟的超时来说,你服务端关闭连接的速度一定赶不上恶意用户建立连接的速度。

      这种情况在应用层来处理就非常的吃力。解决这个问题的更好办法,应该是在防火墙上做相关的策略设定,比如屏蔽掉恶意用户的IP地址,过滤由该地址发出TCP握手请求的Syn包,等等。

 

      阅读 更多ESFramework开发手册系列文章

-----------------------------------------------------------------------------------------------------------------------------------------------

下载免费版本的ESFramework 以及 demo源码

关于ESFramework的任何问题,欢迎联系我们:

电话:027-87638960

Q Q:372841921

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值