php 防止直接访问 ajax 函数调用的文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1756591/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Prevent Direct Access To File Called By ajax Function
提问by jriggs
I'm calling the php code from ajax like this:
我正在像这样从 ajax 调用 php 代码:
ajaxRequest.open("GET", "func.php" + queryString, true);
Since it's a get request anyone can see it by simply examining the headers. The data being passed is not sensitive, but it could potentially be abused since it is also trivial to get the parameter names.
由于它是一个 get 请求,任何人都可以通过简单地检查标题来看到它。传递的数据并不敏感,但它可能会被滥用,因为获取参数名称也很简单。
How do I prevent direct access to http://mysite/func.phpyet allow my ajax page access to it?
如何防止直接访问http://mysite/func.php但允许我的 ajax 页面访问它?
Also I have tried the solution posted herebut its doesn't work for me - always get the 'Direct access not premitted' message.
回答by Ian Van Ness
Most Ajax requests/frameworks should set this particular header that you can use to filter Ajax v Non-ajax requests. I use this to help determine response type (json/html) in plenty of projects:
大多数 Ajax 请求/框架都应该设置这个特定的标头,您可以使用它来过滤 Ajax v Non-ajax 请求。我用它来帮助确定大量项目中的响应类型(json/html):
if( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && ( $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' ) )
{
// allow access....
} else {
// ignore....
}
edit: You can add this yourself in your own Ajax requests with the following in your javascript code:
编辑:您可以在自己的 Ajax 请求中自己添加它,并在您的 javascript 代码中添加以下内容:
var xhrobj = new XMLHttpRequest();
xhrobj.setRequestHeader("X-Requested-With", "XMLHttpRequest");
回答by Gabriel Sosa
what I use is: PHP sessions + a hash that is sent each time I do a request. This hash is generated using some algorithm in the server side
我使用的是:PHP 会话 + 每次发出请求时发送的哈希值。这个哈希是使用服务器端的一些算法生成的
回答by Palantir
Mmm... you could generate a one-time password on session start, which you could store in the _SESSION, and add a parameter to your ajax call which would re-transmit this (something like a captcha). It would be valid for that session only.
嗯...您可以在会话开始时生成一个一次性密码,您可以将其存储在 _SESSION 中,并在您的 ajax 调用中添加一个参数,该参数将重新传输此密码(类似于验证码)。它仅对该会话有效。
This would shield you from automated attacks, but a human who has access to your site could still do this manually, but it could be the base to devise something more complicated.
这可以保护您免受自动攻击,但有权访问您网站的人仍然可以手动执行此操作,但它可能是设计更复杂的东西的基础。
回答by robjmills
I would question why you are so convinced that no-one should be able to visit that file directly. Your first action really should be to assume that people may visit the page directly and act around this eventuality. If you are still convinced you want to close access to this file then you should know that you cannot trust $_SERVERvariables for this as the origins of $_SERVERcan be difficult to determine and the values of the headers can be spoofed. In some testing I did I found those headers ($_SERVER['HTTP_X_REQUESTED_WITH']& $_SERVER['HTTP_X_REQUESTED_WITH']) to be unreliable as well.
我会质疑为什么您如此确信没有人应该能够直接访问该文件。你的第一个行动真的应该是假设人们可能会直接访问页面并解决这种可能性。如果您仍然确信要关闭对该文件的访问,那么您应该知道您不能$_SERVER为此信任变量,因为$_SERVER很难确定其来源,并且标头的值可能会被欺骗。在一些测试中,我发现这些标头 ( $_SERVER['HTTP_X_REQUESTED_WITH']& $_SERVER['HTTP_X_REQUESTED_WITH']) 也不可靠。
回答by Enno
Anyone in this thread who suggested looking at headers is wrong in some way or other. Anything in the request (HTTP_REFERER, HTTP_X_REQUESTED_WITH) can be spoofed by an attacker who isn't entirely incompetent, including shared secrets [1].
该线程中任何建议查看标题的人在某种程度上都是错误的。请求中的任何内容(HTTP_REFERER、HTTP_X_REQUESTED_WITH)都可能被并非完全无能的攻击者欺骗,包括共享秘密 [1]。
You cannot prevent people from making an HTTP request to your site. What you want to do is make sure that users must authenticate before they make a request to some sensitive part of your site, by way of a session cookie. If a user makes unauthenticated requests, stop right there and give them a HTTP 403.
您无法阻止人们向您的站点发出 HTTP 请求。您要做的是确保用户在向您网站的某些敏感部分发出请求之前必须通过会话 cookie 进行身份验证。如果用户发出未经身份验证的请求,请立即停止并给他们一个 HTTP 403。
Your example makes a GET request, so I guess you are concerned with the resource requirements of the request [2]. You can do some simple sanity checks on HTTP_REFERER or HTTP_X_REQUESTED_WITH headers in your .htaccess rules to stop new processes from being spawned for obviously fake requests (or dumb search-crawlers that won't listen to robots.txt), but if the attacker fakes those, you'll want to make sure your PHP process quits as early as possible for non-authenticated requests.
您的示例发出 GET 请求,所以我猜您关心请求的资源需求 [2]。您可以对 .htaccess 规则中的 HTTP_REFERER 或 HTTP_X_REQUESTED_WITH 标头进行一些简单的健全性检查,以阻止为明显虚假请求(或不会侦听 robots.txt 的愚蠢搜索爬虫)生成新进程,但如果攻击者伪造那些,您需要确保您的 PHP 进程尽早退出未经身份验证的请求。
[1] It's one of the fundamental problems with client/server applications. Here's why it doesn't work: Say you had a way for your client app to authenticate itself to the server - whether it's a secret password or some other method. The information that the app needs is necessarily accessible to the app (the password is hidden in there somewhere, or whatever). But because it runs on the user's computer, that means they also have access to this information: All they need is to look at the source, or the binary, or the network traffic between your app and the server, and eventually they will figure out the mechanism by which your app authenticates, and replicate it. Maybe they'll even copy it. Maybe they'll write a clever hack to make your app do the heavy lifting (You can always just send fake user input to the app). But no matter how, they've got all the information required, and there is no way to stop them from having it that wouldn't also stop your app from having it.
[1] 这是客户端/服务器应用程序的基本问题之一。这就是它不起作用的原因:假设您的客户端应用程序有一种方法可以向服务器验证自己的身份 - 无论是秘密密码还是其他方法。应用程序需要的信息必须可供应用程序访问(密码隐藏在某处,或其他什么地方)。但是因为它在用户的计算机上运行,这意味着他们也可以访问这些信息:他们所需要的只是查看源代码、二进制文件或应用程序与服务器之间的网络流量,最终他们会弄清楚您的应用程序进行身份验证和复制的机制。也许他们甚至会复制它。也许他们会写一个聪明的黑客来让你的应用程序完成繁重的工作(你总是可以向应用程序发送虚假的用户输入)。
[2] GET requests in a well-designed application have no side-effects, so nobody making them will be able to make a change on the server. Your POST requests should always be authenticated with session plus CSRF token, to let only authenticated users call them. If someone attacks this, it means they have an account with you, and you want to close that account.
[2] 设计良好的应用程序中的 GET 请求没有副作用,因此任何人都无法在服务器上进行更改。您的 POST 请求应始终使用 session 和 CSRF 令牌进行身份验证,以便只有经过身份验证的用户才能调用它们。如果有人对此进行攻击,则意味着他们在您那里有一个帐户,并且您想关闭该帐户。
回答by Edoardo
I solved this problem preparing a check function that make three things
我解决了这个问题,准备了一个检查功能,它可以做三件事
- check referer $_SERVER['HTTP_REFERER'];
- check http x request $_SERVER['HTTP_X_REQUESTED_WITH'];
- check the origin via a bridge file
- 检查引用 $_SERVER['HTTP_REFERER'];
- 检查 http x 请求 $_SERVER['HTTP_X_REQUESTED_WITH'];
- 通过桥文件检查原点
if all three pass, you success in seeing php file called by ajax, if just one fails you don't get it
如果这三个都通过,你就成功地看到了 ajax 调用的 php 文件,如果只有一个失败,你就不会得到它
The points 1 and 2 were already explained, the bridge file solution works so:
第 1 点和第 2 点已经解释过,桥接文件解决方案的工作原理是:
Bridge File
桥接文件
immagine the following scenario:
想象一下以下场景:
A.php page call via ajax B.php and you want prevent direct access to B.php
A.php 页面通过 ajax B.php 调用,你想防止直接访问 B.php
- 1) when A.php page is loaded it generates a complicated random code
- 2) the code is copied in a file C.txt not directly accessible from web (httpd secured)
3) at the same time this code is in clear sculpted in the rendered html of the A.php page (for example as an attribute of body, es:
data-bridge="ehfwiehfe5435ubf37bf3834i"
4) this sculpted code is retrived from javascript and sent via ajax post request to B.php
- 5) B.php page get the code and check if it exists in the C.txt file
- 6) if code match the code is popped out from C.txt and the page B.php is accessible
- 7) if code is not sent (in case you try to access directly the B page) or not matches at all (in case you supply an old code trapped or trick with a custom code), B.php page die.
- 1) 当 A.php 页面加载时,它会生成一个复杂的随机代码
- 2) 代码被复制到一个不能从网络直接访问的文件 C.txt 中(httpd 安全)
3)同时这段代码在A.php页面渲染出来的html中被清晰的雕刻出来(例如作为body的一个属性,es:
数据桥=“ehfwiehfe5435ubf37bf3834i”
4) 这个雕刻的代码是从 javascript 中提取的,并通过 ajax post 请求发送到 B.php
- 5)B.php页面获取代码并检查C.txt文件中是否存在
- 6) 如果代码匹配,则从 C.txt 中弹出代码并且可以访问页面 B.php
- 7) 如果代码未发送(如果您尝试直接访问 B 页面)或根本不匹配(如果您提供旧代码被困或使用自定义代码进行欺骗),B.php 页面将死亡。
In this way you can access the B page only via an ajax call generated from the father page A. The key for pageB.php is given only and ever from pageA.php
通过这种方式,您只能通过从父页面 A 生成的 ajax 调用访问 B 页面。 pageB.php 的密钥仅从 pageA.php 中给出
回答by Theo Orphanos
Put the following code at the very top of your php file that is called by ajax. It will execute ajax requests, but will "die" if is called directly from browser.
将以下代码放在 ajax 调用的 php 文件的最顶部。它将执行 ajax 请求,但如果直接从浏览器调用,它将“死亡”。
define('AJAX_REQUEST', isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
if(!AJAX_REQUEST) {die();}
Personally, I choose not to output anything after "die()", as an extra security measure. Meaning that I prefer to show just a blank page to the "intruder", rather than giving out hints such as "if" or "why" this page is protected.
就个人而言,作为额外的安全措施,我选择在“die()”之后不输出任何内容。这意味着我更喜欢只向“入侵者”显示一个空白页面,而不是给出诸如“如果”或“为什么”此页面受到保护之类的提示。
回答by Pekka
There is no point in doing this. It doesn't add any actual security.
这样做没有意义。它不会增加任何实际的安全性。
All the headers that indicate that a request is being made via Ajax (like HTTP_X_REQUESTED_WITH) can be forged on client side.
所有表明请求是通过 Ajax 发出的标头(如HTTP_X_REQUESTED_WITH)都可以在客户端伪造。
If your Ajax is serving sensitive data, or allowing access to sensitive operations, you need to add proper security, like a login system.
如果您的 Ajax 提供敏感数据,或允许访问敏感操作,则需要添加适当的安全性,例如登录系统。
回答by Andris
I tried this
我试过这个
1) in main php file (from which send ajax request) create session with some random value, like $_SESSION['random_value'] = 'code_that_creates_something_random';Must be sure, that session is created above $.post.
1)在主php文件(从中发送ajax请求)中创建具有一些随机值的会话,例如$_SESSION['random_value'] = 'code_that_creates_something_random';必须确保,该会话是在上面创建的$.post。
2) then
2)然后
$.post( "process_request.php",
{
input_data:$(':input').serializeArray(),
random_value_to_check:'<?php echo htmlspecialchars( $_SESSION['random value'], ENT_QUOTES, "UTF-8"); ?>'
}, function(result_of_processing) {
//do something with result (if necessary)
});
3) and in process_request.php
3) 并在 process_request.php
if( isset($_POST['random_value_to_check']) and
trim($_POST['random_value_to_check']) == trim($_SESSION['random value']) ){
//do what necessary
}
Before i defined session, then hidden input field with session value, then value of the hidden input field send with ajax. But then decided that the hidden input field not necessary, because can send without it
在我定义会话之前,然后使用会话值隐藏输入字段,然后使用ajax发送隐藏输入字段的值。但后来决定隐藏输入字段没有必要,因为没有它可以发送
回答by Maciek Rek
I have a simplified version of Edoardo's solution.
我有 Edoardo 解决方案的简化版本。
Web page A creates a random string, a
[token], and saves a file with that name on disk in a protected folder (eg. with .htaccess withDeny from allon Apache).Page A passes the
[token]along with the AJAX request to the script B (in OP'squeryString).Script B checks if the
[token]filename exists and if so it carries on with the rest of the script, otherwise exits.You will also need to set-up some cleaning script eg. with Cron so the old tokens don't cumulate on disk.
网页 A 创建一个随机字符串 a
[token],并将具有该名称的文件保存在磁盘上受保护文件夹中(例如,Deny from all在 Apache 上使用 .htaccess )。页面 A 将
[token]与 AJAX 请求一起传递给脚本 B(在 OP 中queryString)。脚本 B 检查
[token]文件名是否存在,如果存在,则继续执行脚本的其余部分,否则退出。您还需要设置一些清理脚本,例如。使用 Cron,因此旧令牌不会在磁盘上累积。
It is also good to delete the [token]file right away with the script B to limit multiple requests.
[token]使用脚本 B 立即删除文件以限制多个请求也很好。
I don't think that HTTP headers check is necessary since it can be easily spoofed.
我不认为 HTTP 标头检查是必要的,因为它很容易被欺骗。

