如何使用PHP和jQuery发送安全的AJAX请求

Luk*_*own 28 javascript php security ajax jquery

问题

所以有一段时间我一直在尝试使用不同的AJAX方法将数据发送到服务器,该服务器将被处理并存储在MySQL数据库中.

AJAX请求命中的页面api.php使用PHP的PDO预处理语句来保存数据,因此MySQL注入并不是真正的问题,需要加密的密码或数据也是由api.php我所要求的处理的这里.我的问题更多地涉及如何确保数据在从客户端传输到服务器时是安全的.

方法

我目前有(对于我在下面列出的登录示例):

  • 域上运行的SSL证书/ HTTPS.
  • 某些AJAX请求(因为没有会话开始与显然不是这个登录请求例子)如果PHP会议是整个网站(两者使用的有效只会工作login.php,并api.php在这个例子中).
  • api.php访问功能时的速率限制.
  • PHP PDO在与内部数据库交互时准备语句api.php.
  • 加密内部的敏感数据api.php(与问题无关).

问题

最后,我的问题是:

  1. 这种使用异步HTTP(Ajax)请求的方法是否足够安全,而不仅仅是将数据提交到PHP页面并重定向?(这种方式改善了用户的体验).
  2. 如何检查我的用户发送的数据是否未被篡改?
  3. 我是否合理地保护用户的数据,如果没有,我还能做些什么?

这个例子

我知道每个人都有不同的方法来处理他们的网站数据和传输数据.我也明白,无论你做什么,你都永远不会受到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"
    });
}
Run Code Online (Sandbox Code Playgroud)

Iva*_*ele 58

1.检查ORIGIN标题

正如OWASP指出的,这还不够,但建议:

尽管从您自己的浏览器中欺骗任何标头都是微不足道的,但在CSRF攻击中通常不可能这样做,除非是通过XSS漏洞.这就是为什么检查标题是CSRF防御中合理的第一步,但由于它们并不总是存在,因此它通常不被认为是足够的防御.

由Mozilla:

Origin头被认为有助于JSON数据窃取和CSRF攻击.Origin提供的信息 - 一些上下文请求创建信息 - 应该向Web服务器提供有关请求可信度的提示[...]

检查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']));
}
Run Code Online (Sandbox Code Playgroud)

1.(bis)检查REFERER标题

再次来自OWASP:

如果Origin标头不存在,请验证Referer标头中的主机名是否与站点的原点匹配.检查引用是防止嵌入式网络设备上的CSRF的常用方法,因为它不需要每用户状态.这种CSRF缓解方法也常用于未经身份验证的请求[...]

HTTP_REFERER在PHP中检查也很简单$_SERVER['HTTP_REFERER'],你可以用它来更新上面的代码.


小心一点,总是需要确定具体的检查:不要只检查example.comapi.example.com,而是检查完整的https://example.com.为什么?因为您可以使用api.example.com.hacker.com这样的来源欺骗此检查.


2.生成CSRF令牌

简而言之,那里给出了一个特别针对PHP的解释清楚的答案:

  1. 生成令牌:

    session_start();
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 通过元数据(如Github)将其添加到生成的视图中:

    <meta name="csrf-token" content="<?= $_SESSION['csrf_token'] ?>">
    
    Run Code Online (Sandbox Code Playgroud)
  3. 设置jQuery ajax调用以包含此标记:

    $.ajaxSetup({
        headers : {
            'CsrfToken': $('meta[name="csrf-token"]').attr('content')
        }
    });
    
    Run Code Online (Sandbox Code Playgroud)
  4. 服务器端检查您的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.']));
    }
    
    Run Code Online (Sandbox Code Playgroud)

大多数PHP框架都有自己的CSRF实现,或多或少都有相同的原则.


3. 消毒 验证用户输入.

你永远都必须 过滤espace输入验证它们.


4.保护您的服务器


5.永远不要相信用户输入

正如@ blue112所说,它是最基本的安全原则之一.

在此输入图像描述

  • 我必须将会话数组键更改为“Csrf-Token”,否则它在我的服务器上的 apache 标头中被重命名为“Csrftoken”。 (2认同)