关于MIDAS的安全问题的解决方案

2/4/2006来源:Delphi教程人气:8502

 

发布问题,编译PRovider单元并将Provider.dcu文件和并入应用服务器目录中,编译应用服务器。这样Tclientdataset必须提供
'minercxy'+'select * from ...' 这样的命令才能被服务器承认。

我把'minercxy'暂且称为钥匙,钥匙可以自己进行随意设置,位数也可随意长度。当然钥匙的随机性越大安全性就越强了。

关于如果屏蔽Tclientdataset的providernames列表的问题,我在文章中也简单的写了一下,就算抛砖引玉吧。
我直接贴出来了

增强MIDAS的安全性

大家都知道,使用RemoteDataModule最令人头疼的就是安全性问题。
主要体现在:
1、远端只要知道应用服务器的端口号即可访问到应用服务器,而一旦访问到应用服务器,TClientDataSet即可获得ProviderNames列表。(观点:不让他轻易得到ProviderNames列表。)
2、一旦知道了ProviderNames列表,这就相当于将数据库暴露在外了。
例如:客户端可以通过SQL语句来对数据库进行操作了。(观点:我们的应用服务器根部不接受SQL语句。)

因为看到大家此类贴多如牛毛,又没有什么更好的解决方法,因此我发表一下我的拙见。

我对IAppServer接口进行了进一步的扩展,增强了RemoteDataModule的安全性。主要体现在:
1、客户端侦测到应用服务器的端口号可以建立与应用服务器的连接,但必须提供由TClientDataSet提供一GUID作为密码方能在设计阶段获得ProviderNames列表。此功能使得系统外部人员无法在设计阶段直接在TClientDataSet的ProviderName中直接获得应用服务器的TProvider实例。如果想通过IAppServer来获取ProviderNames列表则必须提供这一特定的GUID作为密码。
IAppServer的AS_GetProviderNames原形为
function  AS_GetProviderNames: OleVariant; safecall;
扩展后的函数为
function  AS_GetProviderNames(PassWord:WideString): OleVariant; safecall;
系统外部人员能够访问TRemoteDataModule的Provider的唯一方法就是猜测(或者成为蒙)出可能有的ProviderName直接赋值给TClientDataSet的ProviderName属性。当然这是十分困难的(只要你不是直接将datasetProvider1作为TdatasetProvider的名称)。
2、虽然恶意者可能通过其他方法(包括猜测、穷举)来获取到一个具有较高权限的TProvider,但是此步的安全特性完全将其挡在了门外。
TClientDataSet必须提供加密后的CommandText串才能得到应用服务的正确相应。因为这里的加密对象是SQL语句(一个字符串),所以可以使用n多种加密方法。如果应用服务器解密出的串为非法SQL串,会向客户端返回SQL语法错误信息。
而我在处理时并没有对SQL进行真正的加密,而是在TClientDataSet的CommandText中包含了一特定的字符串作为钥匙,而如果服务器得到请求后在CommandText中没有找到这一钥匙则返回“Missiong SQL property”异常。如果服务器得到了这一钥匙,则将这一钥匙从CommandText串中移除后交给TProvider进行处理。

实现:
听上去好像很玄,但实现起来比较简单:
我这里简单说说对SQL串的加密方法:
打开Provider单元,找到TDataSetProvider的SetCommandText方法。
你应该明白了吧。。。

如果你比我还菜,你就这样写:
var commandt:string;
begin
if CommandText='' then Exit;
if Copy(Commandtext,1,8)<>'minercxy' then Exit;
commandt:=Copy(CommandText,9,length(CommandText)-8);
if not(poAllowCommandTet in Options) then
DatabaseError(SCannotChangeCommandText);
CheckDataSet;
iproviderSupport(DataSet).PSSetCommandText(CommandT);
end;

 

发布问题,编译Provider单元并将Provider.dcu文件和并入应用服务器目录中,编译应用服务器。这样Tclientdataset必须提供
'minercxy'+'select * from ...' 这样的命令才能被服务器承认。

我把'minercxy'暂且称为钥匙,钥匙可以自己进行随意设置,位数也可随意长度。当然钥匙的随机性越大安全性就越强了。


 

欢迎大家讨论。

QQ:7943383
我的方法很简单.根本就不用PROVIDER.SERVER和CLIENT来回传递数据流,在SERVER的接口中添加各种自定义方法.我权衡觉得这么做好处很多.
避免了暴露PROVIDER的问题.
客户端可以有选择性地对数据进行压缩/解压缩,加密/解密.灵活性很大.客户端用猫连接我也不惧.
在提交数据时PROVIDER进行了太多封装,自动生成SQL语句等等.我总怕它生成的语句效率太低,尤其是在事务冲突时操作流程被封装了太让人不托底了,还是自己手工实现放心灵活.当然PROVIDER也可以手工处理提交,但那就跟我现在差不多一个意思了.
能少学点东西.比如主从表同时更新怎么拿PROVIDER处理这样的烦恼我就没了.
避免了PROVIDER的一些BUG.DATASNAP各部分都有一些BUG,有些还很严重.看大家经常提一些无法解释的怪异现象.我们不能总得造汽车之前先修机床啊.
第一点是这样的,实际上我是改写了IAppServer的接口,这样直接使用原来的控件进行开发,是无法获取provider列表的。因为我们的目的就是不希望系统之外的人来获取这个列表
每条语句都来一个钥匙是不是太麻烦了?何不在服务器都生成一个列表记录客户状态,客户登录时注册以下,以后在datatprovider事件中判断时候合法即可!如果有必要还可以在即可事件中判断!实现起来代码不是很多吧?
还有这样的办法解决,在服务器端定义一个方法
  procedure checkLogin(const UserName, Password: WideString); safecall;
  定义一个全局变量LoggedIn为 boolean
  获取数据时检查LoggedIn是否为真
在checkLogin内容为以下

procedure TXXX.Login(const UserName, Password: WideString);
begin
  {
  检测UserName,Password 是否为安全用户
  ...     }
  {如果成功}
  LoggedIn := True;
  {否则}
  LoggedIn := false;
  raise Exception.Create('Not logged in');
end;

客用端以DCOM为例 如下

在 dcom onlogin 事件中加入
DCOMConnection1.AppServer.Login(UserName, Password);
这样就可以防止只要知道DCOM GUID就可以连接到服务器上的安全问题