【发布时间】:2014-05-29 10:19:05
【问题描述】:
在开发 project open 这是一个开源应用程序时,url http://[host_ip]:8000/register/ 包含容易受到跨站点脚本和身份验证绕过的 Java 脚本使用 SQL 注入。
我想知道如何避免它?我必须为此插入过滤器吗?我该怎么做?
如果问题不清楚,请告诉我。
【问题讨论】:
标签: javascript open-source tcl xss sql-injection
在开发 project open 这是一个开源应用程序时,url http://[host_ip]:8000/register/ 包含容易受到跨站点脚本和身份验证绕过的 Java 脚本使用 SQL 注入。
我想知道如何避免它?我必须为此插入过滤器吗?我该怎么做?
如果问题不清楚,请告诉我。
【问题讨论】:
标签: javascript open-source tcl xss sql-injection
SQL 注入问题的通用答案是“永远不要将任何用户输入作为 SQL 字符串的一部分发送到数据库”。任何可以作为参数的东西都应该这样做。因此,而不是(在某些可能与您正在查看的内容不完全匹配的方言中):
db eval "SELECT userid FROM users WHERE username = '$user' AND password = '$pass'"
你会的:
db eval "SELECT userid FROM users WHERE username = ? AND password = ?" $user $pass
# I personally prefer to put SQL inside {braces}… but that's your call
关键是因为数据库引擎只知道这些是参数,它从不尝试将它们解释为 SQL。 不可能注入。(除非你使用了写得不好的存储过程。)
如果您希望由用户指定表或列名,情况会变得更加复杂。这是您无法将其作为参数发送的情况;此类 SQL 标识符必须由 SQL 引擎解释。您唯一的选择是从用户提供的术语重新映射到您控制的术语,或者严格验证。
重新映射是通过一个单独的普通表来完成的,该表将用户提供的名称映射到您生成的名称:
db eval {SELECT realname FROM namemap WHERE externalname = ?} $externalname
由于生成的名称易于保证不含讨厌的字符,并且不是 SQL 关键字之一,因此可以在 SQL 文本中安全使用,无需进一步引用。您还可以尝试通过从请求中删除所有坏字符来执行每个请求的映射(当然,将映射代码分解为过程)。一个合适的regsub 可能是:
regsub -all {\W+} $externalname "" realname
但是我们需要额外的检查来确定它不是“邪恶的”:
# You'll need to create an array, SQLidentifiers, first, perhaps like:
# array set SQLidentifers {UPDATE - SELECT - REPLACE - DELETE - ALTER - INSERT -}
# But you can do that once, as a global "constant"
if {[regexp {^\d} $realname] || [info exist SQLidentifiers([string toupper $realname])]} {
error "Bad identifier, $externalname"
}
如您所见,最好将此类转换和检查分解到它们自己的过程中,以便您正确,一次。
您必须广泛测试您的代码。我再怎么强调也不过分。您的测试必须非常努力地破坏事物,通过任何人可以传递到软件中的每个可能的字段进行 SQL 注入;它们中的任何一个都不应该导致您的代码所期望的任何事情发生。
让其他人至少编写一些测试可能是个好主意;安全社区的经验表明,编写自己无法破解的代码相对容易,但编写别人无法破解的代码要困难得多。还可以考虑进行模糊测试,在接口处发送计算机生成的随机数据。在所有情况下,要么事情应该给出一个正常的错误,要么应该成功,但绝不会导致应用程序彻底失败。
(您可能会允许高度验证的用户(系统/数据库管理员)直接指定要评估的 SQL,以便他们可以执行诸如设置系统之类的操作,但他们只是少数情况。)
这实际上在概念上非常相似:它(主要)是由于某人在您的网站中放置的内容意外地被解释为 HTML(或 CSS 或 Javascript)而不是人类可读的文本(使用 SQL 注入,这是解释为 SQL 而不是数据)。因为在返回客户端时无法执行等效的参数化查询,所以必须谨慎引用。强烈建议您使用构建 DOM 树的适当模板库进行仔细引用(来自用户或来自数据库的数据仅作为文本节点插入)。
如果您希望用户提供一段标记的文本,请考虑在使用 Javascript 将其呈现为 Markdown 之前将其作为纯文本返回,或者在服务器上完全解析用户提供的文本以构造一个模型(例如,DOM 树)应交付的内容,然后将其作为从该模型生成的 HTML 发送回。
您不得允许用户指定您从中加载脚本或框架的位置。即使允许他们指定链接也令人担忧,但如果您不能将内容限制为纯文本,您可能必须允许这样做。 (考虑添加一种机制来列出用户提供的所有链接。考虑使用rel=nofollow 标记所有外部链接,除非您可以肯定地检测到它们进入了您列入白名单的某个地方。)
直接提供 HTML 是“仅限高度认证的用户”操作。
(我在上面撒了一个谎。您可以执行与 SQL 参数化查询等效的操作。您编写客户端执行的 JS 以使用 AJAX 查询获取用户数据,可能序列化为 JSON,并且然后在那里进行 DOM 操作以呈现它;实际上,您正在将 DOM 构造从服务器移动到客户端,但您仍在进行 DOM 构造,因为这是您如何正确处理的核心。您必须记住从不将检索到的内容插入为直接 HTML。客户端不能太信任服务器。)
我上面关于测试的cmets也适用于此。通过对 XSS 的测试,您希望注入类似 <script>alert("boom!")</script> 的东西;任何时候你可以进入并引发一个弹出对话框——除非你是一个直接有权直接编辑 HTML 的系统管理员——你有一个巨大的危险漏洞需要填补。 (尝试注射是一件非常好的事情,因为它非常明显,而且本身也相当良性。)
不要试图只使用正则表达式过滤掉<script>。要做到这一点太难了。
【讨论】: