继续把dvwa环境安全级别调整为impossible
观察界面
看上去似乎和low级别没有大的区别,只是浏览器中get方法提交的参数多了一个。
impossible.php代码如下
prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
$row = $data->fetch();
// Make sure only 1 result is returned
if( $data->rowCount() == 1 ) {
// Get values
$first = $row[ 'first_name' ];
$last = $row[ 'last_name' ];
// Feedback for end user
echo "ID: {$id}
First name: {$first}
Surname: {$last}
";
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
在impossible级别这个界面上如果尝试注入是不会成功的。因为这里增加了2个安全防范措施。
1.Check Anti-CSRF token
在impossible.php中有以下代码,是为了防止CSRF攻击。可以参考《Web安全之CSRF攻击》
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
CSRF,Cross Site Request Forgery,中文是跨站点请求伪造。CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。
CSRF攻击是源于Web的隐式身份验证机制!Web的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。
CSRF攻击的一般是由服务端解决。
应对CSRF的方法有
在上面代码中就是采用的最后一种。
那么什么是Token呢?
Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。这样可以减轻服务器的压力,减少频繁的查询数据库。
使用Token有2种方式:
Token的好处是服务端不需要存储相应信息。
大概的流程是这样的:
当然,如果Token被不怀好意的人从中间获取到该信息时,也容易被利用,非法获取数据。 想增强安全性,一般可以在服务端生成时配合时间戳,服务端在接收到client发来带token的信息时,先检测token的时间戳信息,如果该时间戳在超过某个时间点时,就认为过期,需要重新获取。
Token一般用在两个地方:
现在我们来看看代码里如何生成的。
在登录界面/var/www/html/login.php和本页面都能生成Token
**// Anti-CSRF
generateSessionToken();
这个函数在/var/www/html/dvwa/includes/dvwaPhpIds.inc.php中
function generateSessionToken() { # Generate a brand new (CSRF) token
if( isset( $_SESSION[ 'session_token' ] ) ) {
destroySessionToken();
}
$_SESSION[ 'session_token' ] = md5( uniqid() );
}
验证就是在impossible.php中。
请注意:要避免"加token但不进行校验"的情况。在session中增加了token,但服务端没有对token进行验证,那根本起不到防范的作用。
我们可以尝试提交2次
http://192.168.99.100/vulnerabilities/sqli/index.php?id=1&Submit=Submit&user_token=9f874c66039244204e9951d4c7f7d551#
http://192.168.99.100/vulnerabilities/sqli/index.php?id=1&Submit=Submit&user_token=76f150ddea1eb5a7e670718f1f9f4d61#
可以发现浏览器的URL中2次的Token是不一样的。
2.sql预编译语句
观察以下代码,这个就是预编译。
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程。预编译语句能防止sql注入。
mysql4.1以后支持预编译。
在impossible.php中还限制了只允许返回一条数据。
if( $data->rowCount() == 1 )