[长度限制]
有时候输入对数据的长度加以限制会使攻击困难许多,这的确阻止了一些攻击,但一个很短的SQL语句也可能造成非常大的危害:
Username: ';shutdown--
关闭SQL-Server,只用了12个字符。另一个例子:
drop table
如果长度限制是在字符串过滤后,另一个问题可能会发生。假设用户名被限制在16个字符之内,密码也被限制在16个字符之内,下面的用户名和密码结合可以执行'shutdown'命令:
Username:aaaaaaaaaaaaaaa'
Password:'; shutdown--
原因是程序过滤用户名最后的单引号,但是字符串又被切回到16个字符,删除了过滤的单引号。结果是密码域可以包含一些SQL, 只要它以一个单引号开始,最后的查询会变成这样:
select * from users where username = 'aaaaaaaaaaaaaa'' and password=''';shutdown--
用户名在查询里就变成:
aaaaaaaaaaaaaaa' and password='
后面附上的SQL被执行。
[躲避审核]
SQL Server在sp_traceXXX系列的函数包含丰富审核接口,它可以记录任何数据库里的事件。这里我们特别感兴趣的是T-SQL事件,它记录了所有的SQL语句以及服务器上准备好的和已运行了的批处理。如果这个级别的审核开启的话,所有我们讨论的注入都将被记录下来有经验的数据库管理员将会看到所有发生的事情。但是如果攻击者附加下面的字符:
sp_password
到一个Transact-SQL语句,这个审核记录如下:
-- 'sp_password' was found in the text of this event.
-- The text has been replaced with this comment for security reasons.
这在所有的的T-SQL日志记录时都会发生,即使'sp_password'出现在注释中。这当然是在用户传递sp_password时有意隐藏用户的明文密码,但这对攻击者相当有用。
所以,为了隐藏所有的注入攻击者只需要在注释符'--'后面加一个字符串:
Username: admin'--sp_password
事实上一些执行了的SQL将被记录,但是查询字符串本身被强制不记录。
[防 范]
这部分讨论一些针对这些攻击的防范措施。输入验证已经讨论过了,一些代码也给出了,后面我们研究SQL-Server防范问题。
[输入验证]
输入验证是一个很复杂的问题。一般在一个开发项目中它很少被注意,因为过度的验证往往使一个程序的某部分被打断,所以输入验证是个难题。输入验证往往不加到程序的功能里,因而在工期将至而赶程序时不会被人注意。
下面是关于验证的简单的讨论附示例代码,这个示例代码当然不能直接用在程序里,但可以很好的说明不同的策略。
各种数据验证的途径可以分类为以下几种:
1)整理数据使之变得有效
2)拒绝已知的非法输入
3)只接受已知的合法的输入
有很多概念上的问题;首先,开发者没有必要知道非法数据由什么组成,因为新形式的非法数据随时都可能产生。第二,改变数据会改变它的长度,这样会导致前面提到的问题。最后,还有需要对系统已有数据的重用的话有二次注入的问题.
解决方案2也会遇到和1的一些相似的问题,了解非法数据会过时,因为新的攻击技术也在发展。
解决方案3可能是三种方法中最好的,但是比较难于执行。
从安全角度来考虑可能最好多解决方法是把解决方案2和3结合起来只允许合法的输入,然后再寻找非法字符。
一个必须结合这两种途径的例子是带有连字符的名字的问题:
Question Bassington-Bassington
我们必须在合法输入里允许连字符号,但是也要明白字符串'--'在SQL-Server里意味着什么。
当数据整理结合了非法字符验证时另一个问题就会发生。假设我们应用“非法字符探测器”来探测'--','select'和'union'”后使用“数据整理过滤器”删除单引号,攻击者就可以指定这样的输入:
uni'on sel'ect @@version-'-
因为单引号被过滤器删除了,攻击者可以把单引号散布于它的已知的非法字符串里来躲避检查。
下面是一些验证的代码:
方法1-躲避单引号
function escape( input )
input = replace(input, "'", "''")
escape = input
end function
方法2-抵制已知的非法输入
function validate_string( input )
know_bad = array( "select", "insert", "update", "delete", "drop", "--", "'")
validate_string = true
for i = lbound( know_bad ) to ubound( known_bad )
if( instr( 1, input, known_bad(i), vbtextcompare) <> 0 )
validate_string = false
exit function
end if
next
end function
方法3-只允许合法输入
function validatepassword( input )
good_password_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
validatepassword = true
for i = 1 to len( input )
c = mid( input, i, 1 )
if ( instr( good_password_chars, c ) = 0 ) then
validatepassword = false
exit function
end if
next
end function