我必须处理一个存在重大安全问题的旧网站:SQL注入非常容易执行。
显然,防止这种攻击的最好方法是逃避查询中使用的内容(使用PDO编写语句,使用MySql编写mysql_real_escape_string等),但我们不能快速做到:整个站点都是程序性PHP (没有上课),查询在任何地方“准备好”,每天有数百个页面和数千个用户,新版本将尽快出现。
因此,从今天早晨开始,每个请求都会调用以下函数来检测基于关键字的可疑POST或GET参数。
const SQLI_UNSAFE = 3;
const SQLI_WARNING = 2;
const SQLI_SAFE = 1;
const SQLI_MAIL_DEST = 'monmail@mondest.com';
function sqlicheck() {
$params = array_merge($_GET, $_POST);
$is_warning = false;
foreach($params as $key=>$param) {
switch(getSafeLevel($param)) {
case SQLI_SAFE:
break;
case SQLI_WARNING:
$is_warning = true;
break;
case SQLI_UNSAFE:
mail(SQLI_MAIL_DEST, 'SQL INJECTION ATTACK', print_r($_REQUEST, true).' '.print_r($_SERVER, true));
header('Location: http://monsite/404.php');
exit();
}
}
if($is_warning === true) {
mail(SQLI_MAIL_DEST, 'SQL INJECTION WARNING', print_r($_REQUEST, true).print_r($_SERVER, true));
}
}
function getSafeLevel($param) {
$error_words = array('select%20','drop%20','delete%20','truncate%20','insert%20','%20tbclient','select ','drop ','delete ','truncate ','insert ',);
$warning_words = array('%20','select','drop','delete','truncate', ';','union');
foreach($error_words as $error_word) {
if(stripos($param, $error_word) !== false) return SQLI_UNSAFE;
}
foreach($warning_words as $warning_word) {
if(stripos($param, $warning_word) !== false) return SQLI_WARNING;
}
return SQLI_SAFE;
}
这似乎可以检测到某些类型的攻击,但它显然非常基本。有什么改进的想法吗?任何重大问题?
答案 0 :(得分:2)
首先,确保执行查询的数据库用户只具有选择,更新,删除权限。如果用户无法执行drop,则不会发生这种情况(这假设您的用户永远不需要创建或删除表,但如果他们这样做,您可以创建表级权限来保护大表)。 / p>
其次,您的脚本只会告诉您人们使用的是什么;它不会在网站范围内检查可能的查询;如果您网站的某个部分使用不多,您将不会收到任何邮件告诉您。最好只使用搜索工具梳理代码。
之后,你必须开始修改代码并进行转义和验证,这只需要一段时间。
答案 1 :(得分:0)
为什么不使用real_escape_string()
htmlentities()
stripslashes和过滤类的php并记录SQL查询,以便你可以看到人们发送给你的东西,使用sha256和一些md5你会没事的其他东西用于快速操作只需以二进制格式发送登录数据