使你的ColdFusion程序免受SQL注入攻击
出自9RIA.com WIKI
(英文原文:Secure your ColdFusion application against SQL injection attacks) 原作者:Ryan Wagener 翻译者:得失
ColdFusion不会比其他任何服务端语言(server side language)容易受攻击,同时ColdFusion的确有一些方法可以帮助你保护你的网站免受sql攻击(SQL attacks)。需要记住的是,安全问题不是因为ColdFusion很糟糕易受攻击或者其它语言有缺陷,它只是因为应用程序的编码有问题才导致的。
对于那些花了很多有宝贵时间来建站的开发人员来讲,他们很快会认识到,考虑安全问题很重要。某些不怀好意的用户会在任何时间内攻击你的网站,迫使你花时间识别和修正那些已经暴露的漏洞。如果你修改了你的网站,你甚至还需要花更多的时间来把网站恢复到以前的正常状态,与此同时,这会无可挽回地,影响了正常用户对你网站的印象。
当一些人非法获取权限来访问你的网站,他们会有许多方法来破坏网站,包括:
- 重定向(Redirecting)到另一个网站
- 删除,修改,添加动态内容
- 删除或修改数据表
- 导致网站不可访问或者报错
你可以采用一些基本的方法来极大地减少网站在未经授权的情况下被修改的机会。我会在本文中列举些小招数,你可以采用它们来保护你的网站。虽然有一些其它关于安全方面的知识需要理解,有一些步骤需要实践,但是这些小招数已经可以让你更好的理解保护网站的方法。
注意:本文用到的例子基于ColdFusion和Microsoft SQL Server。需要注意的是,这些产品不会比其它任何服务器软件差。大多数安全问题只是应用程序的编码问题,而不是底层技术造成的。
目录 |
什么是SQL注入
SQL注入(SQL injection)是一种攻击技术,某些恶意用户用它来修改你的sql语句,达到有别于初衷的其它目的(通常是恶意的)。SQL注入技术包括给数据库删除,修改,或添加条目,使你的网页产生变化。网站上任何容易被攻击的而且需要访问数据库来构造的动态页面,都有可能被修改成如下的样子:
- 显示一些跟你无关的东西
- 删除,修改或者添加一些内容
- 使用Javascript重定向(redirect)到另一个网站
SQL注入如何工作
在很多应用程序里,开发人员会使用GET和POST变量来构造SQL语句并执行。(GET变量通过URL传递,而POST变量包含在提交的表单)
黑客会修改这些变量的值,这通常不是开发人员所希望的。那些被修改了的GET或POST变量往往含有SQL指令,当你把它们添加到你的SQL代码里的时候,它们就会按照黑客的意图依次执行。
使用这种技术,黑客们只需要使你的数据库访问出错,就能在页面上看到数据表名称,比如,当某个SQL需要一个数字,而且它是当作字符串传入的,这个SQL就很可能导致错误。某些情况下,黑客们使用SQL注入技术来探知你所有的数据表名称和加入javascript重定向。
如何测试你的网站是否含有可能被SQL注入的漏洞
SQLFury是一个用Adobe AIR写的开发者工具,它可以对你的网站执行SQL注入扫描,来确认是否有漏洞。
数据表名称是如何暴露的
试想一个网站使用URL来传递ID,页面引用ID从数据库内查找一款特定品牌的汽车。比如:
http://localhost/cars.cfm?id=75
在ColdFusion,你可能会在cars.cfm页面内用如下的Sql语句,根据ID查找这款汽车。
<cfquery name="models" datasource="cars"> Select pkID, strModelName, strModelDescription from Models where pkID=#url.id# </cfquery>
如果一个恶意用户使用一个字符串做ID(比如http://localhost/cars.cfm?id=hello),就会引发一个数据库错误。随后,页面就会显示一个错误报告并暴露出数据表名和某些列名(见图一)。
现在这个恶意用户已经知道有一个数据表叫Models,某些列名叫 "pkID", "strModelName"和"strModelDescription"。有了这些信息,这个家伙就可以使用一些更高级的URL技术,比如:
http://localhost/cars.cfm?id=75%union%select%BASE_SCHEMA_VER%as%pkid,name%as%strmodelname,%nam%as%strModelDescription%from%sysobjects
然后,cars.cfm会使用如下的sql来查询:
union select BASE_SCHEMA_VER as pkid,name as strmodelname, name as strModelDescription from sysobjects
查询结果最终通过数据输出的方式暴露了所有的数据表。
这些步骤可以通过脚本自动执行,这让黑客不费吹灰之力就扫描你网站的漏洞。现在你可能在想,这需要大量的工作,但是做这些事情的人仅仅需要执行一个爬(spider)网站的脚本就能查找漏洞了。
保护你的数据表名称和数据库信息
你可以采用一些技术来隐藏你的数据表名称和数据库信息,包括:使用动态数据表名称,错误报告模板页面,最小化操作权限,和使用存储过程(stored procedures)。
使用动态数据表名称
你可以使用一些网站特有的数据表名称来隐藏数据库细节。比如,不要给数据表命名"employees"和"products",而要这么命名"ABC_Inc_employees"和"ABC_Inc_products"。
定义一个带有类似字符串(名称前缀)的变量在你的Application.cfm文件(或者其它名字的文件,该文件会被包含(included)在任何带有数据库访问的页面里)。比如,定义一个变量叫"stringDatabasePrefix",值是"ABC_Inc"。
然后把这个变量使用到你的SQL语句中,比如:
Select * from #stringDatabasePrefix#_employees
或者,你干脆把整个表名定义到变量内,然后把这个变量使用到数据库访问中。
比如,你可以在Application.cfm文件中(或其它被包含的文件(included file))使用下列代码。
<cfset strEmployees= "adobe_employees">
然后你可以用下面的格式来执行数据库查询:
Select * from #strEmployees#
现在,即使数据库访问抛出一个错误,用户也不会看到数据表的实际名称了。黑客们不得不猜猜strDatabasePrefix或strEmployees的内容,才会知道数据表名称。
使用错误报告模板页面
你可以使用自定义的错误报告模板来控制用户看到的信息。这不仅可以让你隐藏数据表名称,也能保持网站错误信息显示的一致性。
要设置一个自定义的错误报告页面,你需要在Application.cfm里使用cferror标签,使用template属性来指定什么错误显示什么页面。比如
<cferror type="exception" template="database_error_page.cfm" >
使用最低权限
给每个用户和过程授予最低的权限来访问它们需要的信息,往往是一个不错的实践(practice)。也就是说,比如,你应该配置你的数据库和程序,让某些不希望被修改的数据表仅仅只有读权限(read access)。实践中,你可以采用如下几条准则:
- 在Microsoft SQL Server中,针对每个用户,针对不同的表,定义合适的读写改,及其它权限。
- 当在ColdFusion Administrator中定义数据源(data source)的时候,使用高级设置(Advanced Settings)限制一些SQL命令。
使用存储过程(stored procedures)
你可以使用存储过程来隐藏数据表名称,分离程序和数据库逻辑。讨论存储过程超出了本书的范围,想知道更多请访问A Beginner's Guide to Using Stored Procedures with ColdFusion。
恶意数据是如何被插入到你的数据库里的
恶意用户也会使用GET和POST变量来插入一些会引起问题的数据。
通过GET变量发送SQL语句
试想有一张页面含有下列代码,是用来显示所有匹配课程ID的课程列表:
<cfquery name = "getFirst" dataSource = "cfsnippets"> SELECT * FROM courses WHERE intCourseID = #intCourseID# </cfquery>
课程ID作为URL的一部分,比如:
http://www.adobe.com?intCourseID=57
通过修改URL,恶意用户就可以执行额外的SQL语句了。
比如下面这个URL,它附着了一个SQL指令来删除employee表。
http://www.adobe.com?intCourseID=57;Drop table employees;
通过POST变量(表单里的变量)来发送SQL语句
除了上一节讲到的删除数据表的黑客技术,你也可以通过修改POST变量而不是GET变量来实现。
利用同样的原理,可以插入未授权的页面重定向(page redirect)到某个页面。试想一个含有下面表单的页面,它允许用户输入名字和题目(topic),然后页面把它们添加到数据库:
<form name="forumPost" action="forumPostp.cfm"> Name: <input type="text" name="strName" id="strName" >
Topic: <textarea name="txtTopic" id="txtTopic"></textarea>
<input type="submit" name="submit" value="submit" </form>
ColdFusion程序用下列代码来插入信息:
<cfparam name="form.strName" default="">
<cfparam name="form.txtTopic" default="">
<cfquery name = "getFirst" dataSource = "cfsnippets">
Insert into forumTopics(strName,txtTopic)
Values
('#strName#', '#txtTopic#')
</cfquery>
在一个独立的页面里,数据表每列数据将会以表格形式显示:
| NAME | TOPIC |
|---|---|
| Ryan | SQL Injection |
| Ryan |
由于对用户输入的文字没有限制,恶意用户可以在主题列里输入下面这类文字:
<script type="text/javascript"> window.location = "http://www.bad_site.com/" </script>
这些信息会被插入数据库,然后,无论什么时候用户访问带有这个数据表的页面,都会被跳转到www.bad_site.com
阻止恶意数据进入数据库
下面介绍一些方法来阻止恶意数据进入数据库。
使用cfqueryparam或者cfprocparam标签
为了保护你的数据库,你应该在每个cfquery标签里都使用cfqueryparam标签。
下面这段代码使用了cfqueryparam来确保intCourseID是一个整数(integer)类型,因此,它不可能包含额外的SQL命令(恶意指令)。
<cfquery name = "getFirst" dataSource = "cfsnippets"> SELECT * FROM #strDatabasePrefix#_courses WHERE intCourseID = <cfqueryparam value = #intCourseID# CFSQLType = "CF_SQL_INTEGER"> </cfquery>
这会确保intCourseID是一个整数,同时,不会带有额外的SQL命令。
除此以外,当你使用下面这段代码,它可以确保变量值仅仅包含字母和数字。
<cfqueryparam value="#strDescription#" CFSQLType="VARCHAR">
cfprocparam标签在存储过程中提供了类似的功能。
应用"最小权限"的法则
正如前面提到的,你应该通过在Microsoft SQL Server给每个表定义用户权限,来严格限制对数据库的访问。当在ColdFusion Administrator内建立一个数据源连接(data source connection),或者把数据源连接作为cfquery或cfstoreproc标签里的data source属性的时候,你可以定义一个限制级用户权限。
验证字符串数据
在接收GET和POST变量的时候,你可以使用cfparam标签来检测是否是你想要的类型。
比如,下面的代码确保了url.id是一个数字。
<cfparam name="url.id" type="numeric" default=0>
你还可以用cfif和cfqueryparam标签来检测字符串长度。
下面这段代码检测form.status的长度是不是大于20。
<cfif len(form.status) gt 20>
下面这段代码又用类似的方式检测strDescription。
<cfqueryparam value="#strDescription#" CFSQLType="VARCHAR" maxlength="20">
使用cfapplication标签的scriptProtect属性
你可以使用cfapplication标签的scriptProtect属性来使你的变量免受跨站脚本攻击(cross-site scripting attacks)。在ColdFusion Administrator设置页面中,当前激活的Global Script Protection选项就是脚本保护的默认设置。你可以使用scriptProtect属性来覆盖(override)这个默认设置,比如:
<cfapplication scriptProtect="all">
这种脚本保护机制通过定义在neo-security.xml文件里的正则表达式,来查找GET或POST里的恶意变量。
你可以编辑neo-security.xml文件,来自定义那个正则表达式。
比如:
<var name=";.*(select|insert|update|delete|drop|alter|create)">
<string>SQL_INJECTION_ATTEMPT</string>
</var>
写代码来查找潜在的恶意关键字
你也可以自己写代码来检测GET和POST变量是否含有恶意关键字。
为了这个目的,在Application.cfm文件里建立一个恶意关键字列表,比如:
<cfset strList="select, insert, update, delete, drop">
要在GET变量里搜索这些关键字的话,用类似下面的代码:
<cfif len(cgi.query_string) gt 0> //Do some action here to check what is being passed </cfif>
下面这段代码在一个表格里查找所有的POST变量是否含有关键字"Select":
<cfif IsDefined ("form.submit")>
<cfloop index="i" from="1" to="#numberoffields#">
<cfset test=form[variables.formfield]>
<cfloop list="#strList#" index="i">
<cfif FindNoCase(i, test)>
</cfif>
</cfloop>
</cfloop>
</cfif>
确保每个访问请求都是来自你网站的其它页面
当有些人来攻击你的网站的时候,他们通常不会手动点击链接或者键盘输入网址,一般会写一个脚本来攻击某些有漏洞的页面。
比如,试想一个网站含有一个页面叫myForm.cfm,页面里包含一个链接指向myFormProcessing.cfm页面,该页面会执行一个数据查询。现在,一个恶意用户不会直接通过点击myForm.cfm页面来攻击myFormProcessing.cfm页面。
你可以使用下面的技术来阻止这类攻击。
首先在myFormProcessing.cfm页面,你要验证HTTP_REFERRER是否匹配myForm.cfm页面的URL,比如:
<cfif HTTP_REFERER IS NOT "<your_url>/myForm.cfm"> </cfif>
你也可以在你的表单里实现图形检测,要求用户输入图片里显示的字母和数字。这种验证码检测方案是用来区别实体输入还是脚本自动录入。
第三类方案就是在表单里添加session,但是把session作为隐藏值传递。页面处理表单的时候你就可以检测那个隐藏值是否匹配session值。
下面的代码片段实现了这种技术:
In myForm.cfm:
//set a random number <cfset formValidator=Randomize(100000)#> //Set a session with this value <cfset Session.formValidator = formValidator > // add the random number in your form <input type="hidden" name=" formValidator" value="#formValidator#">
In myFormProcessing.cfm:
//check to see that the form value equals the session value, if not then kick the user out. If the user does pass this step then delete the session <cfif form.formValidator eq session.formValidator>
你还可以通过加密随机数字来使得这套方案更加安全,然后只要在解密后确认它还是那个数字就行了。或者,你可以传递一组键值对(a value and a key),而不是随机数字,用键值来解密。更多信息见ColdFusion livedoc Encrypt章节。
加密URL字符串中的数据
当URL附着一个查询字符串,使用ColdFusion的Encrypt()和Decrypt()方法。这样,最终用户就看不到附着在URL上的变量名了。
使用HTMLEditFormat
当你允许一个最终用户在一个文本框内输入一些信息的时候,使用HTMLEditFormat来编码(encode)用户输入的HTML。这能阻止用户在表单里提交JavaScript代码。
现在你该怎么做
本文概述了保护ColdFusion网站免受恶意攻击的一些步骤。
为了自动化测试,你可以使用SQLFury,这是一个基于Adobe AIR runtime的开发者工具。它可以做SQL注入扫描,来识别网站中的SQL注入漏洞。更多信息见http://www.sqlfury.com/。
SQL注入只是一种普通攻击技术,你必须知道,黑客们还会使用一些其它注入技术,包括轻量级目录访问协议(LDAP),对象关系映射(ORM),用户代理(User Agent),XML等等。
更多ColdFusion的安全知识见ColdFusion开发者中心的Security page。
感谢Denis-Claude Fleury对本文的贡献。
本文许可证:Creative Commons Attribution-Noncommercial 3.0 Unported License.
关于作者
Ryan Wagener自从2002年就开始使用ColdFusion 6,做网络程序的开发。他目前是一名企业开发顾问,主要关于Flex,FMS和ColdFusion方面。