Javascript 如何使用 PHP 和 jQuery 发送安全的 AJAX 请求
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37912937/
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
How to send secure AJAX requests with PHP and jQuery
提问by Luke Brown
The problem
问题
So for a while now I've been experimenting with different AJAX approaches in sending data to a server that will be processed and stored inside a MySQL database.
所以一段时间以来,我一直在尝试使用不同的 AJAX 方法将数据发送到将被处理并存储在 MySQL 数据库中的服务器。
The page that the AJAX request hits api.php
, uses PHP's PDO prepared statements to save the data, so MySQL injections aren't really a problem and the passwords or data that needs to be encrypted are also handled by api.php
which isn't what I'm asking here. My question relates more to how to ensure the data is secure when being transferred from the client to the server.
AJAX 请求命中的页面api.php
,使用 PHP 的 PDO 准备语句来保存数据,因此 MySQL 注入并不是真正的问题,并且需要加密的密码或数据也由api.php
这不是我要问的处理这里。我的问题更多地涉及如何确保数据从客户端传输到服务器时的安全。
The approaches
方法
I currently have (for the login example I have included below):
我目前有(对于我在下面包含的登录示例):
- SSL Cert/HTTPS running on the domain.
- Certain AJAX request (obviously not this login request example as there is no session to begin with) will only work if the PHP Session is valid across the site (used on both
login.php
andapi.php
in this example). - Rate limiting on
api.php
when accessing functions. - PHP PDO prepared statements when interacting with the database inside
api.php
. - Encrypts sensitive data inside
api.php
(not relevant to the question).
- 在域上运行的 SSL 证书/HTTPS。
- 某些 AJAX 请求(显然不是这个登录请求示例,因为没有会话开始)只有在 PHP 会话在整个站点上有效时才有效(在本示例
login.php
和api.php
本示例中都使用)。 api.php
访问函数时的速率限制。- PHP PDO 在与里面的数据库交互时准备语句
api.php
。 - 加密内部的敏感数据
api.php
(与问题无关)。
The questions
问题
Finally, my questions are:
最后,我的问题是:
- Is this approach to using asynchronous HTTP (Ajax) requests safe enough to use rather than just submitting data to a PHP page and redirecting onwards? (As this way improves the user's experience).
- How can I check to know that the data my user's are sending hasn't been tampered with?
- Am I reasonably doing enough to protect my user's data, if not, what else can I do?
- 这种使用异步 HTTP (Ajax) 请求的方法是否足够安全,而不仅仅是将数据提交到 PHP 页面并继续重定向?(因为这种方式改善了用户的体验)。
- 我怎样才能知道我的用户发送的数据没有被篡改?
- 我在保护用户数据方面做得足够合理吗?如果没有,我还能做什么?
The example
这个例子
I understand everyone has different approaches to handling their site's data and transporting that data. I also understand that no matter what you do, you can never be 100% protected, as there may be vulnerabilities and ways around your system that you can't account for. I'm looking for feedback/improvements on my general approach in sending data securely rather than criticism of the specific code below as it is only an example. But any constructive answers are welcome. Thanks for taking the time to read/answer.
我知道每个人都有不同的方法来处理他们网站的数据和传输这些数据。我也明白,无论您做什么,您都无法得到 100% 的保护,因为您的系统可能存在您无法解释的漏洞和方法。我正在寻找关于我安全发送数据的一般方法的反馈/改进,而不是批评下面的特定代码,因为它只是一个例子。但欢迎任何建设性的答案。感谢您花时间阅读/回答。
function loginUser() {
var process = "loginUser";
var data = $("form").serializeArray();
data[1].value = SHA512(data[1].value); // sha then encrypt on api.php page
data = JSON.stringify(data);
$("#loginButton").html('<i class="fa fa-spinner fa-pulse fa-lg fa-fw"></i> Login');
$.ajax({
type: "POST",
url: "api.php",
data: {"process": process, "data": data},
success: function(data) {
if (data.response.state == "success") {
// if api.php returns success, redirect to homepage
} else {
// if api.php returns failure, display error
}
},
error: function(jqXHR, textStatus, errorThrown, data) {
// error handling
},
dataType: "json"
});
}
回答by Ivan Gabriele
1. Check the ORIGIN header
1. 检查 ORIGIN 标头
As specified by OWASP, this is not enough but recommended :
根据OWASP 的规定,这还不够,但建议:
Although it is trivial to spoof any header from your own browser, it is generally impossible to do so in a CSRF attack, except via an XSS vulnerability. That's why checking headers is a reasonable first step in your CSRF defense, but since they aren't always present, its generally not considered a sufficient defense on its own.
尽管从您自己的浏览器中欺骗任何标头是微不足道的,但在 CSRF 攻击中通常不可能这样做,除非通过 XSS 漏洞。这就是为什么检查标头是您的 CSRF 防御中合理的第一步,但由于它们并不总是存在,因此它本身通常不被认为是足够的防御。
And by Mozilla:
和Mozilla:
The Origin header is considered helpful against JSON data theft and CSRF attacks. The information provided by Origin--a bit of contextual request-creation information--should provide hints to web servers about trustworthiness of requests [...]
Origin 标头被认为有助于防止 JSON 数据盗窃和 CSRF 攻击。Origin 提供的信息——一些上下文请求创建信息——应该向 Web 服务器提供有关请求可信度的提示 [...]
Checking the HTTP_ORIGIN
header could be written as :
检查HTTP_ORIGIN
标题可以写为:
header('Content-Type: application/json');
if (isset($_SERVER['HTTP_ORIGIN'])) {
$address = 'http://' . $_SERVER['SERVER_NAME'];
if (strpos($address, $_SERVER['HTTP_ORIGIN']) !== 0) {
exit(json_encode([
'error' => 'Invalid Origin header: ' . $_SERVER['HTTP_ORIGIN']
]));
}
} else {
exit(json_encode(['error' => 'No Origin header']));
}
1. (bis) Check the REFERER header
1.(之二)检查REFERER头
Again from OWASP:
再次来自 OWASP:
If the Origin header is not present, verify the hostname in the Referer header matches the site's origin. Checking the referer is a commonly used method of preventing CSRF on embedded network devices because it does not require a per-user state.. This method of CSRF mitigation is also commonly used with unauthenticated requests [...]
如果 Origin 标头不存在,请验证 Referer 标头中的主机名与站点的来源匹配。检查引用是防止嵌入式网络设备上 CSRF 的常用方法,因为它不需要每个用户的状态。 这种缓解 CSRF 的方法也常用于未经身份验证的请求 [...]
Checking the HTTP_REFERER
is also quite simple in PHP with $_SERVER['HTTP_REFERER']
, you can just update the above code with it.
HTTP_REFERER
在 PHP 中使用 进行检查也很简单$_SERVER['HTTP_REFERER']
,您只需使用它更新上面的代码即可。
BE CAREFULwith the checking which always need to be really specific : do no check just example.comor api.example.combut the full https://example.com. Why ? Because you could spoof this check with an origin like api.example.com.hacker.com.
小心检查总是需要非常具体:不要只检查example.com或api.example.com,而是检查完整的https://example.com。为什么 ?因为您可以使用api.example.com.hacker.com 之类的来源来欺骗此检查。
2. Generate CSRF tokens
2.生成CSRF令牌
A well-explained answer specific to PHP has been given there, in short :
那里已经给出了一个很好解释的特定于 PHP 的答案,简而言之:
Generate the token :
session_start(); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); }
Add it in your generated views via a meta (like Github) :
<meta name="csrf-token" content="<?= $_SESSION['csrf_token'] ?>">
Setup jQuery ajax calls to include this token :
$.ajaxSetup({ headers : { 'CsrfToken': $('meta[name="csrf-token"]').attr('content') } });
Server-side check your AJAX requests :
session_start(); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } header('Content-Type: application/json'); $headers = apache_request_headers(); if (isset($headers['CsrfToken'])) { if ($headers['CsrfToken'] !== $_SESSION['csrf_token']) { exit(json_encode(['error' => 'Wrong CSRF token.'])); } } else { exit(json_encode(['error' => 'No CSRF token.'])); }
生成令牌:
session_start(); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); }
通过元(如 Github)将其添加到您生成的视图中:
<meta name="csrf-token" content="<?= $_SESSION['csrf_token'] ?>">
设置 jQuery ajax 调用以包含此令牌:
$.ajaxSetup({ headers : { 'CsrfToken': $('meta[name="csrf-token"]').attr('content') } });
服务器端检查您的 AJAX 请求:
session_start(); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } header('Content-Type: application/json'); $headers = apache_request_headers(); if (isset($headers['CsrfToken'])) { if ($headers['CsrfToken'] !== $_SESSION['csrf_token']) { exit(json_encode(['error' => 'Wrong CSRF token.'])); } } else { exit(json_encode(['error' => 'No CSRF token.'])); }
Most PHP frameworks have their own CSRF implementation, which more or less lay upon the same principle.
大多数 PHP 框架都有自己的 CSRF 实现,它们或多或少都基于相同的原则。
3. Sanitizevalidate user input.
3. 消毒验证用户输入。
You always must filterespace inputsand validate them.
你总是必须 筛选espace 输入并验证它们。
4. Protect your server
4. 保护您的服务器
- Limit the number of your requests.
- Use https as much as possible.
- Block bad queries.
- Protect POST requests.
5. Never trust user input
5. 永远不要相信用户输入
As @blue112 said, it is one of the most elementary security principles.
正如@blue112 所说,这是最基本的安全原则之一。
回答by blue112
Short answer: you can't protect your client side.
简短回答:您无法保护您的客户端。
Long answer:
长答案:
- Using AJAX is just as safe as posting data with, for instance, a form.
- Using HTTPS prevents man-in-the-middle to see your user data, so the actual data sent by the user are safe.
- 使用 AJAX 就像使用表单发布数据一样安全。
- 使用 HTTPS 可以防止中间人查看您的用户数据,因此用户发送的实际数据是安全的。
You can't do anything to make the browser prove that it's actually your javascript code that runs on the client side. Then, the obvious action to take is the most simple one: NEVER TRUST USER INPUT.
你不能做任何事情来让浏览器证明它实际上是你在客户端运行的 javascript 代码。然后,显而易见的行动是最简单的行动:永远不要相信用户输入。
This mean, as you started to do, securing your server side using session, rate limiting, data validation, fail2ban (banning a client ip after a certain number of fail), log monitoring...
这意味着,正如您开始做的那样,使用会话、速率限制、数据验证、fail2ban(在一定数量的失败后禁止客户端 IP)、日志监控来保护您的服务器端......
回答by Osemwota Osagie Anthony
the best way is still to do server side securing and if i was a logged in user in the site i would also be able to check the csrf token on my meta tag and forge a script to the server. So the sure bet is server side validation
最好的方法仍然是进行服务器端保护,如果我是站点中的登录用户,我还可以检查元标记上的 csrf 令牌并为服务器伪造脚本。所以肯定的赌注是服务器端验证