如何保护云服务器免受SQL注入
SQL 注入利用了宽松的编码习惯。 这是一种攻击,恶意用户将代码提交到您的一个 Web 表单,而不是您尝试收集的任何数据。 恶意代码要么以您意想不到的方式查询您的数据库,要么突破您的 Web 应用程序并直接在您的云服务器上执行操作。 对毫无戒心的站点发起 SQL 注入攻击非常容易。 从第一天起,防范此类攻击就应该是您的首要任务之一。
第一步 - 学习识别易受攻击的代码
SQL 攻击总是采用包含两部分的用户提交的字符串的形式。 第一部分是猜测如何安全地终止代码尝试执行的命令; 第二部分是攻击者希望在您的 VPS 上运行的恶意代码。 下面是一个用户提交的字符串示例,旨在利用代码中的松散性:
x' AND user.email IS NULL; --
这看起来几乎就像你自己写的东西——这就是重点。 用户希望您的代码将采用此字符串,并在如下所示的 SQL 查询中使用它:
SELECT email, passwd FROM user WHERE email = 'x' AND user.email IS NULL; --';
这似乎没有多大作用,但根据您的应用程序的响应方式,它可以通知攻击者他们已经正确猜到了相关的表名。 在那之后,还有进一步的攻击可以开始泄露更多信息,例如用户名和密码。
要理解的关键细节是恶意代码试图关闭您的 SQL 引用,或者以其他方式突破 SQL 查询的约束,并请求或插入它自己的数据。
并非所有 SQL 注入攻击都需要右引号。 如果您的代码对数字执行查询,则不会将该数据放在引号中。 这使您容易受到以下类型的注入:
2097 OR 1=1
攻击者希望您的应用程序执行以下操作:
SELECT somedata FROM yourtable WHERE userid = 2097 OR 1=1;
您的代码希望上述 SQL 查询仅在用户 ID 与正确用户匹配时才返回数据。 但是 SQL 注入导致查询总是返回数据。
关键不是任何特定 SQL 注入的确切行为。 最重要的一点是所有 SQL 注入的共同特征:尝试猜测如何终止查询的一部分,并启动您未预料到的另一部分。 这是所有 SQL 注入的特征。 这就是如何与他们作战。
第二步 - 找到你的输入
在您的代码中,通过查找您的 HTML 表单来跟踪所有可能的 SQL 注入攻击入口点的最佳位置是 not。 当然,您可以通过这种方式捕获大量数据,但是用户可以通过其他方式输入数据,包括在 URL 中或通过您的一个 AJAX 接口。
不,最好的查看位置就在漏洞点——SQL 查询本身。 可能您的所有查询都是使用相同的基本命令执行的 - 或者可能是两个或三个不同的可能命令。 只需在您的代码库中搜索那些,您就会很快找到每个漏洞点。 例如,如果您的代码使用 Perl,那么您的所有查询都可能采用类似于以下的形式:
$result = mysql_query($sql)
在这种情况下,您可以使用类似于以下的命令从命令行快速找到所有漏洞点:
$ grep -R mysql_query *
第三步 - 清理您的输入
人们使用多种技术来防止 SQL 注入攻击,但您的前线防御应该是清理所有用户输入。 您绝不能假设用户会以您想要的格式提交数据。 事实上,你应该假设正好相反——他们想要提交正确的字符串来撕裂你的代码。
清理输入意味着所有用户提交的字符串都经过测试,以确保它们只包含安全字符,这些字符永远不会用于攻击。
您的 SQL 服务器将通过以下两种方式之一使用用户提交的字符串:
1. 作为一个数字,比如 2097
2. 作为字符串,例如用户名、密码或电子邮件地址
您的代码始终需要这两种形式的数据之一。 在本教程顶部给出的示例中,第一个示例需要一个字符串,第二个示例需要一个数字。 它将永远是一个或另一个。
还有两种清理数据的方法——好的方法和坏的方法。 不好的方法是检查它是否有可能的注射。 不好的原因是因为可能的注入次数如此之多,攻击者的创造力如此之大。 清理数据的好方法是确定正确输入的外观,并排除不符合这些约束的所有内容。
数字数据最容易清理,所以我们将首先介绍。 一个数字在左边有一个可选的减号,后跟一些数字,可能还有一个小数点。 没有其他的。 在 Perl 中,您可以像这样测试数字用户输入:
if($numericaluserinput !~ /^-?[0-9.]+$/) { # the user input is not a number }
很难想象任何敌对的注入会偷偷越过这个障碍。
清理文本输入更加困难,因为有很多方法可以尝试。 攻击者可能会以创造性的方式使用引号和反斜杠。 事实上,试图将用户输入中的特定字符列入黑名单是不受欢迎的,因为很容易错过一些重要的东西。
正如我们对数字输入所做的那样,一种更好的方法是将用户输入限制为一组列入白名单的字符。 例如,电子邮件地址可能仅限于字母、数字、破折号、下划线、加号、小数和 @ 符号。 在 Perl 中,您可以像这样测试电子邮件地址输入:
if($useremail ~= /^[0-9a-zA-Z\-_+.\@]$/) { # the user's email address is unacceptable }
对于其他类型的用户输入,例如用户名、家庭地址等,可以找到类似的白名单。
用户可能会反对白名单。 电子邮件地址的规范包括上述测试中未包含的各种符号。 当然,您可以自由地在测试中包含其他符号,但在这样做之前,您应该研究每个符号以确保它从未在 SQL 注入中使用过。 考虑到用户舒适度和站点安全性之间的选择,站点安全性必须始终放在首位。
使用列入白名单的字符集对输入进行清理很好,因为它很容易。 如果您愿意对用户可以提交的数据类型设置一些简单的限制,并且如果您勤于定期检查 SQL 查询以确保它们只使用经过清理的输入,那么您几乎可以消除 SQL 注入的风险.
第四步 - 接受有风险的输入
什么? 但是我们刚刚讨论了不接受风险输入的重要性。
确实,如果您的应用程序可以容忍对用户施加这种级别的限制,那么高度限制性的白名单就是要走的路。 但在某些情况下,对您的业务模型而言,不对用户输入施加任何限制可能很重要。
在这种情况下,您的应用程序中使用的编程语言可能具有库来保护您的代码免受恶意用户输入的影响。 例如,Perl DBI 库有一些方法可以防止用户输入超出预期的查询的特定部分:
my $sql = "INSERT INTO user (username, email) VALUES (?, ?)"; my $handle = $dbh->prepare( $sql ); $handle->execute( $untrustedusername, $untrustedemail);
在上面的例子中,'?' 字符用作占位符。 Perl DBI 库将它们替换为从用户那里收集的不受信任的变量。 但这样做时,DBI 库明确地将这些变量限制为仅与期望它们的字段相关。
其他语言也有类似的库,要么限制用户数据的使用,要么转义数据。
这种技术的好处是你可以相信维护库代码的人,他们会维护它,并保持它没有错误和安全漏洞。 这种技术的缺点是它的可读性稍差,因此您的某些 SQL 查询更有可能忘记使用适当的库调用来保护自己。
第五步 - 减轻可以通过的攻击
根据您的业务模型,您可能希望实施最后一道防线——完全独立于您的应用程序开发人员。 毕竟,也许其中一个人在某处使用了错误的白名单,或者未能调用适当的库调用来隔离用户数据。 代码中的一个漏洞可能会使您的整个站点容易受到 SQL 注入的攻击。
首先,您应该假设如果攻击者破坏了您的注入防御,他们现在已经获得了该 VPS 的完全 root 权限。 他们拥有这台机器,即使你正在对它进行所有的照顾和喂养。
为了减轻由此产生的影响,VPS 本身应该配置在网络的一个隔离部分中,以便该系统上的 root 用户无法查看或访问您的基础架构中的任何其他系统。 这种防御称为 DMZ,它非常酷,也超出了本教程的范围。
无论您使用何种机制来保护云服务器免受已经入侵的恶意用户的攻击,您仍应尝试设置某种警报系统,以便在 VPS 上记录某些活动时通知您的系统管理员。 这些指标将告诉您您的应用程序代码已被渗透,您需要立即检查整个代码库以找到错误。 如果没有这些警报,攻击者可以花时间潜入您的 DMZ,您可能永远不会知道有什么不对劲,直到他们带着您认为被隔离在一个完全不同的服务器上的所有信用卡号码潜逃,在一个“无法访问”的部分的网络。
第六步 - 聘请安全专家
如果您在没有安全专家帮助的情况下实施 Web 应用程序,那么您将在非常危险的水域中游泳。 如果由于某种原因您还没有准备好聘请安全专业人员,您应该考虑一种非常严格的方法来将用户输入中允许的字符列入白名单。 白名单的实施和审查相对简单,一个足够严苛的系统应该是相当坚不可摧的。 让您的用户体验受限字符集带来的轻微不便,同时您可以建立业务并准备进行所需的招聘。 一旦有人了解安全问题,您就可以更好地防范 SQL 注入、跨站点脚本攻击和许多其他困扰现代 Web 的危险安全问题。