WEB安全 - 伪造请求-越过前端JS校验

  • 作者:KK

  • 发表日期:2017.10.25


要点速读

  1. 这种问题一般在1-2年工作经验以下的新手中容易发生,主要是由于缺少对HTTP通讯模式的基本理解造成的

  2. 无论前端写了多么完美的校验,如果服务端不对请求过来的参数进行校验,那别人通过代码发送请求模拟参数过来的时候就可以对服务端耍手段了


尝试

  1. 被请求的代码

    先在A项目下准备一个被请求的服务端代码,文件名叫count.php吧,以PHP代码为示例:

    $count1 = $_POST['count1'];
    $count2 = $_POST['count2'];
    $result = ($count1 + $count2) * 2;
    echo $result;
    

    以上代码接收一个POST过来的count参数,并进行一些数学计算后输出结果


  2. 发起请求的代码

    接下来我们另外独立建一个新的B项目,在这里开一个send.php,代码如下:

    $url = 'http://test.com/count.php';
    $params = [
    	'count1' => ['a', 'b'],
    	'count2' => 999,
    ];
    
    $curlHandler = curl_init();
    curl_setopt($curlHandler, CURLOPT_URL, $url);
    curl_setopt($curlHandler, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curlHandler, CURLOPT_POST, 1); //用POST方法请求
    $params = http_build_query($params);
    curl_setopt($curlHandler, CURLOPT_POSTFIELDS, $params); //设置POST的参数
    $responseContent = curl_exec($curlHandler);
    curl_close($curlHandler);
    
    header('Content-type:text/plain; charset=utf-8');
    echo "以下是A项目的返回内容:\n\n\n\n";
    echo $responseContent;
    

    以上代码是curl的请求代码,其实懂curl的同学基本不会踩入这个安全雷区,所以可以大概猜到他们是不熟悉curl的,相关文章参考:《PHP进阶 - curl


  3. 开始测试

    执行B项目的代码,让它向A项目发起请求

    执行方式1:命令行cd到B项目下,执行php send.php就会运行里面的代码了

    执行方式2:B项目搭个web,然后通过http://127.0.0.1/send.php来访问触发

    重点是它的本质就是发起了一次http请求给另一端(不过是不是相同域名,只有能接受http请求)

    结果A项目那边就返回了报错信息,因为count1变量是一个数组,无法转换成数字进行数学计算


总结

以上尝试过程就体现出了A项目的count.php在没有对请求参数进行校验的情况下,B项目将count1参数弄成了个数组发送过去,导致了A项目计算出错(数组无法参与数学运算),而A项目预期是count1和count2两个参数都是发送数字过来的

然而实际上发送端可以不老实地发送非数字过来,我们不能认为所有请求都是来自浏览器,只要可以编程,极大部分语言都可以发起http请求,下面附一个node.js的请求代码:

var http = require('http');
//var http = require('https');

const postData = querystring.stringify({
	'count1[0]' : 'a',
	'count1[1]' : 'b',
	'count2' : 999
});

var options = {
	hostname: 'kk',
	port: 80,
	path: '/count.php',
	method: 'POST',
	headers: {
	'Content-Type': 'application/x-www-form-urlencoded',
	'Content-Length': Buffer.byteLength(postData)
	}
};

var req = http.request(options, (res) => {
	res.setEncoding('utf8'),
		responseContent = '';
	res.on('data', function(data){
		responseContent += data;
	});
	res.on('end', function(){
		console.log('以下是A项目的返回内容:\n\n\n\n' + responseContent);
	});
});

req.on('error', (e) => {
	console.error(`problem with request: ${e.message}`);
});

req.write(postData);
req.end();

安全准则

在服务端代码的角度,所有http请求来源发送的参数都必须经过校验,否则可能是别人模拟请求发过来的,该数字就判断数字,该字符就判断字符,否则严重情况可能造成经济损失,甚至服务端产生大量不必要的错误日志

前端代码校验只是为了让用户更早地知道所填写的选项和参数是否有误,并且避免向服务端发起请求造成服务端计算压力,但前端校验本质上是防君子不防小人的——用户是君子,黑客是小人