2009-12-23 102 views
43

我使用我的PHP后端通过检查$_SERVER['HTTP_X_REQUESTED_WITH']中的值来检测AJAX请求。在PHP中检测Ajax并确保请求来自我自己的网站

这给了我一个可靠的检测,确保请求是利用AJAX技术。

如何确保请求来自我自己的域,而不是外部域/机器人?

www.example.com/ajax?true可以允许任何人进行AJAX调用并剪切信息。

我可以为每个进入我的网站的人正常开会,然后允许AJAX呼叫......但那也可能是假的。

这些事情现在有没有关系?

回答

31

让你控制器

  • 生成会话访问令牌
  • 商店后面的比较

在你看来

  • 声明访问令牌作为JS变量
  • 每个请求发送令牌

回到你的控制器

  • 验证HTTP_X_REQUESTED_WITH
  • 验证令牌

检查这些安全guidelines from OpenAjax
另外,请阅读有关codinghorror.com Annie的文章。

+3

+1由于基于浏览器的AJAX请求随每个请求一起发送cookie,因此您只需在每个页面上添加一个令牌检查。 – Xeoncross 2009-12-23 17:12:47

+7

这不会阻止机器人直接打开API。它可以像JavaScript应用程序一样获取访问令牌。 – Quentin 2014-09-17 09:18:19

0

检查$_SERVER['HTTP_REFERER']。这在很多情况下都能正常工作,但不应该将它们混淆成完全安全的解决方案。

+1

这会给你你需要的东西,但不可靠,因为浏览器可以把它喜欢的东西放在这里。假冒很容易,所以价值不可能真正值得信赖。从根本上说,即使您向页面发出令牌并使您的ajax请求将令牌传回给您,您也无法保证请求来自任何地方。真的,你必须问自己,这是否重要? – 2009-12-23 16:56:29

+0

@Jonathan Sampson - 我可以像BurpSuite那样捕获请求并更改HTTP_REFERER ...所以我不认为这是解决方案。我也可以用卷曲的脚本来做。 – 2009-12-23 16:56:37

23

您可以检查HTTP_REFERRER,但不是所有浏览器都设置它。最好的方法是在JavaScript端发送ajax调用的包装器,它将document.cookie的一部分发送回服务器 - 只有你的域可以访问cookie。您可以将请求标头中的cookie与php中的AJAX调用中的cookie进行比较。

作为回应,“它甚至很重要,这些天” - 是的,它的确如此! Read this

+2

我不确定这种方法会通过默默无闻的方式完成除安全以外的任何操作。制作一个Web请求是微不足道的,这是一个Ajax请求。检查Javascript包装并添加它将添加的任何内容稍微不重要。所有这些都是模拟会话令牌。 – zombat 2009-12-23 17:27:09

+1

我猜如果他们没有使用Javascript,它可能会让机器人停留在海湾。但它本身不会提出请求*安全*。 – zombat 2009-12-23 17:29:43

+0

此方法需要访问会话cookie。如果机器人可以访问会话cookie,那么是的,该网站是不安全的。但是你也有比XSRF更大的问题! – Annie 2009-12-23 17:50:35

3

大卫·沃尔什具有良好的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.... */ 
+1

这与保护请求无关。 – Xeoncross 2009-12-23 17:13:29

+0

任何人都可以将HTTP标头添加到请求中。 – 2014-05-15 20:10:09

2

真的,最安全的方法就是按照您的建议使用服务器端会话,因为这些会话不能像Cookie那样制作。当然,某人仍然可以劫持会话ID,但是如果您还将用户的IP地址存储在他们的会话中并在每个请求中检查它,那么可以清除很多劫持。只有在同一局域网或代理上的人才能劫持它。

提到的任何其他方法 - cookies,javascript,http referer - 取决于客户端数据,这些数据是不安全的,应始终怀疑是伪造,伪造,劫持和恶意构建。

+0

同意,并且可以改变会话生成的方式,以便它们更安全。 – Quamis 2009-12-24 13:13:12

6

关于你的最后一个问题:“在这些日子里,它甚至很重要吗?” 这是个案的问题。如果ajax请求正在做一些不需要安全性的事情(例如,加载最新的股票报价),那么它对于恕我直言并不重要。如果请求正在加载应该保护的信息(例如,返回标识信息或在服务器上执行某些操作),那么您应该将其视为等待。

我个人不使用服务器变量来知道何时是ajax请求。相反,我只是添加一个查询参数给ajax调用(例如http://domain.com/?ajax=true)。如果我需要保护ajax调用,那么我会使用与保护常规页面请求(使用客户端和服务器)相同的方法。正如Lucas Oman指出的那样,客户端的任何东西都可能是伪造的。底线不相信任何请求,即使您认为它来自您的网站或数据库。始终遵循口头禅“过滤器输入 - 退出输出”。

+1

是的,是的,是的。吉姆说话有道理。听他的。 – Quentin 2009-12-23 17:54:34

+0

这是最好的答案。您永远无法确定向您的网站发送请求的来源。即使请求已通过身份验证,用户也可能正在使用bot和有效凭据,甚至只是将其cookie从浏览器中拉出。 – frostymarvelous 2015-11-11 12:58:45

1

使用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)); 
} 
+0

出于安全原因,我不会使用会话ID。建议为每个请求生成一个随机令牌,保存在会话中。阅读此:https://docs.phalconphp.com/en/latest/reference/security.html#cross-site-request-forgery-csrf-protection – Yossi 2016-02-13 23:01:33

+0

虽然这将工作,因为ajax发布数据(不使用get) ,任何人都可以通过查看JavaScript来看到这些值。他们可以复制数据,创建自己的表单并将其发布到同一位置。对$ _SERVER ['HTTP_REFERER']的检查将有助于防止这种情况,就像检查数据是否通过ajax发布一样,但它仍然不完全安全。有没有更好的办法? – james 2017-09-26 10:41:39