我正在制作一个网页,允许用户从搜索字段中搜索mysql数据库,代码如下:
<script>
function finduser(str) {
if (str == "") {
document.getElementById("txtstatus").innerHTML = "";
return;
} else {
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("txtstatus").innerHTML = this.responseText;
}
};
xmlhttp.open("GET","search.php?q="+str,true);
xmlhttp.send();
}
}
</script>
和search.php
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<?php
$q = htmlspecialchars($_GET['q']);
$con = mysqli_connect('localhost','user','pass','db');
if (!$con) {
die('Could not connect: ' . mysqli_error($con));
}
mysqli_select_db($con,"db");
$sql="SELECT * FROM people WHERE CONCAT( firstname, ' ', lastname) LIKE '".$q."' OR phone LIKE '".$q."' OR email LIKE '".$q."'";
$result = mysqli_query($con,$sql);
$num_rows = mysqli_num_rows($result);
echo "<div class='records'><small>" . $num_rows . " records</small></div>";
while($row = mysqli_fetch_array($result)) {
if (is_null($row['hidden'])) {
} else {
continue;
}
echo "<div class='people'><small><a href='" . $row['id'] . "'>" . $row['firstname'] . " " . $row['lastname'] . "</a> submitted on " . date("M j\, Y", strtotime($row['timestamp']))"</small></div>";
}
echo "</ul>";
mysqli_close($con);
?>
</body>
</html>
几个问题:
1) 这段代码是安全的,以防止攻击,如mysql注入攻击?如果不是,我该如何保护它?
2) 我想强加一个速率限制,在这个限制中,用户每秒只能搜索一定的次数,以防止洪水泛滥。一分钟5个问题。这只是一个个人项目,没有像财务信息的利害关系或类似的,但我想被展示如何正确地做到这一点。最好的办法是什么?
我怀疑已经有人告诉我把登录凭证放到一个单独的php文件中并包含它。我打算这么做,有人能告诉我为什么这是一个好的做法吗?是不是服务器在html请求时意外地发出了原始php?
1条答案
按热度按时间drkbr07n1#
这段代码是关于一些最常见的web应用程序漏洞的试验品。:)这很好,对其他人也有教育意义。
sql注入
您正在使用
htmlspecialchars
错了。它不是一个输入净化函数,而是一个输出编码函数。它是无用的方式,你使用它,应该从那里删除。由于没有有效的保护,上面的代码很容易受到sql注入的攻击q
与此类似的参数:union select * from other; -- \
(注意开头的空格)。这样,您在第17行的查询将是
请注意
htmlspecialchars
对反斜杠没有任何作用,也不必。为了可读性和字符转义的工作方式,删除like表达式字符串实际上等于一些解决方案是使用准备好的语句、phppdo或orm。这个问题在这里已经得到了多次解答。它甚至在php手册中有描述。
您应该始终对sql注入有适当的保护,并且永远不要像您那样将sql语句与参数连接起来。
跨站点脚本(xss)
上面的代码可能易受xss攻击,这取决于数据进入数据库的方式。但不管怎样,通过清理动态输出,您的代码应该对xss更加健壮。
问题是在javascript中,您使用
innerHTML
将数据插入dom,只需用php从数据库中读取数据,在写入页面之前从不进行编码。如果田野firstname
或者lastname
如果包含像javascript脚本标记这样的html代码,它将被写入并插入页面,javascript将被运行。为了避免这种情况,你应该
使用
htmlspecialchars()
,htmlentities()
,或输出编码库来正确编码输出。请注意,前两个函数用于html上下文,它们不适合javascript(这里不适用,因为您是用纯html编写的)。所以在您的示例中,您应该对中的变量应用编码echo
线条,就像htmlentities($row[firstname])
等。在javascript中,避免
innerHTML
如果可以的话,使用innerText
相反。当您的php使用标记编写html时,您不能这样做,这没关系,但是所有变量都需要按照上面所示进行编码。请注意,虽然
id
列将永远是用户输入,作为最佳实践,您不应该将变量单独写入链接的href中。考虑以下xss:<a href="javascript: alert(1)">
,如果id为javascript:...
. (顺便说一句,将这样的id写入href也没有多大意义,除非数据库中有非常时髦的id。)插入url的未编码字符串(非常弱的url注入)
在javascript中
str
函数的参数应该是用户输入的(可能来自输入字段的值,或者通过某种逻辑)。该字符串直接连接到一个url,在复合攻击中可能会或不会被利用。这不是一个直接的弱点,但我想说这是一个弱点。在将该值添加到查询的url之前,应该对其进行url编码。硬编码凭证
正如您正确指出的,凭证不应硬编码。但原因并不是php文件会以文本的形式返回。如果是的话,一个不同的php也不会有帮助,因为它也会以文本的形式返回。:)但是你会如何把它放到版本控制中呢?任何能够访问您的代码库的人都可以访问生产机密,这在许多环境中是不可接受的。此外,对这些凭证的更改将意味着一个新的“版本”,一个新版本的软件,因为您必须更改代码,而不仅仅是配置。
因此,作为最佳实践,机密(至少在产品版本中)应该从安全的地方读取,例如环境变量,web服务器可以为php设置环境变量。这样,如果访问权限设置正确,您就必须是Web服务器用户(甚至是根用户)才能访问这些凭据(或者必须模拟应用程序进程,这当然足以读取其环境)。
如何准确地为php进程设置环境变量取决于您的web服务器,但是在php中读取环境变量的方法类似于
getenv("databasepassword")
.这可以防止文件包含攻击获取您的机密(因为机密不在应用程序可访问的文件中),还允许您自由地将任何代码签入到版本控制中。
潜在拒绝服务(dos)
你问题中的查询效率很低,因为
concat()
在两个字符串字段上,用于like
比较。正如您所提到的,速率限制(throttling)是您可以选择的一个选项,但在php中实现并不简单。
你可以在你的web服务器上做这个速率限制,这是离题的,但是apache,nginx等都可以做到这一点,如果配置正确的话。
您可以实现类似验证码的功能,以便有效的查询只能由人发送。
您可以为这些函数实现身份验证,并接受通过身份验证的用户利用dos的剩余风险。你可以用不同的方法来处理,例如服务条款。