mirror of https://github.com/Mabbs/mabbs.github.io
				
				
				
			
		
			
	
	
		
			126 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Markdown
		
	
	
		
		
			
		
	
	
			126 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Markdown
		
	
	
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								layout: post
							 | 
						|||
| 
								 | 
							
								title: 关于制作考试(答题)系统的研究
							 | 
						|||
| 
								 | 
							
								tags: [考试, 答题]
							 | 
						|||
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  在答题系统的安全性上,没有人胜利……<!--more-->   
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# 答题系统的来由
							 | 
						|||
| 
								 | 
							
								  在我维护的花火学园上,有一套答题系统。另外我能成为花火学园的运维也是因为这个系统,这件事也是个很神奇的事情。   
							 | 
						|||
| 
								 | 
							
								  具体内容也充满了机遇性,在我上高中的一天,我偶然发现了花火学园论坛,发现了之后我就想注册,然而这个论坛注册要求挺高,需要邀请码,如果没有就要去答20道题来获得邀请码。   
							 | 
						|||
| 
								 | 
							
								  那个题很难,正常人会的可能只有不到一半,如果有兴趣也可以看看[花火考场](https://www.say-huahuo.com/answer/),对于这些题同样我也不会。不过不会没关系,我以前在做网上的考试时,从来都不会安心看题,总会想着按下F12看看有没有什么不答题也能通过的方法。   
							 | 
						|||
| 
								 | 
							
								  按正常来说,应该没有人会把答案放到网页源代码中,但是在我做的好多次网络考试中,他们都把答案放到了源代码之中。我想这也许是为了降低服务器的运算压力吧……但是这样考试有点计算机知识的人都能把答案找出来,那考试还有啥意义……   
							 | 
						|||
| 
								 | 
							
								  总之就是因为这样奇怪的程序员很多,所以我每次在网上答题前都会按下F12看看有没有什么更简单的通过方法。   
							 | 
						|||
| 
								 | 
							
								  当时我看了看花火考场的源代码,是打包后的vue代码,看来是没法从网页源代码这里入手了,那第二点就是查看网络请求的数据了。   
							 | 
						|||
| 
								 | 
							
								  我大概看了一下,这个答题系统的获得题目和提交都是由同一个php程序工作的,过程是答题之前获得一组题目,答完后将答案和一组表示题目的ID组合到一起提交到这个程序上,提交后如果分数超过70分就会生成一个邀请码。看着这个过程,我突然脑洞大开,想着既然这个程序阅卷时只用ID和答案,那么我如果会做其中一道题,让这个程序反复批已知答案的同一道题,那么我就可以得满分了吧。   
							 | 
						|||
| 
								 | 
							
								  结果还真是这样,有点不可思议,于是我就成功的注册了花火学园。   
							 | 
						|||
| 
								 | 
							
								  注册后我就给管理员说了这个问题,也是这样的一次机会我加入了花火学园的程序组当中。   
							 | 
						|||
| 
								 | 
							
								  这个答题系统是由[you06](https://github.com/you06)大佬开发的,不过我反馈了这个问题以后,貌似问题仍然没有被解决……而我一般看代码也是有一定的机遇,只有心情适合写代码或看代码的时候才愿意去写,所以我成为管理员以后我也没有解决。   
							 | 
						|||
| 
								 | 
							
								  关于答题系统这个东西我一直觉得开发起来很简单,像客观题就是比对一下答案是不是一致就行了,所以说我上了大学以后,我一直想着和同学一起做个答题系统练练手,可惜现代大学生最喜欢干的事情就是摸鱼,所以一直都没有做答题系统。   
							 | 
						|||
| 
								 | 
							
								  不过前几天正好我的心情符合看代码的状态,然后就看了看我能不能解决这个问题。
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# 修复漏洞
							 | 
						|||
| 
								 | 
							
								  我闲来无事打开看了看阅卷的这个小程序,整个代码很短,也很好理解。当时加入程序组后我和you06说用session解决这个问题也许不错,虽然问题没解决,但是能看到他已经写了一部分了,也许是因为调试时出问题了,所以写了一半之后就再没管。   
							 | 
						|||
| 
								 | 
							
								  我看了看之所以you06的代码出了问题,其实主要原因是因为环境变量上少加了下划线,所以没读到数据才导致程序出问题的,既然问题找到了,我很轻松的就改了过来。
							 | 
						|||
| 
								 | 
							
								   
							 | 
						|||
| 
								 | 
							
								# 参考代码
							 | 
						|||
| 
								 | 
							
								  既然漏洞解决了,那我觉得即使大家看到程序的源代码也没关系,我检查了很多遍,也没看出问题,所以现在我将阅卷系统的核心代码展示出来:
							 | 
						|||
| 
								 | 
							
								```php
							 | 
						|||
| 
								 | 
							
								<?php
							 | 
						|||
| 
								 | 
							
								  Session_start();
							 | 
						|||
| 
								 | 
							
								  $filename = "problem.json";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  if ($_SERVER['REQUEST_METHOD'] === 'GET') {
							 | 
						|||
| 
								 | 
							
								    // query problem data
							 | 
						|||
| 
								 | 
							
								    $json_string = file_get_contents($filename);
							 | 
						|||
| 
								 | 
							
								    $questionSet = json_decode($json_string, true);
							 | 
						|||
| 
								 | 
							
								    $questiones = array();
							 | 
						|||
| 
								 | 
							
								    for ($i = 0; $i < 20; $i++) {
							 | 
						|||
| 
								 | 
							
								      $index = mt_rand(0, sizeof($questionSet) - 1);
							 | 
						|||
| 
								 | 
							
								      array_push($questiones, array(
							 | 
						|||
| 
								 | 
							
								        'title' => $questionSet[$index]['title'],
							 | 
						|||
| 
								 | 
							
								        'code' => $questionSet[$index]['code'],
							 | 
						|||
| 
								 | 
							
								        'options' => $questionSet[$index]['options'],
							 | 
						|||
| 
								 | 
							
								        'img' => $questionSet[$index]['img']
							 | 
						|||
| 
								 | 
							
								      ));
							 | 
						|||
| 
								 | 
							
								      array_splice($questionSet, $index, 1); 
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    $_SESSION['questiones'] = $questiones;
							 | 
						|||
| 
								 | 
							
								    echo json_encode($questiones);
							 | 
						|||
| 
								 | 
							
								    die();
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								  if ($_SERVER['REQUEST_METHOD'] === 'POST') {
							 | 
						|||
| 
								 | 
							
								    // check result
							 | 
						|||
| 
								 | 
							
								    $answers = json_decode(file_get_contents("php://input"), true);
							 | 
						|||
| 
								 | 
							
								    $score = calcscore($answers);
							 | 
						|||
| 
								 | 
							
								    $pass = $score >= 70;
							 | 
						|||
| 
								 | 
							
								    $invitecode = '';
							 | 
						|||
| 
								 | 
							
								    if ($pass) {
							 | 
						|||
| 
								 | 
							
								      $invitecode = invite();
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    echo json_encode(array(
							 | 
						|||
| 
								 | 
							
								      'score' => $score,
							 | 
						|||
| 
								 | 
							
								      'pass' => $pass,
							 | 
						|||
| 
								 | 
							
								      'invitecode' => $invitecode
							 | 
						|||
| 
								 | 
							
								    ));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								if (isset($_SESSION['questiones'])) {
							 | 
						|||
| 
								 | 
							
								unset($_SESSION['questiones']);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    die();
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  function calcscore($answers) {
							 | 
						|||
| 
								 | 
							
								    $score = 0;
							 | 
						|||
| 
								 | 
							
								    $filename = "problem.json";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    $json_string = file_get_contents($filename);
							 | 
						|||
| 
								 | 
							
								    $questionSet = json_decode($json_string, true);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    for ($i = 0; $i < sizeof($answers); $i++) {
							 | 
						|||
| 
								 | 
							
								      if ($answers[$i]['answer']) {
							 | 
						|||
| 
								 | 
							
								        for ($j = 0; $j < sizeof($questionSet); $j++) {
							 | 
						|||
| 
								 | 
							
								          if ($answers[$i]['code'] == $questionSet[$j]['code'] &&
							 | 
						|||
| 
								 | 
							
								            $answers[$i]['answer'] == $questionSet[$j]['answer']) {
							 | 
						|||
| 
								 | 
							
								              $score += 5;
							 | 
						|||
| 
								 | 
							
								          }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								      }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    $matchCount = 0;
							 | 
						|||
| 
								 | 
							
								    if ($_SESSION && $_SESSION['questiones']) {
							 | 
						|||
| 
								 | 
							
								      for ($i = 0; $i < sizeof($answers); $i++) {
							 | 
						|||
| 
								 | 
							
								        for ($j = 0; $j < sizeof($_SESSION['questiones']); $j++) {
							 | 
						|||
| 
								 | 
							
									  if ($answers[$i]['code'] == $_SESSION['questiones'][$j]['code']) {
							 | 
						|||
| 
								 | 
							
									    $matchCount++;
							 | 
						|||
| 
								 | 
							
									  }
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								      }
							 | 
						|||
| 
								 | 
							
								    } else {
							 | 
						|||
| 
								 | 
							
								      return 0;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    if ($matchCount == sizeof($answers)) {
							 | 
						|||
| 
								 | 
							
								      return $score;
							 | 
						|||
| 
								 | 
							
								    } else {
							 | 
						|||
| 
								 | 
							
								      return 0;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  function invite() {
							 | 
						|||
| 
								 | 
							
								    //generate invite code
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# 结尾
							 | 
						|||
| 
								 | 
							
								  其实答题系统想让用户钻不了空还是相当简单的事情,不过我个人还是希望那些答题系统能不要写的太精密,这样谁都不会胜利,互相为难也不好嘛……
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# 后记
							 | 
						|||
| 
								 | 
							
								  我的愚蠢超乎我想象,这个代码的漏洞应该说完全没有解决,看似复杂的代码就如同破铜烂铁一般,轻易就能击碎QAQ。   
							 | 
						|||
| 
								 | 
							
								  我完全没有测试就胡乱改,改完之后除了让代码看起来更加复杂外没有解决任何问题。今天一位名叫[Sora Jin](https://github.com/MoeLoli)的大佬像曾经的我一样测试了这个垃圾答题系统,重新发现了这个漏洞……   
							 | 
						|||
| 
								 | 
							
								  不过这次的修复不怎么美观,代码我就不放上来了,不是很难的问题,有兴趣的读者可以自己研究一下。
							 |