用户输入、PHP、Javascript 和安全性
我正在开发一种路线服务,用户可以输入出发地和目的地地址,并获取路线表(提供逐段信息)以及显示路线的地图。
下面是完整的源代码 ( getdirections.php ):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Directions</title>
<style>
* { font-family: Verdana; font-size: 96%; }
label { width: 15em; float: left; }
label.error { display: block; float: none; color: red; vertical-align: top; }
p { clear: both; }
.submit { margin-left: 12em; }
em { font-weight: bold; padding-right: 1em; vertical-align: top; }
</style>
<script src="jquery-1.3.1.js" type="text/javascript">
</script>
<script src="http://maps.google.com/maps?file=api&v=2&sensor=false& amp;key=[Your Key Here]"
type="text/javascript">
</script>
</head>
<body onunload="GUnload()">
<div id="container">
<div id="directform">
<form id="direct" action="getdirections.php" method="get">
<p><label for="loc1">From Here:</label>
<input id="loc1" type="text" name="location1" class="required" /></p>
<p><label for="loc2">To Here:</label>
<input id="loc2" type="text" name="location2" class="required" /></p>
<p><input type="submit" value="Search" /></p>
</form>
</div>
<?php
function filterInput ( $input ) {
$replacement = ',';
$input = preg_replace('/(\n|\r)+/', $replacement, $input);
$replacement = " ";
$input = preg_replace('/(\t)+/', $replacement, $input);
$inputarray = explode(' ', $input);
foreach ( $inputarray as $i => $value ) {
$ch = '';
if ( $value[strlen($value)-1] == ',') {
$ch = ',';
$value = substr($value, 0, -1);
}
$value =
preg_replace('/^(\&|\(|\)|\[|\]|\{|\}|\"|\.|\!|\?|\'|\:|\;)+/', "", $value);
$inputarray[$i] =
preg_replace('/(\&|\(|\)|\[|\]|\{|\}|\"|\.|\!|\?|\'|\:|\;)+$/', "", $value);
$inputarray[$i] = $inputarray[$i].$ch;
}
$filteredString = implode(" ", $inputarray);
return $filteredString;
}
?>
</div>
<table class="directions">
<tr>
<td valign="top">
<div id="directions" style="width: 100%"></div>
</td>
</tr>
<tr>
<td valign="top">
<div id="map_canvas" style="width: 250px; height: 400px"></div>
</td>
</tr>
<td valign="top">
<div id="directions_url"></div>
</td>
</table>
<noscript><b>JavaScript must be enabled in order for you to use Google Maps.</b>
However, it seems JavaScript is either disabled or not supported by your browser.
To view Google Maps, enable JavaScript by changing your browser options, and then
try again.
</noscript>
<script type="text/javascript">
// This programming pattern limits the number of global variables
// Thus it does not pollute the global namespace
// for_directions is the only global object here.
for_directions = function(){
// The map is loaded into the div element having id specified by mapid
// private variable
var mapid = "map_canvas";
// The direction listing is loaded into the div element having id specified by directionsid.
// private variable
var directionsid = "directions";
// From here
// private variable
var location1;
// To here
// private variable
var location2;
// The functions ( init and addevent ) are public methods of for_directions object
return {
// Called on loading of this page
// public method
init: function (){
location1 = "<?= filterInput($_GET['location1']) ?>" || 0;
location2 = "<?= filterInput($_GET['location2']) ?>" || 0;
var directions = document.getElementById(directionsid);
directions.innerHTML = "Please check the address and try again";
if ( GBrowserIsCompatible() && location1 != 0 && location2 != 0){
mapAddress(location1, location2);
}
},
// This method is cross browser compliant and is used to add an event listener
// public method
addEvent:function(elm,evType,fn,useCapture){
if(elm.addEventListener){
elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent) {
var r = elm.attachEvent('on' + evType, fn);
return r;
} else {
elm['on' + evType] = fn;
}
}
};
// Called from init
// private method
function mapAddress ( address1, address2 ){
var geocoder = new GClientGeocoder();
var directions = document.getElementById(directionsid);
var i = 0;
geocoder.getLatLng( address1, function(point1){
if (point1){
geocoder.getLatLng ( address2, function(point2){
if (point2){
getDirections();
} else {
directions.innerHTML = "Please check the address and try again";
}
});
} else {
directions.innerHTML = "Please check the address and try again";
}
});
}
// Called from mapAddress to load the directions and map
// private method
function getDirections( ){
var gmap = new GMap2(document.getElementById(mapid));
var gdir = new GDirections(gmap,document.getElementById(directionsid));
gdir.load("from: " + location1 + " to: " + location2,
{ "locale": "en_US" });
generateURL();
}
function generateURL(){
var url = "http://maps.google.com/maps?saddr=";
url += location1;
url += "&daddr=";
url += location2;
var a = $("<a></a>").attr('href',url);
$(a).text("Google Maps");
$("#directions_url").append(a);
}
}();
// The (); above results in the function being interpreted by the browser just before the page is loaded.
// Make for_directions.init as the listener to load event
// Note that the init method is public that why its accessible outside the object scope
for_directions.addEvent(window, 'load', for_directions.init, false);
</script>
</body>
</html>
如果您在系统上尝试此代码,请将其命名为 getdirections.php。 您唯一需要更改的是 google 地图 api 密钥。 您可以在此处获取密钥。
生成密钥后,将其放入密钥参数中(为了方便起见,复制了下面的行):
<script src="http://maps.google.com/maps?file=api&v=2&sensor=false& amp;key=[Your key here]"
type="text/javascript">
从上面的代码可以看出,我通过 PHP 获取输入并在 Javascript 中进行处理。 现在,我不希望用户逃脱任何类型的输入(javscript、危险的 HTML 等)。 我尝试在 PHP 中使用 urlencode 函数。 但是,JavaScript 代码不接受编码的用户输入,即使输入正确也会失败。
作为解决此问题的方法,我在 PHP 中编写了一个 filterInput 函数,该函数将替换/删除某些字符并阻止用户尝试通过输入执行 Javascript 代码。
这很有效。 但是,当用户确实尝试提供恶意输入时,比如“+alert("hello")+”,同时包含开头和结尾引号,filterInput 函数会修剪前导和尾部引号,结果字符串如下
+alert("hello")+
:执行下面的代码:
location1 = "<?= filterInput($_GET['location1']) ?>" || 0;
PHP 用其返回值替换函数调用,如下所示:
location1 = "+alert("hello")+" || 0;
脚本的执行因上面的行而停止,并出现错误(缺少 ; before 语句)
注意,如果我没有修剪引号并使用 $_GET['location1 '] 我会直接得到。
location1 = ""+alert("hello")+"" || 0;
警报(“你好”)将被执行!
所以,我陷入了困境。 如果我过滤输入,我会在某些用户输入上收到 javascript 错误,如果我不过滤输入,我将允许用户执行任何类型的 javascript。
我的问题是:
- 处理网络输入的正确且安全的方法是什么?
- 这种跨语言的用户输入(从 PHP 到 Javascript)可以吗?
- 除了用户能够执行 JavaScript 之外,还有哪些其他类型的安全威胁会导致这段代码容易受到攻击?
谢谢阅读!!
请帮忙。
I am working on a directions service where users enter the from and to addresses and get the directions table ( that gives turn by turn information ) along with a map showing the route.
Below is the complete source code ( getdirections.php ):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Directions</title>
<style>
* { font-family: Verdana; font-size: 96%; }
label { width: 15em; float: left; }
label.error { display: block; float: none; color: red; vertical-align: top; }
p { clear: both; }
.submit { margin-left: 12em; }
em { font-weight: bold; padding-right: 1em; vertical-align: top; }
</style>
<script src="jquery-1.3.1.js" type="text/javascript">
</script>
<script src="http://maps.google.com/maps?file=api&v=2&sensor=false& amp;key=[Your Key Here]"
type="text/javascript">
</script>
</head>
<body onunload="GUnload()">
<div id="container">
<div id="directform">
<form id="direct" action="getdirections.php" method="get">
<p><label for="loc1">From Here:</label>
<input id="loc1" type="text" name="location1" class="required" /></p>
<p><label for="loc2">To Here:</label>
<input id="loc2" type="text" name="location2" class="required" /></p>
<p><input type="submit" value="Search" /></p>
</form>
</div>
<?php
function filterInput ( $input ) {
$replacement = ',';
$input = preg_replace('/(\n|\r)+/', $replacement, $input);
$replacement = " ";
$input = preg_replace('/(\t)+/', $replacement, $input);
$inputarray = explode(' ', $input);
foreach ( $inputarray as $i => $value ) {
$ch = '';
if ( $value[strlen($value)-1] == ',') {
$ch = ',';
$value = substr($value, 0, -1);
}
$value =
preg_replace('/^(\&|\(|\)|\[|\]|\{|\}|\"|\.|\!|\?|\'|\:|\;)+/', "", $value);
$inputarray[$i] =
preg_replace('/(\&|\(|\)|\[|\]|\{|\}|\"|\.|\!|\?|\'|\:|\;)+$/', "", $value);
$inputarray[$i] = $inputarray[$i].$ch;
}
$filteredString = implode(" ", $inputarray);
return $filteredString;
}
?>
</div>
<table class="directions">
<tr>
<td valign="top">
<div id="directions" style="width: 100%"></div>
</td>
</tr>
<tr>
<td valign="top">
<div id="map_canvas" style="width: 250px; height: 400px"></div>
</td>
</tr>
<td valign="top">
<div id="directions_url"></div>
</td>
</table>
<noscript><b>JavaScript must be enabled in order for you to use Google Maps.</b>
However, it seems JavaScript is either disabled or not supported by your browser.
To view Google Maps, enable JavaScript by changing your browser options, and then
try again.
</noscript>
<script type="text/javascript">
// This programming pattern limits the number of global variables
// Thus it does not pollute the global namespace
// for_directions is the only global object here.
for_directions = function(){
// The map is loaded into the div element having id specified by mapid
// private variable
var mapid = "map_canvas";
// The direction listing is loaded into the div element having id specified by directionsid.
// private variable
var directionsid = "directions";
// From here
// private variable
var location1;
// To here
// private variable
var location2;
// The functions ( init and addevent ) are public methods of for_directions object
return {
// Called on loading of this page
// public method
init: function (){
location1 = "<?= filterInput($_GET['location1']) ?>" || 0;
location2 = "<?= filterInput($_GET['location2']) ?>" || 0;
var directions = document.getElementById(directionsid);
directions.innerHTML = "Please check the address and try again";
if ( GBrowserIsCompatible() && location1 != 0 && location2 != 0){
mapAddress(location1, location2);
}
},
// This method is cross browser compliant and is used to add an event listener
// public method
addEvent:function(elm,evType,fn,useCapture){
if(elm.addEventListener){
elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent) {
var r = elm.attachEvent('on' + evType, fn);
return r;
} else {
elm['on' + evType] = fn;
}
}
};
// Called from init
// private method
function mapAddress ( address1, address2 ){
var geocoder = new GClientGeocoder();
var directions = document.getElementById(directionsid);
var i = 0;
geocoder.getLatLng( address1, function(point1){
if (point1){
geocoder.getLatLng ( address2, function(point2){
if (point2){
getDirections();
} else {
directions.innerHTML = "Please check the address and try again";
}
});
} else {
directions.innerHTML = "Please check the address and try again";
}
});
}
// Called from mapAddress to load the directions and map
// private method
function getDirections( ){
var gmap = new GMap2(document.getElementById(mapid));
var gdir = new GDirections(gmap,document.getElementById(directionsid));
gdir.load("from: " + location1 + " to: " + location2,
{ "locale": "en_US" });
generateURL();
}
function generateURL(){
var url = "http://maps.google.com/maps?saddr=";
url += location1;
url += "&daddr=";
url += location2;
var a = $("<a></a>").attr('href',url);
$(a).text("Google Maps");
$("#directions_url").append(a);
}
}();
// The (); above results in the function being interpreted by the browser just before the page is loaded.
// Make for_directions.init as the listener to load event
// Note that the init method is public that why its accessible outside the object scope
for_directions.addEvent(window, 'load', for_directions.init, false);
</script>
</body>
</html>
If you try out this code on your system name it as getdirections.php. The only thing you would need to change is the google maps api key. You can get the key here.
Once you generate your key put in the key parameter ( reproduced the line below for convenience ):
<script src="http://maps.google.com/maps?file=api&v=2&sensor=false& amp;key=[Your key here]"
type="text/javascript">
As is seen from the code above, I get the input through PHP and do the processing in Javascript. Now, I don't want users to get away with any kind of input ( javscript, dangerous HTML, etc ). I tried using the urlencode function in PHP. However, the encoded user input is not accepted by the javascript code and fails even on good input.
As a workaround to this problem I wrote a filterInput function in PHP that will replace/delete certain characters and thwart any attempt by the user to try and execute Javascript code through input.
This worked well. However, when the user did try give malicious input, say like "+alert("hello")+" with both the beginning and ending quotes included, the filterInput function trimmed the leading and tailing quotes and the resulting string is below:
+alert("hello")+
Now when code below is executed:
location1 = "<?= filterInput($_GET['location1']) ?>" || 0;
PHP substitues the function call with its return value like below:
location1 = "+alert("hello")+" || 0;
Execution of the script halts with the line above with an error ( missing ; before statement )
Note, had I not trimmed the quotes and used $_GET['location1'] directly I would get.
location1 = ""+alert("hello")+"" || 0;
alert("hello") would get executed!!
So, I am in a fix. If I filter input I get a javascript error on certain user input and if I don't filter input I allow users to execute any kind of javascript.
My questions then are:
- What is a proper and secure way to handle input on the web?
- Is this kind of user input crossing languages ( from PHP to Javascript ) ok?
- Apart from the user being able to execute javascript what other kinds of security threats does is this piece of code vulnerable?
Thanks for reading!!
Please help.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可以尝试 php 中的 json_encode 和 javascript 中的 eval 。
如果JS在完成输入的同一台机器上执行,我就不会太担心安全性。 黑客可以破解自己的机器,应该没问题。
You can try json_encode in php and an eval in javascript.
If the JS is executed on the same machine where the input is done, I wouldn't be too concerned about security. A hacker can hack his own machine, it should be no problem.