先看源代码
if(isset($_POST['submit']) && $_POST['username'] && $_POST['password'] && $_POST['token']){
$username = $_POST['username'];
$password = $_POST['password'];
$token = $_POST['token'];
$sql = "select * from users where username=? and password=md5(?)";
$line_pre = $link->prepare($sql);
$line_pre->bind_param('ss',$username,$password);
if($token == $_SESSION['token']){
if($line_pre->execute()){
$line_pre->store_result();
if($line_pre->num_rows>0){
$html.= '<p> login success</p>';
} else{
$html.= '<p> username or password is not exists~</p>';
}
}else{
$html.= '<p>执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'</p>';
}
}else{
$html.= '<p> csrf token error</p>';
}
}
//生成token
set_token();
第一行代码:
isset($_POST['submit']) && $_POST['username'] && $_POST['password'] && $_POST['token']
前面已经说过了,这里就不多讲了,我们直接看最后一个。
token
为什么明明就没有token的输入框,却又要验证输入的token呢?
让我们看看网页的源代码。
<input type="hidden" name="token" value="6390565f66cf158db7033575345" />
type=hidden可以在表单中设置一些默认值,这些默认值不需要被用户直接修改,例如自动填充某些数据,或者记录用户的操作等。
另外一些敏感数据,例如用户的ID、会话ID等,不需要被用户看到,但又需要在表单提交时一起提交,这时候也可以使用type=hidden。
显而易见,token是已经设定好的默认值,不需要修改,可以自动填充的。
来看后面的代码:
$token == $_SESSION['token']
这里验证输入的token是否与默认的token值相同,这是为了防止密码爆破行为的出现。
就和前两关的验证码是一样的,第二关验证码的漏洞在于,如果默认验证码的值没有变化,那么就可以一直使用该验证码进行爆破,而第三关验证码的漏洞在于,它只做了前端验证,没有后端验证,将前端js禁用后,不论你的验证码输入了什么,都不会在后端进行验证的。
最关键的代码是这一句:
//生成token
set_token();
它会在后端代码每校验一次后,便重置token的值,导致原来的token值失效,从而阻止爆破的发生。
但绕过也很简单,将每次爆破使用token进行更新就行。