畅享博客 > 安仕达信息化分享 > 信息化构想 > [讨论]危险的二次开发功能,软件开发商要小心了。
2008-7-30 17:56:35

[讨论]危险的二次开发功能,软件开发商要小心了。

安仕达软件中的二次开发功能曾经被恶意攻击过,我们吸取了这次教训,软件升级后对二次开发功能进行了更严格的安全性检查,本文介绍了对数据库管理类MIS系统二次开发可能存在危险的安全漏洞,以及正确的处理方法。


看起来很美
许多MIS软件都号称提供很完美的二次开发功能,这本来很不错,增加了软件的灵活性和通用性。这些二次开发程度不同,开发程度高的可能提供设计环境,允许开发者使用宏语言进行开发(其基本原理是解释执行),程度差些的也提供自己定义SQL指令并执行的功能。

 

危险在暗处
实际上,在里面隐藏着相当大的危险!更严重的是各软件开发商要么闭口不谈、要么根本不知道存在这样的严重问题。

 

关键的问题出现在什么地方呢?

 
我们知道,对于宏指令系统,如果存心搞破坏,确实是很难预防的(一个简单处理的办法是,开发商对二次开发宏代码进行检查和认证),但是,这样的破坏动作(如格式化)对计算机水平要求是很高的,一般的二次开发人员并没有这个水平。真正容易出问题的是哪些执行SQL指令的场合,随便一个高中生或者大学毕业生,也许他的计算机水平不怎么样,但是他仍然可以很容易地利用SQL指令进行破坏性工作,这些危险的SQL指令并不难掌握,例如DELETE UPDATE ALTERDROP… …

 

为什么有人要搞破坏呢?

林子大了,什么鸟 都有?总有些人对开发商或者软件用户不满,如果你给了他这样的机会,那么软件开发商显然也是要一大部分责任的。即时今天不出问题,也许明天就会出,未来是 不可预料的,电子数据也是企业宝贵的财产,并且删除后无法还原,更严重的是一些跟利益打交道的软件领域(例如冲值卡、会员卡……),如果数据被伪造,后果是无法想像的。

 
如何去避免

跟二次开发技术原理有关

首先,软件开发商必须自己有这样的安全理念,其次跟该软件二次开发技术的实现原理是有很大关系的,使用DELPHIVCL控件技术来实现二次开发的,很明显要比使用ACTIVEX或者DLL来实现类似功能的更加容易控制安全性,因为后者你没有办法去对他进行修改。

 
控制举例

例如,DELPHI用户广泛使用的FastReport和报表机器就存在这样的隐患,这些控件的二次开发功能相当强大,客户非常喜欢,我们用他们来举例子说明如何预防危险的SQL命令。

当你在这些控件的开发环境里面去使用危险的指令的时候,你不会受到任何控制。怎么办呢,这些控件并没有提供这样的事件,允许你在执行SQL指令前去检查他们的安全性,好在他们都有源码,经过分析,你会发现容易控制问题的地方有2个,一个是数据控件的打开代码,如下:

function TRMDAstaClientDataSet.DoMethod(const MethodName: string; Par1, Par2, Par3: Variant): Variant;

begin

  Result := inherited DoMethod(MethodName, Par1, Par2, Par3);

  if Result = Null then

    Result := LinesMethod(FQuery.SQL, MethodName, 'SQL', Par1, Par2, Par3);

  if MethodName = 'EXECSQL' then

  begin

    OnBeforeOpenQueryEvent(FQuery);

    FQuery.ExecSQL;

  end;

end;

 
我们可以插入我们自己的检查
SQL指令代码

 
function TRMDAstaClientDataSet.DoMethod(const MethodName: string; Par1, Par2, Par3: Variant): Variant;

begin

  Result := inherited DoMethod(MethodName, Par1, Par2, Par3);

  if Result = Null then

    Result := LinesMethod(FQuery.SQL, MethodName, 'SQL', Par1, Par2, Par3);

  if MethodName = 'EXECSQL' then

  begin

    SQLParser.SQL.Text := FQuery.sql.Text ;

    SQLParser.Deconstruct; // 进行分解

    CheckSQLParser(SQLParser);

    OnBeforeOpenQueryEvent(FQuery);

    FQuery.ExecSQL;

  end;

end;

 

另外一个地方是,在控件提供的定义QUERY字段的地方,例如下面这样的代码

 
    … …

    Query.DataSet.FieldDefs.Update;

    … …

我们修改后 是这样的

    … …

    CheckDataSet(Query.DataSet) ;// 我们自己开发检查方法

    Query.DataSet.FieldDefs.Update;

    … …

 

补充:如果你自己注册了解释器去执行一些SQL指令,同理你也需要自己去在这些函数里面检查SQL指令的安全性

 
如何检查安全性

 
分析我们已经知道的危险指令,并且考虑我们实际应用的需要,我们可以知道,即使是危险指令,如果针对的目标是临时表,那么我们也不需要去阻止执行指令,因为也许在二次开发过程终就是需要对这些临时表进行操作的。因此我们最后实现的
SQL指令安全性检查代码大约是下面这样的

 
//
检查是否临时表

function IsTempTable(const TblName : string ) : boolean ;

var

  temp : string ;

Begin

  temp := Trim(Uppercase(TblName)) ;

  Result := True ;

  if temp = '' then

    Exit ;

  if Temp[1] = '#' then

    Exit ;

  if Copy(Temp,1,4) = 'Temp' then

    Exit ;

  Result := False ;

End ;

 

// 检查QRYSQL指令的安全性

procedure CheckDataSet(DataSet : TDataSet);

var

  Temp :string ;

Begin

  temp := '' ;

  if DataSet is TQuery then

    temp := TQuery(DataSet).sql.text ;

  if DataSet is TAdoQuery then

    temp := TAdoQuery(DataSet).SQL.Text ;

  if DataSet is TAstaClientDataSet then

    temp := TAstaClientDataSet(DataSet).SQL.Text ;

  if Temp <> '' then

  Begin

    SQLParser.SQL.Text := Temp ;

    SQLParser.Deconstruct; // 进行分解

    CheckSQLParser(SQLParser);

  end;

End ;

 

procedure CheckSQLParser(SQLParser : TAstaSQLParser) ;

Begin

    if not IsTempTable(SQLParser.InsertTable) then

      raise Exception.Create('二次开发错误:插入指令必须针对临时表!');

    if not IsTempTable(SQLParser.DeleteTable) then

      raise Exception.Create('二次开发错误:删除指令必须针对临时表!');

    if not IsTempTable(SQLParser.UpdateTable) then

      raise Exception.Create('二次开发错误:更新指令必须针对临时表!');

    if SQLParser.Alter <> '' then

      raise Exception.Create('二次开发错误:ALTER指令不允许使用!');

    if SQLParser.Drop <> '' then

      raise Exception.Create('二次开发错误:DROP指令不允许使用!');

 

End ;

 
后话

当然了,这里只是拿DELPHI来举了这个例子,实际上这个问题对所有的开发语言几乎都是存在的,该如何去避免危机的出现还是需要具体情况具体分析的。希望本文对还没有主意到这样问题的同行有所帮助。


推荐到鲜果:

评论

您正在以 匿名用户 的身份发表评论  快速登录
(不得超过 50 个汉字)
       看不清,换一个
提示消息
(输入完内容可以直接按Ctrl+Enter提交)