Подтвердить что ты не робот

Как отправить безопасные запросы AJAX с PHP и jQuery

Проблема

Итак, какое-то время я экспериментировал с различными подходами AJAX при отправке данных на сервер, который будет обрабатываться и храниться в базе данных MySQL.

Страница, на которую обращается запрос AJAX api.php, использует подготовленные заявления PHP PDO для сохранения данных, поэтому MySQL-инъекции на самом деле не проблема, и пароли или данные, которые необходимо зашифровать, также обрабатываются api.php что я не прошу здесь. Мой вопрос больше связан с тем, как обеспечить безопасность данных при передаче с клиента на сервер.

Подходы

В настоящее время у меня (для примера входа в систему, который я привел ниже):

  • SSL Cert/HTTPS, работающий в домене.
  • Определенный запрос AJAX (очевидно, не этот пример запроса на вход, так как нет сеанса для начала) будет работать, только если PHP-сессия действительна на сайте (используется в login.php и api.php в этом примере).
  • Ограничение скорости при api.php при доступе к функциям.
  • PHP PDO подготовил операторы при взаимодействии с базой данных внутри api.php.
  • Шифрует конфиденциальные данные внутри api.php (не относится к вопросу).

Вопросы

Наконец, мои вопросы:

  • Является ли такой подход к использованию асинхронных HTTP-запросов (Ajax) достаточно безопасным для использования, а не просто отправкой данных на страницу PHP и перенаправлением вперед? (Так как этот способ улучшает работу пользователя).
  • Как я могу проверить, что данные, которые мой пользователь отправляет, не были изменены?
  • Я достаточно делаю достаточно для защиты своих пользовательских данных, если нет, что еще я могу сделать?

Пример

Я понимаю, что у каждого есть разные подходы к обработке данных своих сайтов и транспортировке этих данных. Я также понимаю, что независимо от того, что вы делаете, вы никогда не можете быть защищены на 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"
    });
}
4b9b3361

Ответ 1

1. Проверьте заголовок ORIGIN

Как указанный OWASP, этого недостаточно, но рекомендуется:

Хотя тривиально обманывать любой заголовок из вашего собственного браузера, как правило, это невозможно сделать при атаке CSRF, за исключением уязвимости XSS. То, что проверка заголовков является разумным первым шагом в вашей защите CSRF, но поскольку они не всегда присутствуют, она обычно не считается достаточной защитой сама по себе.

И от Mozilla:

Заголовок Origin считается полезным для предотвращения кражи данных JSON и CSRF. Информация, предоставленная Origin - небольшая информация о создании контекстуального запроса - должна давать подсказки веб-серверам о достоверности запросов [...]

Проверка заголовка 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. (бис) Проверьте заголовок REFERER

Снова из OWASP:

Если заголовок Origin отсутствует, убедитесь, что имя хоста в заголовке Referer совпадает с исходным кодом сайта. Проверка рефератора является широко используемым методом предотвращения CSRF на встроенных сетевых устройствах, поскольку для него не требуется состояние для каждого пользователя. Этот метод смягчения CSRF также широко используется с неаутентифицированными запросами [...]

Проверка HTTP_REFERER также довольно проста в PHP с помощью $_SERVER['HTTP_REFERER'], вы можете просто обновить приведенный выше код.


Будьте осторожны с проверкой, которая всегда должна быть действительно конкретной: не проверяйте example.com или api.example.com, но полный https://example.com. Зачем? Потому что вы можете обмануть эту проверку с помощью источника, такого как api.example.com.hacker.com.


2. Создание токенов CSRF

Хорошо объясненный ответ, специфичный для PHP, был дано там, короче:

  • Создайте токен:

    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.']));
    }
    

Большинство фреймворков PHP имеют собственную реализацию CSRF, которая более или менее основана на одном и том же принципе.


3. Sanitize подтвердите ввод пользователя.

Вы всегда должны фильтровать вкладки espace и проверить их.


4. Защитите свой сервер


5. Никогда не доверяйте пользовательскому вводу

Как сказал @blue112, это один из самых элементарных принципов безопасности.

введите описание изображения здесь

Ответ 2

Короткий ответ: вы не можете защитить свою клиентскую сторону.

Длинный ответ:

  • Использование AJAX столь же безопасно, как и отправка данных, например, с помощью формы.
  • Использование HTTPS предотвращает доступ пользователей к данным пользователя, поэтому фактические данные, отправленные пользователем, безопасны.

Вы ничего не можете сделать, чтобы браузер показал, что это действительно ваш код javascript, который работает на стороне клиента. Тогда очевидное действие, которое нужно предпринять, является самым простым: НИКОГДА НЕ ДОПУСКАТЬ ВХОД ПОЛЬЗОВАТЕЛЯ.

Это означает, что по мере того, как вы начали делать, защита вашего сервера с помощью сеанса, ограничение скорости, проверка данных, fail2ban (запрет на IP-адрес клиента после определенного количества сбоев), мониторинг журнала...

Ответ 3

лучший способ по-прежнему заключается в обеспечении защиты на стороне сервера, и если бы я был зарегистрированным пользователем на сайте, я бы также смог проверить токен csrf на своем метатеге и подделать сервер script. Таким образом, уверенная ставка - проверка на стороне сервера