在 PHP 中检测 Ajax 并确保请求来自我自己的网站
Posted
技术标签:
【中文标题】在 PHP 中检测 Ajax 并确保请求来自我自己的网站【英文标题】:Detecting Ajax in PHP and making sure request was from my own website 【发布时间】:2010-12-29 13:32:53 【问题描述】:我使用我的 php 后端通过检查 $_SERVER['HTTP_X_REQUESTED_WITH']
中的值来检测 AJAX 请求。
这为我提供了可靠的检测,确保使用 AJAX 技术发出请求。
如何确保请求来自我自己的域,而不是外部域/机器人?
www.example.com/ajax?true 可以允许任何人进行 AJAX 调用并剪切信息。
我可以为正常进入我网站的每个人创建会话,然后允许 AJAX 调用..但这也可以伪造。
这些天还重要吗?
【问题讨论】:
【参考方案1】:使用 google recaptcha...它为您的 Ajax 生成一个令牌,然后在服务器端您可以验证该令牌...
【讨论】:
【参考方案2】:您可以检查 HTTP_REFERRER,但并非所有浏览器都设置它。最好的方法是在 javascript 端为你的 ajax 调用编写一个包装器,它将 document.cookie 的一部分发送回服务器——只有你的域可以访问 cookie。您可以将请求标头中的 cookie 与 php 中的 AJAX 调用中的 cookie 进行比较。
回应“这几天有没有关系”——是的,有关系! Read this.
【讨论】:
我不确定这种方法能否通过默默无闻来实现安全性之外的任何事情。制作 Web 请求很简单,这就是 Ajax 请求的全部内容。检查 Javascript 包装器并添加它要添加的任何内容都不是那么简单。所有这一切都是模拟一个会话令牌。 我想如果机器人不使用 Javascript,它可能会阻止机器人。但它本身不会使请求安全。 此方法需要访问会话 cookie。如果机器人可以访问会话 cookie,那么是的,该站点不安全。但是你也有比 XSRF 更大的问题! 我阅读了这篇博客文章,它提供了非常丰富的信息,因为这个主题对我来说是新的。根据我的检查,CakePHP 会自动执行此操作,并在重新启动浏览器时重置 cookie。那应该处理注册用户。会发生什么,在公共场所反对机器人,四处走动? example.com/blog/title1?ajax=true | example.com/blog/title2?ajax=true 关于“现在重要吗”,我想我根本无法阻止机器人轻松爬过我所有的公共区域。 关于公共区域,您可以使用验证码等垃圾邮件预防技术:en.wikipedia.org/wiki/CAPTCHA【参考方案3】:使用 POST 会话安全请求:
在网页内部(例如 index.php)我们需要存储 sessionid
<?php
// Create Session
$session = session_id();
if(empty($session)) session_start();
?>
<head>
...
<script type="text/javascript">
sid = '<?php echo session_id(); ?>';
</script>
<script type="text/javascript" src="ajaxrequest.js"></script>
...
</head>
ajax 请求 (ajaxrequest.js)
/* simple getAjax function
* @param $url request url
* @param $param parameter (dont use ?)
* @param callback function on success
*/
var spinnerid = '#spinner'; // Spinner as long ajax requests running
$(document).ajaxStart(function() $(spinnerid).show(); );
$(document).ajaxStop(function() $(spinnerid).hide(); );
function getAjax( url, param, callback )
var data = null;
url += "?sid=" + sid + "&" + param;
$.ajax(
url: url,
method: "POST", // uncomment to use GET, POST is secured by session
cache: false,
async: true,
success : function(data)
callback(data);
,
getAjax( 'http://domain.com/', 'data=foo', function( data )
// do stuf with data
var jsonobj = eval("(" + data + ")");
var data = jsonobj[0][ 'data' ];
);
负责的php端:
if( isset( $_GET['sid'] ) ) $client_sid = $_GET['sid'];
if( session_id() == null ) session_start();
if( session_id() != $client_sid )
// noID or wrongID, redirect to mainindex
ignore_user_abort(true);
header( "HTTP/1.1 403 Forbidden" );
header("Connection: close", true);
exit;
else
// get data
if( isset( $_GET['data'] ) )
$data = $_GET['data'];
else if( isset( $_POST['data'] ) )
$data = $_POST['data'];
else
$data = null;
// do stuff with data
// return data as json
$resp[0]['data'] = $data;
print_r( json_encode( $resp ) );
【讨论】:
出于安全原因,我不会使用会话 ID。建议为每个请求生成一个随机令牌,该令牌保存在您的会话中。阅读:docs.phalconphp.com/en/latest/reference/… 虽然这会起作用,因为 ajax 正在发布数据(不使用 get),但任何人仍然可以通过查看 javascript 来查看值。然后他们可以复制数据,创建自己的表单并将其发布到同一位置。检查 $_SERVER['HTTP_REFERER'] 将有助于防止这种情况发生,检查数据是否通过 ajax 发布但仍不完全安全。有没有更好的办法?【参考方案4】:关于您的最后一个问题:“现在还重要吗?” 这是一个个案问题。如果 ajax 请求正在做一些不需要安全性的事情(例如加载最新的股票报价),那么恕我直言,这真的无关紧要。如果请求正在加载应该保护的信息(例如返回识别信息或在服务器上执行某些操作),那么您应该这样对待它。
我个人不使用服务器变量来知道什么时候是 ajax 请求。相反,我只是在 ajax 调用中添加一个查询参数(例如http://domain.com/?ajax=true)。如果我需要保护 ajax 调用,那么我将使用与保护常规页面请求相同的方法(同时使用客户端和服务器)。正如卢卡斯·阿曼(Lucas Oman)所指出的,客户端的任何东西都可以伪造。底线不要相信任何请求,即使您认为它来自您的站点或数据库。始终遵循口头禅“过滤输入 - 转义输出”。
【讨论】:
是的,是的,是的。吉姆说得很有道理。听他的。 这是最好的答案。您永远无法确定对您网站的请求的来源。即使请求已通过身份验证,用户也可能使用机器人和有效凭据,甚至只是将他们的 cookie 从浏览器中提取出来。【参考方案5】:确实,最安全的方法是按照您的建议使用服务器端会话,因为这些会话无法像 cookie 那样制作。
当然,仍然有人可以劫持会话 ID,但是如果您还将用户的 IP 地址存储在他们的会话中并在每次请求时检查它,您就可以清除很多劫持。只有同一 LAN 或代理上的人才能劫持它。
提到的任何其他方法——cookies、javascript、http referer——依赖于客户端数据,这是不安全的,应该始终怀疑是假的、伪造的、劫持的和恶意构建的。
【讨论】:
同意,我们可以改变会话的生成方式,这样会更安全。【参考方案6】:让你控制
生成访问令牌 存储在会话中以供以后比较在你看来
将访问令牌声明为JS变量 随每个请求发送令牌回到你的控制器
验证 HTTP_X_REQUESTED_WITH 验证令牌检查这些安全guidelines from OpenAjax。 另外,请阅读有关 codinghorror.com Annie 链接的文章。
【讨论】:
+1 由于基于浏览器的 AJAX 请求会随每个请求发送 cookie - 您只需在每个页面上添加令牌检查。 这不会阻止机器人直接访问 API。它可以像你的 JavaScript 应用一样获取访问令牌。【参考方案7】:大卫沃尔什有一个很好的solution
/* decide what the content should be up here .... */
$content = get_content(); //generic function;
/* AJAX check */
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
/* special ajax here */
die($content);
/* not ajax, do more.... */
【讨论】:
这与保护请求无关。 任何人都可以在请求中添加 HTTP 标头。【参考方案8】:检查$_SERVER['HTTP_REFERER']
。这在许多情况下都有效,但不应与完全安全的解决方案混淆。
【讨论】:
这会给你你需要的东西,但并不可靠,因为浏览器可以把它喜欢的任何东西放进去。很容易造假,所以价值不能真正被信任。从根本上说,您不能保证请求来自任何地方,即使您向页面发出令牌并让您的 ajax 请求将令牌传回给您。真的,你要问问自己,这有关系吗? @Jonathan Sampson - 我可以在 BurpSuite 之类的东西中捕获请求并更改 HTTP_REFERER ......所以我认为这不是解决方案。我也可以在 w/curl 的脚本中做到这一点。以上是关于在 PHP 中检测 Ajax 并确保请求来自我自己的网站的主要内容,如果未能解决你的问题,请参考以下文章