危险的二次开发功能(1分)

M

mstar

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

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

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

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

为什么有人要搞破坏呢?

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

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

控制举例
例如,DELPHI用户广泛使用的FastReport和报表机器就存在这样的隐患,当你在这些控件的开发环境里面去使用危险的指令的时候,你不会受到任何控制。怎么办呢,这些控件并没有提供这样的事件,允许你在执行SQL指令前去检查他们的安全性,好在他们都有源码,经过分析,你会发现容易控制问题的地方有2个,一个是数据控件的打开代码,如下:
function TRMDAstaClientDataSet.DoMethod(const MethodName: string;
Par1, Par2, Par3: Variant): Variant;
begin

Result := inheriteddo
Method(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 := inheriteddo
Method(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 ;

// 检查QRY的SQL指令的安全性
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来举了这个例子,实际上这个问题对所有的开发语言几乎都是存在的,该如何去避免危机的出现还是需要具体情况具体分析的。希望本文对还没有主意到这样问题的同行有所帮助。
 
G

gohoo

Unregistered / Unconfirmed
GUEST, unregistred user!
错了,你完全错了,二次开发包提供的就是简单易用的开发环境,功能强大的开发工具。
客户做什么是他自己的责任。我们没有必要去管。
updata,delte是常用的你怎么能禁止?
有的用户虽然是限制,但是可以对某些表完全操作的。
什么都限制,难道你的二次开发只支持开发报表?
你可以做个角色管理的功能(或者控件),用户帐号在数据库什么角色就能做什么操作。
 
M

mstar

Unregistered / Unconfirmed
GUEST, unregistred user!
出问题的时候 谁来负责 ?
还是希望 哪些带SQL开发的控件 提供方法 让别人可以控制一些危险的事情
 
M

mstar

Unregistered / Unconfirmed
GUEST, unregistred user!
接受答案了.
 

Similar threads

D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
1K
DelphiTeacher的专栏
D
D
回复
0
查看
881
DelphiTeacher的专栏
D
顶部