Интернет        25.03.2024   

Простой пример использования PHP и AJAX. Простой пример использования PHP и AJAX Добавление анимации обратной связи

7.4K
В этой статье я опишу создание и отправку формы AJAX . После этого мы сможем рассмотреть реализацию анимации с помощью animate.css , валидации данных с помощью JavaScript .

На момент написания данной статьи Bootstrap 3.3.5 является актуальной версией фреймворка. Для этой статьи мы используем сборку Bootstrap по умолчанию (с 12 столбцами ). Когда вы будете выполнять задания этой статьи, убедитесь, что используете последние сниппеты и структуру кода, описанные в документации Bootstrap .

Структура файлов и папок

Мы создадим корневой каталог и добавим в него следующие файлы и папки:

Bootstrap-Form:


Нам нужно будет подключить некоторые front-end библиотеки:
  • Bootstrap ;
  • jQuery .

С учетом этих библиотек структура файлов будет выглядеть следующим образом:

Bootstrap-Form:

Создание формы

Откройте файл index.html и скопируйте в него следующую базовую структуру AJAX формы обратной связи :

Contact form using Bootstrap 3.3.4 " Send me a message

Это базовый шаблон HTML , в который мы будем добавлять содержимое формы. Мы подключили все необходимые файлы CSS и JavaScript . Заметьте, что для этого конкретного примера нам не нужен bootstrap.js .

Мы включили мета-тег viewport для медиа-запросов в рамках Bootstrap . JavaScript был помещен в нижней части файла, чтобы в первую очередь обрабатывался основной код.

В теге body мы включили div с классом col-sm-6 col-sm-offset-3 . Это означает, что в пределах окна просмотра sm (маленького ) и поверх него мы хотим отобразить столбец шириной 50% (максимальное количество столбцов 12 ). Класс col-sm-offset-3 задает отступ слева на 25% .

Таким образом, создается макет, который занимает половину доступного пространства и отцентрирован по горизонтали. После этого мы включили h3 , и дальше идет основа формы. Убедитесь в том, что вы применили к форме идентификатор, чтобы позже прикрепить к ней событие для отправки формы AJAX JQuery :

Без борьбы не бывает победы

Name Email Message Submit Message Submitted!

Это все поля ввода и кнопки, с которыми будет взаимодействовать пользователь. Первоначальный div с присвоенным классом row — это классический синтаксис Bootstrap , представляющий собой горизонтальную группировку элементов col . Столбцы в пределах Bootstrap создают отступы или пробелы. Удаляя их, можно добиться, чтобы строка равномерно вписывалась в контейнер.

Мы создали два столбца с классом col-sm-6 (50%) , который будем использовать, чтобы отделить верхнюю часть формы. В пределах первого столбца col-sm-6 мы создали label и поля для имени и электронной почты. Каждый из них содержит label с соответствующим атрибутом for , поэтому метка связана с соответствующим полем.

Каждый из этих столбцов включает в себя form-group , который семантически группирует метки, создавая небольшой отступ снизу:

Типографика

Bootstrap позволяет использовать классы для H1-H6 . Они помогают задать стили встроенных элементов без добавления дополнительных полей или создания блоков элементов super AJAX contact form . Мы использовали класс для H4 , чтобы задать стиль label и сделать их большими.

Класс form-control применяется для каждого элемента ввода, чтобы он занимал всю ширину контейнера (ширина 100% ). Этот класс также добавляет различные стили, которые позволяют создать легко читаемый элемент формы (большой размер, четкие края и т.д. ).

После этих столбцов мы включаем тело сообщения. Мы оборачиваем его в form-group и применяем те же стили, что и для меток и для текстовых полей.

Призыв к действию

Создадим submit button . Bootstrap содержит классы для различных кнопок и их состояний. Мы решили использовать кнопку “success ” (btn-success ), которая, по умолчанию, является зеленой.

Также нужно применить базовый класс btn , чтобы сбросить основные параметры кнопки (рамка, отступ, выравнивание текста, размер шрифта ). Мы применили класс btn-lg , который создает большую кнопку, а затем класс pull-right , который задает обтекание кнопки слева.

После кнопки мы включили div с идентификатором #msgSubmit и применили следующие классы: «h3 text-center hidden «. Класс h3 помогает создать больший заголовок, text-center устанавливает выравнивание текста по центру, а hidden — задает display: none и visible: hidden :

Добавление функционала отправки данных

Мы создали базовую Bootstrap JQuery AJAX form , но она еще ничего не делает. Наш следующий шаг — создать функцию, которая принимает вводимые Пользователями данные и отправляет их асинхронно в PHP .

Откройте файл scripts.js и скопируйте в него следующий код:

$("#contactForm").submit(function(event){ // отменяет отправку данных формы event.preventDefault(); submitForm(); });

Этот фрагмент кода JQuery , который прослушивает функции отправки данных #contactForm (как указано ранее ). Перед этой функцией мы обрабатываем переменную event , которая хранит действие отправки данных формы для функции.

event.preventDeafult() останавливает отправку данных формы при обновлении страницы без выбора действия в форме. И в конце этот код запрашивает функцию submitForm(); :

function submitForm(){ // Инициируем переменную с содержимым формы var name = $("#name").val(); var email = $("#email").val(); var message = $("#message").val(); $.ajax({ type: "POST", url: "php/form-process.php", data: "name=" + name + "&email=" + email + "&message=" + message, success: function(text){ if (text == "success"){ formSuccess(); } } }); } function formSuccess(){ $("#msgSubmit").removeClass("hidden"); }

Три инициированные переменные захватывают значения каждого из полей ввода формы и передают их переменной JavaScript для использования в дальнейшем.

Мы инициируем объект AJAX внутри JQuery и устанавливаем параметры для post , адрес размещения файла PHP , данные, которые мы хотим отправить, и функцию обратного вызова. Данные включают в себя все три переменные с соответствующим id . Функция обратного вызова вызывается, когда объект AJAX успешно принял информацию от скрипта PHP . Функция захватывает возвращенный текст и проверяет, равен ли он строке “success ”. Если да, то запускается финальная функция formSuccess .

Она удаляет скрытый класс из DIV #msgSubmit , который мы применили ранее, выводя таким образом текст.

Подключение к функции PHP Mail

Теперь нужно написать скрипт, который будет получать данные из формы AJAX , и отправлять контент через функцию PHP Mail . Откройте файл process.php и добавьте в него следующий код:

Нам необходимо сохранить переменные, которые мы хотим использовать. Из почтовой функции можно получить три входных переменных и присвоить им те же имена в PHP . Переменная $EmailTo является адресом электронной почты, которую можно задать в скрипте. $Subject — это строка, описывающая тему электронного письма.

Тело письма создается произвольно с добавлением трех созданных переменных. Сначала мы задаем текст описания, например, «Name: «, затем идет переменная, а затем перенос на новую строку (/n) . Те же действия мы повторяем, связывая все данные с переменной $body .

Чтобы отправить электронное письмо, мы присоединяем его к почтовой функции. Присвоив значение переменной $success , мы задаем адрес электронной почты, на который будет отправлено письмо, тему письма, тело и адрес электронной почты отправителя.

Чтобы начать процесс отправки электронной почте, нужно вызвать его в операторе if . Таким образом можно проверить, были ли данные формы успешно предоставлены или нет. Если функция Mail возвращает “true ”, скрипт возвращает “success ”, если функция выдает ошибку, возвращается “invalid ”.

Этот результат будет возвращаться объекту AJAX , и обрабатываться на стороне клиента. Преимущества AJAX заключается в том, что все это делается в асинхронном режиме на стороне клиента. Это позволяет пользователю продолжать взаимодействовать с сайтом во время отправки данных AJAX form :

Наводим блеск

Теперь мы сконцентрируемся на обеспечении обратной связи с пользователем с помощью различных дополнительных функций, которые можно включить. В частности, мы рассмотрим обратную связь формы с помощью обработки ошибок, как на стороне клиента, так и на стороне сервера.

Также для валидации формы мы используем некоторые инструменты:

  • Animate.css: ;
  • Bootstrap Validator .

Добавьте их в проект, как мы ранее делали с Bootstrap и JQuery . Эти инструменты помогут обеспечить обратную связь с пользователем после того, как он отправил данные.

Структура проекта теперь должна выглядеть следующим образом:

Валидация формы

Давайте начнем с установки валидатора после ввода данных формы обратной связи AJAX PHP. Перейдите в файл scripts.js и отредактируйте первый фрагмент кода, который вызывает функцию SubmitForm() после того, как данные формы отправлены.
Его нужно изменить следующим образом:

$("#contactForm").validator().on("submit", function (event) { if (event.isDefaultPrevented()) { // обработка ошибки формы... } else { // все в порядке! event.preventDefault(); submitForm(); } });

Этот новый фрагмент кода проверяет, нашел ли Bootstrap Validator проблемы и остановил ли работу кода. Если нет, мы продолжаем выполнение действий в стандартном режиме. Нам все еще нужно исключить действие по умолчанию (перезагрузку страницы без заполнения формы ) из сценария представления данных формы.

Теперь, если мы нажмем кнопку отправки данных формы, не заполнив все поля, пустые будут выделяться красным цветом:


В процессе добавления валидации мы заблокировали родную валидацию HTML5 . Можно добавить в валидацию дополнительный контекст, включив сообщения об ошибках. Bootstrap Validator имеет удобную функцию, позволяющую отобразить сообщения об ошибке по каждому из полей. Чтобы добавить их, нужно дополнить разметку HTML .

Внутри каждой form-group под полем ввода данных нужно разместить следующий HTML-код :

В качестве примера ниже приведен дополнительный div , добавляемый полям имени и адреса электронной почты:

Name Email

Теперь при повторной отправке данных AJAX JQuery формы будет выводиться сообщение об ошибке, если поля формы не были заполнены: “Please fill in this field. ”. Добавив data-атрибут для вводимых данных под названием “data-error ”, можно включить пользовательское сообщение об ошибке.

Например:

Добавление анимации обратной связи

Мы добавили функционал для индикации незаполненных полей формы. Но было бы неплохо добавить в форму дополнительную анимацию и несколько сообщений, которые дадут пользователю знать, что происходит. В настоящее время при успешной отправке данных формы появляется сообщение «Message Submitted! «, но как насчет ошибок?

Чтобы задействовать существующий код, мы изменим существующее сообщение об успешной отправке данных. Первым делом удалим текст «Message Submitted! » из HTML-разметки и оставим пустой div :

Теперь нужно создать новую функцию для обработки статуса сообщения. Добавьте эту функцию в нижнюю часть файла scripts.js :

function submitMSG(valid, msg){ var msgClasses; if(valid){ msgClasses = "h3 text-center tada animated text-success"; } else { msgClasses = "h3 text-center text-danger"; } $("#msgSubmit").removeClass().addClass(msgClasses).text(msg); }

Эта функция принимает два аргумента. valid будет логической переменной: если ее значение true , будет выводиться сообщение об успешной отправке данных. Если false , будет выводиться сообщение об ошибке. msg — это сообщение, которое мы будем выводить на экран в блоке div .

Данная функция проверяет, имеем ли мы дело с сообщением об успешной отправке данных или с сообщением об ошибке. Это делается через проверку значения переменной valid . В любом случае она устанавливает переменную с соответствующими классами CSS (нам необходимо повторно включить h3 и text-center , так как мы удалим их ).

Примечание: Для класса сообщения об успешной отправке данных мы используем некоторые классы animate.css . При успешной отправке данных AJAX JQuery contact form будет проигрываться анимация tada .

Наконец, функция удаляет все классы из #msgSubmit (чтобы избежать пересечения классов ), а затем устанавливает приоритетные классы и добавляет текст сообщения в div .

Внутри инициализации валидатора в начале этого раздела мы обновляем код, чтобы добавить вызов следующей функции внутри оператора if , когда он дает значение true :

submitMSG(false, "Did you fill in the form properly?");

Теперь, если вы не заполнили все поля, будет выводиться сообщение об ошибке “Did you fill in the form properly? »

Последний шаг для этой новой функции submitMSG — вызвать ее, когда данные формы отправлены успешно. Обновите функцию formSuccess() следующим образом:

$("#contactForm").reset(); submitMSG(true, "Message Submitted!")

Мы хотим сбросить форму и очистить значения, когда вызываем функцию submitMSG , как указано выше с сообщением об успешной отправке данных. Теперь при успешной отправке данных формы должно отображаться соответствующее сообщение с анимацией animate.css tada :

Встряхнемся

Давайте добавим ко всей форме анимацию ошибки, универсальная анимация “тряски ” должна подойти!

Создайте сразу после функции formSuccess() новую и назовите ее formError() :

function formError(){ $("#contactForm").removeClass().addClass("shake animated").one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend", function(){ $(this).removeClass(); }); }

Эта функция использует подход, описанный на демонстрационной странице animate.css , который позволяет добавить анимацию к элементу, а затем повторно вызывать ее снова.

Анимация CSS имеет неприятную особенность: отсутствие возможности повторного проигрывания, даже если удалить и повторно добавить класс. Эта функция помогает сбросить классы конца анимации, что позволяет повторно добавить их. Когда пользователь нажимает кнопку «Отправить », не заполнив все поля AJAX формы обратной связи, мы проигрываем анимацию shake . И если он снова не заполнит все поля, нужно снова проиграть эту анимацию.

Можно вызвать эту функцию formError() выше функции submitMSG() , которую мы создали для сообщения об ошибке. Например, так:

formError(); submitMSG(false, "Did you fill in the form properly?");

Теперь, когда пользователь попытается отправить данные формы, не заполнив все поля, она будет трястись, чтобы он понял, что что-то не так.

Больше валидации

Валидация на стороне клиента — это хорошо, но любой пользователь может отключить ее и отправить форму с пустыми полями, отредактировав код в браузере. Необходимо создать сервис валидации на стороне сервера.

Нам нужно открыть файл process.php и внести в него необходимые изменения, чтобы обеспечить проверку заполнения всех полей. Мы создадим переменную $errorMSG , с помощью которой будем перехватывать сообщения об ошибках, а затем включим дополнительную проверку $_POST :

Этот PHP-код проверяет, существуют ли пустые поля AJAX form , перед тем как установить их данные в качестве соответствующих переменных (заменяет существующие заданные в коде переменные из $_POST ). Если поля пусты, мы задаем общее сообщение для отправки обратно клиенту.

В ответ на исходный вызов AJAX нам нужно послать сообщение об ошибке, которое будет отображаться в браузере. Для этого отредактируйте оператор if , который мы создали ранее в нижней части кода PHP :

Через оператор if мы проверяем, является ли переменная $errorMSG пустой («») , а также статус встроенной функции Mail , которую мы использовали для переменной $success . В условии else мы включили дополнительную проверку, является ли ошибка результатом сбоя $success . Если да, то отправляем обратно сообщение “Something went wrong: “. Иначе выводим сообщение, которое было скомпилировано, когда мы производили проверку пустых полей.

И последний этап — нужно принять новое сообщение в AJAX и вывести его в форме. Нам нужно обновить объект AJAX в файле scripts.js следующим образом:

$.ajax({ type: "POST", url: "php/form-process.php", data: "name=" + name + "&email=" + email + "&message=" + message, success: function(text){ if (text == "success"){ formSuccess(); } else { formError(); submitMSG(false,text); } } });

Мы только что обновили условия else , которое проверяет соответствие text == success . В else мы вызываем функцию formError() , которая применяет анимацию «тряски » и запрашиваем у функции submitMSG() текст, возвращенный из PHP .

Заключение

Зайдите на Github , чтобы посмотреть весь код целиком. Теперь форма обратной связи AJAX PHP предоставляет пользователю информацию о том, какие из полей он не заполнил. Мы выводим контекстные сообщения, основанные на статусе и возвращаемом от PHP сообщении. А также реализовали дополнительный уровень проверки на стороне сервера для тех пользователей, которые пытаются обойти front-end валидацию.

Я надеюсь, вам понравилась эта статья. Пожалуйста, оставляйте в комментариях свои вопросы и отзывы.

Данная публикация представляет собой перевод статьи «Building a Bootstrap Contact Form Using PHP and AJAX » , подготовленной дружной командой проекта

Это доступный и безопасный способ платить за товары и услуги через интернет. Добавьте обработчик этой системы в ваш интернет-магазин и принимайте платежи без проволочек.

Обновленный протокол Яндекс.Деньги 3.0 позволяет использовать разные типы оплаты:

  • Собственно Яндекс.Деньги ;
  • Банковские карты;
  • Платежи через терминалы;
  • Мобильные платежи.
Кроме того, процедура подключения магазинов, созданных на платформе 1С-Битрикс: Управление сайтом, к Янедекс.Деньгам значительно упрощена. Вам нужно оставить заявку на сайте Яндекс.Деньги (в комментарии укажите, что у вас сайт на с версией протокола 3.0.) и заполните упрощенную анкету (состоящую всего из 3-х полей).

Для подключения нового протокола Яндекс.Деньги в интернет-магазине на платформе 1С-Битрикс нужно создать новую платежную систему и выбрать обработчик

Проверьте, отвечает ли ваш сайт по протоколу https. Это обязательное условие для приема платежей по протоколу Янедекс.Деньги 3.0.

Можно использоваться самоподписанный SSL-сертификат или нашу Виртуальную машину , где уже все настроено.

Далее укажите Идентификатор магазина в ЦПП , Номер витрины магазина в ЦПП , которые вы должны получить от Яндекса при заключении договора, и Пароль магазина (shopPassword) из анкеты магазина для Яндекса.

Для каждого типа отплаты необходимо будет создать свой обработчик и выбрать требуемый тип оплаты.

Чтобы протестировать оплату нужно поставить значение “Y” в поле Тестовый режим . При отправке заявки на подключение протокола Яндекс.Деньги 3.0 вам придет специальная ссылка, используя которую вы пополнить баланс вашего кошелька для теста на 1000 рублей.

В настройках модуля Интернет-магазин можно переопределить пути к страницам с сообщением об успешной оплате или ошибке.

Всё. Настройка окончена.Пользуйтесь

Клиентам, подключившим предыдущую версию Яндекс.Денег (1.6), не обязательно переходить на версию 3.0. Т.е. обе версии поддерживаются, как Яндекс.Деньгами, так и 1С-Битрикс: Управление сайтом. НО в версию 1.6 не добавляется возможность использовать оплату, кроме Яндекс.Деньги. Новые клиенты этой платежной системы подключаются по протоколу 3.0.

В поставку продукта 1С-Битрикс: Управление сайтом обработчик Яндекс.Денег (3.0) выйдет в 14.0.0 версии модуля Интернет-магазин (sale). До выхода этого обновления (предполагаем, в конце месяца выйдет в альфе) обработчик можно запросить через Техническую поддержку.


В списке десяти наиболее распространённых видов атак по версии OWASP первые два места занимают атаки с внедрением кода и XSS (межсайтовый скриптинг). Они идут рука об руку, потому что XSS, как и ряд других видов нападений, зависит от успешности атак с внедрением. Под этим названием скрывается целый класс атак, в ходе которых в веб-приложение внедряются данные, чтобы заставить его выполнить или интерпретировать вредоносный код так, как это нужно злоумышленнику. К таким атакам относятся, например, XSS, внедрение SQL, внедрение заголовка, внедрение кода и полное раскрытие путей (Full Path Disclosure). И это лишь малая часть.


Атаки с внедрением - страшилка для всех программистов. Они наиболее распространены и успешны за счёт разнообразия, масштабности и (иногда) сложности защиты от них. Всем приложениям нужно брать откуда-то данные. XSS и UI Redress встречаются особенно часто, поэтому я посвятил им отдельные главы и выделил их из общего класса.


OWASP предлагает следующее определение атак с внедрением:


Возможности внедрения - вроде SQL, OS и LDAP - возникают тогда, когда интерпретатор получает ненадёжные данные в виде части командного запроса. Зловредные данные могут обмануть интерпретатор и заставить его выполнить определённые команды или обратиться к неавторизованным данным.

Внедрение SQL

Внедрение SQL - самая распространённая и чрезвычайно опасная форма атак с внедрением. Трудно переоценить серьёзность этой угрозы, поэтому крайне важно понимать, что влияет на успешность атак и как от них защититься.


Итак, данные внедряются в веб-приложение, а затем используются в SQL-запросах. Обычно они попадают из ненадёжных источников ввода, например из веб-форм. Однако внедрение может выполняться и из других мест, скажем, из самой базы данных. Программисты часто верят в полную безопасность своей базы, не осознавая, что если она была безопасна в одном случае, то это совершенно не значит, что она будет безопасна и впредь. Данные из базы должны считаться ненадёжными до тех пор, пока не доказано обратное, т. е. пока они не прошли проверку.


Если атака прошла успешно, злоумышленник может манипулировать SQL-запросом так, чтобы тот выполнял с базой данных операции, которые не предусмотрены разработчиками.


Посмотрите на этот запрос:


$db = new mysqli("localhost", "username", "password", "storedb"); $result = $db->query("SELECT * FROM transactions WHERE user_id = " . $_POST["user_id"]);

Здесь целый ряд косяков. Во-первых, мы не проверяли содержимое POST-данных на предмет корректности user_id . Во-вторых, мы позволяем ненадёжному источнику сообщать нам, какой user_id использовать: атакующий может подсунуть любой корректный user_id . Возможно, он содержался в скрытом поле формы, которую мы считали безопасной, потому что её нельзя редактировать (при этом забыв, что атакующие могут вводить любые данные). В-третьих, мы не заэкранировали user_id и не передали его в запрос в виде параметра (bound parameter), что тоже позволяет атакующему внедрять произвольные строки, которые будут манипулировать SQL-запросом, учитывая, что мы не смогли проверить его в первую очередь.


Это три упущения очень часто встречаются в веб-приложениях.


Что касается доверия к базе данных, то представьте, что мы искали транзакции с помощью поля user_name . Имена имеют широкий охват и могут содержать кавычки. Допустим, атакующий сохранил внедрённое строковое значение в одном из пользовательских имён. Когда мы снова используем это значение в одном из следующих запросов, то оно будет манипулировать строкой запроса, раз мы посчитали базу надёжным источником, не изолировали и не ограничили скомпрометированный запрос.


Также уделяйте внимание другому фактору внедрения SQL: постоянное хранилище не всегда нужно держать на сервере. HTML 5 поддерживает использование БД на стороне клиента, куда можно отправлять запросы с помощью SQL и JavaScript. Для этого есть два API: WebSQL и IndexedDB. В 2010 году W3C не рекомендовал выбирать WebSQL; он поддерживается WebKit-браузерами, использующими SQLite в качестве бэкенда. Скорее всего, поддержка сохранится ради обратной совместимости, даже несмотря на рекомендацию W3C. Как следует из его названия, этот API принимает SQL-запросы, а значит, может быть мишенью атак с внедрением. IndexedDB - это более новая альтернатива, база данных NoSQL (не требует использования SQL-запросов).

Примеры внедрения SQL

Манипулирование SQL-запросами может преследовать такие цели:

  • Утечки данных.
  • Раскрытие хранимой информации.
  • Манипулирование хранимой информацией.
  • Обход авторизации.
  • Внедрение SQL на стороне клиента.
  • Защита от внедрения SQL

    Защита от внедрения SQL базируется на принципе эшелонирования. Перед использованием данных в запросе надо проверять, что их форма корректна. Также необходимо изолировать данные до включения в запрос либо включать в виде параметра перехода.

    Проверка

    Я не устаю повторять: все данные, которые не были явным образом созданы в исходном PHP-коде текущего запроса, ненадёжны. Строго проверяйте их и отклоняйте всё, что не прошло проверок. Не пытайтесь «исправить» данные, можно внести лишь лёгкие, косметические изменения в формат.


    К частым ошибкам относится проверка данных для дальнейшего текущего использования (например, для отображения на экране или вычислений) и отсутствие проверки полей базы данных, в которой в результате будет сохранена информация.

    Экранирование

    С помощью расширения mysqli вы можете изолировать все данные, включённые в SQL-запрос. Это делает функция mysqli_real_escape_string() . Расширение pgsql для PostgresSQL предлагает функции pg_escape_bytea() , pg_escape_identifier() , pg_escape_literal() и pg_escape_string() . В расширении mssql (Microsoft SQL Server) нет изолирующих функций, а подход с применением addslashes() неэффективен - вам понадобится кастомная функция .


    Чтобы ещё больше усложнить вам жизнь, скажу, что вы не имеете права на ошибку при изолировании вводимых в запрос данных. Один промах - и вы уязвимы для атаки.


    Подведём итог. Экранирование - не лучший вариант защиты. К нему стоит прибегать в крайнем случае. Оно может понадобиться, если используемая вами для абстракции библиотека БД допускает настройку голых SQL-запросов или частей запроса без принудительной привязки параметров. В остальных случаях лучше вообще избегать изолирования. Этот подход сложен, провоцирует ошибки и различается в зависимости от расширения базы данных.

    Параметризованные запросы (заранее подготовленные выражения)

    Параметризация, или привязка параметров, - это рекомендованный способ создания SQL-запросов. Все хорошие библиотеки БД применяют его по умолчанию. Вот пример использования расширения PDO для PHP:


    if(ctype_digit($_POST["id"]) && is_int($_POST["id"])) { $validatedId = $_POST["id"]; $pdo = new PDO("mysql:store.db"); $stmt = $pdo->prepare("SELECT * FROM transactions WHERE user_id = :id"); $stmt->bindParam(":id", $validatedId, PDO::PARAM_INT); $stmt->execute(); } else { // отклонить значение id и сообщить пользователю об ошибке }

    Метод bindParam() , доступный для выражений PDO, позволяет привязывать параметры к «местам для вставки» (placeholders), представленным в заранее подготовленном выражении. Этот метод принимает параметры основных типов данных, например, PDO::PARAM_INT , PDO::PARAM_BOOL , PDO::PARAM_LOB и PDO::PARAM_STR . Для PDO::PARAM_STR это делается по умолчанию, если не задано другое, так что запомните и для других значений!


    В отличие от ручного изолирования, привязка параметров (или иной другой метод, используемый вашей библиотекой БД) позволит корректно изолировать автоматически привязываемые данные, так что вам не придётся вспоминать, какую функцию применять. Также согласованная привязка параметров гораздо надёжнее, чем попытка не забыть, что нужно изолировать всё вручную.

    Реализация принципа наименьших привилегий

    Прервать успешное внедрение SQL не менее важно, чем его полностью предотвратить. Когда атакующий получает возможность выполнять SQL-запросы, то он будет это делать как конкретный пользователь БД. Благодаря принципу наименьших привилегий можно удостовериться, что все пользователи имеют лишь те привилегии, которые абсолютно необходимы для выполнения их задач.


    Если у пользователя широкие привилегии, то атакующий может удалять таблицы и менять привилегии других пользователей, выполняя от их имени новые внедрения SQL. Чтобы этого не произошло, никогда не обращайтесь к БД из веб-приложения от лица root’а, администратора или иного пользователя с высокими привилегиями.


    Другое применение принципа - разделение ролей чтения и записи данных в базу. Выберите одного пользователя с правами только на запись, а другого - с правами только на чтение. Если атака будет направлена на «читающего» пользователя, то у злоумышленника не получится манипулировать данными в таблице или записывать их. Можно ограничивать доступ в ещё более узких рамках, тем самым уменьшая последствия успешных атак с внедрением SQL.


    Многие веб-приложения, особенно с открытым кодом, разработаны так, что в них используется только один пользователь БД, уровень привилегий которого почти наверняка никогда не проверяется. Так что не забывайте об этом моменте и не пытайтесь запускать приложения под администраторским аккаунтом.

    Внедрение кода (известно как удалённое включение файла, Remote File Inclusion)

    Внедрение кода - это любые способы, которые позволяют атакующему добавлять в веб-приложение исходный код с возможностью его интерпретировать и исполнять. При этом речь не идёт о внедрении кода в клиентскую часть, например в JavaScript, здесь применяются уже XSS-атаки.


    Можно внедрить исходный код напрямую из ненадёжного источника входных данных либо заставить веб-приложение загрузить его из локальной файловой системы или внешнего ресурса вроде URL. Когда код внедряется в результате включения внешнего источника, то это обычно называют удалённым включением файла (RFI), хотя само по себе RFI всегда предназначено для внедрения кода.


    Основные причины внедрения кода:

    • обход проверки входных данных,
    • внедрение ненадёжных входных данных в любой контекст, где они будут расценены как PHP-код,
    • взлом безопасности репозиториев исходного кода,
    • отключение предупреждений о загрузке сторонних библиотек,
    • переконфигурация сервера, чтобы он передавал в PHP-интерпретатор файлы, не относящиеся к PHP.

    Последнему пункту уделяйте особое внимание: в этом случае ненадёжные пользователи могут загрузить на сервер любые файлы.

    Примеры внедрения кода

    В PHP множество целей для внедрения кода, так что этот тип атак возглавляет список наблюдения у любого программиста.

    Включение файла

    Самые очевидные цели для внедрения кода - функции include() , include_once() , require() и require_once() . Если ненадёжные входные данные позволят определить передаваемый в эти функции параметр path , то можно будет удалённо управлять выбором файла для включения. Нужно отметить, что включённый файл не обязан быть настоящим PHP-файлом, допускается использование файла любого формата, способного хранить текстовые данные (т. е. почти без ограничений).


    Параметр path также может быть уязвим для атак обхода каталога (Directory Traversal) или удалённого включения файла. Использование в path комбинаций символов../ или… позволяет атакующему переходить почти к любому файлу, к которому имеет доступ PHP-процесс. Заодно в конфигурации PHP по умолчанию вышеприведённые функции принимают URL, если не отключён allow_url_include.

    Проверка

    Функция PHP eval() принимает к исполнению строку PHP-кода.

    Внедрение регулярных выражений

    Функция PCRE (регулярное выражение, совместимое с Perl) preg_replace() в PHP допускает использование модификатора e (PREG_REPLACE_EVAL). Это означает замещающую строку, которая после подстановки будет считаться PHP-кодом. И если в этой строке имеются ненадёжные входные данные, то они смогут внедрить исполняемый PHP-код.

    Дефектная логика включения файла

    Веб-приложения по определению включают в себя файлы, необходимые для обслуживания любых запросов. Если воспользоваться дефектами логики маршрутизации, управления зависимостями, автозагрузки и других процессов, то манипуляция с путём прохождения запроса или его параметрами заставит сервер включить конкретные локальные файлы. Поскольку веб-приложение не рассчитано на обработку таких манипуляций, последствия могут быть непредсказуемыми. Например, приложение невольно засветит маршруты, предназначенные только для использования в командной строке. Или раскроет другие классы, конструкторы которых выполняют задачи (лучше классы так не проектировать, но это всё же встречается). Любой из этих сценариев может помешать операциям бэкенда приложения, что позволит манипулировать данными или провести DOS-атаку на ресурсоёмкие операции, которые не подразумевают прямого доступа.

    Задачи внедрения кода

    Спектр задач чрезвычайно широк, поскольку этот тип атак позволяет выполнять любой PHP-код на выбор атакующего.

    Defenses against Code InjectionCommand InjectionExamples of Command InjectionDefenses against Command InjectionВнедрение лога (известно как внедрение лог-файла, Log File Injection)

    Многие приложения собирают логи, а авторизованные пользователи часто просматривают их через HTML-интерфейс. Поэтому логи - одна из главных целей злоумышленников, желающих замаскировать другие атаки, обмануть тех, кто просматривает логи, и даже провести затем атаку на пользователей мониторингового приложения, с помощью которого читаются и анализируются логи.


    Уязвимость логов зависит от механизмов контроля над записью логов, а также от обращения с данными логов как с ненадёжным источником при просмотре и анализе записей.


    Простая система журналирования может записывать в файл текстовые строки с помощью file_put_contents() . Например, программист регистрирует ошибочные попытки авторизации в виде строк следующего формата:


    sprintf("Failed login attempt by %s", $username);

    А что, если атакующий использует в форме имя «AdminnSuccessful login by Adminn»?


    Если эта строка будет вставлена в лог из ненадёжных входных данных, то атакующий успешно замаскирует неудачную попытку авторизации с помощью невинной неудачи ввода админского пароля. Подозрительность данных снизится ещё больше, если добавить успешную попытку авторизации.


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

    Задачи внедрения лога

    Одна из целей внедрения - интерпретаторы формата логов. Если инструмент анализа использует регулярные выражения для парсинга записей в логе, чтобы делить их на части и раскидывать по разным полям, то можно создать и внедрить такую строку, которая заставит регулярные выражения выбирать внедрённые поля вместо правильных. Например, эта запись может стать источником нескольких проблем:


    $username = "iamnothacker! at Mon Jan 01 00:00:00 +1000 2009"; sprintf("Failed login attempt by %s at %s", $username,)

    Более изощрённые атаки с внедрением логов базируются на атаках с обходом каталога, чтобы отобразить лог в браузере. При подходящих условиях внедрение PHP-кода в сообщение лога и открытие в браузере файла с записями приведёт к успешному внедрению кода, аккуратно форматированного и выполняемого по желанию злоумышленника. А если дойдёт до исполнения на сервере зловредного PHP, то остаётся надеяться лишь на эффективность эшелонирования защиты, которое может уменьшить ущерб.

    Защита от внедрения лога

    Проще всего фильтровать все внешние сообщения логов с помощью белого списка. Допустим, ограничить набор символов только цифрами, буквами и пробелами. Сообщения, содержащие неразрешённые символы, считаются повреждёнными. Тогда в журнале появляется запись о потенциальной попытке внедрения лог-файла. Это простой способ защиты для простых текстовых логов, когда нельзя избежать включения в сообщения ненадёжных входных данных.


    Второй метод защиты - преобразование порций ненадёжных входных данных с помощью системы наподобие base64 , которая поддерживает ограниченный набор символов, при этом позволяя хранить в текстовом виде разнообразную информацию.

    Обход пути (известен как обход каталога, Directory Traversal)

    Атаки с обходом пути - это попытки повлиять на операции чтения или записи файлов в бэкенде веб-приложения. Делается это с помощью внедрения параметров, которые позволяют манипулировать путями файлов, вовлечённых в операции бэкенда. Так что атаки этого типа облегчают раскрытие информации (Information Disclosure) и локальное/удалённое внедрение файлов.


    Такие атаки мы рассмотрим отдельно, но в основе их успешности лежит именно обход пути. Поскольку описанные ниже функции характерны именно для манипулирования путями файлов, имеет смысл упомянуть, что многие PHP-функции не принимают пути к файлам в привычном смысле слова. Вместо этого функции наподобие include() или file() принимают URI.


    Выглядит совершенно противоестественно. Но это означает эквивалентность двух следующих вызовов функций, которые используют абсолютные пути (например, не полагаясь на автозагрузку относительных путей).


    include(‘/var/www/vendor/library/Class.php’); include(‘file:///var/www/vendor/library/Class.php‘);

    Дело в том, что относительный путь обрабатывается на стороне (настройка include_path в php.ini и доступных автозагрузчиках). В таких случаях PHP-функции особенно уязвимы для многих форм манипуляций с параметрами, включая подмену схемы URI файла (File URI Scheme Substitution), когда атакующий может внедрить HTTP или FTP URI, если в начало пути файла внедрены ненадёжные данные. Подробнее об этом мы поговорим в разделе, посвящённом атакам с удалённым включением файлов, а пока сосредоточимся на обходах путей файловых систем.


    Эта уязвимость подразумевает изменение пути для обращения к другому файлу. Обычно это достигается с помощью внедрения серии последовательностей../ в аргумент, который затем присоединяется к функциям или целиком вставляется в функции наподобие include() , require() , file_get_contents() и даже менее подозрительные (для кого-то) функции вроде DOMDocument::load() .


    С помощью последовательности../ атакующий заставляет систему вернуться в родительский каталог. Так что путь /var/www/public/../vendor на самом деле ведёт в /var/www/vendor . Последовательность../ после / public возвращает нас в родительский каталог, т. е. в / var/www . Таким образом злоумышленник получает доступ к файлам, расположенным вне каталога / public , доступного с веб-сервера.


    Конечно, обход пути не ограничивается одним лишь возвратом. Можно внедрить новые элементы пути, чтобы получить доступ к дочерним каталогам, которые недоступны из браузера в связи с настройками ограничений в.htaccess. Операции PHP с файловой системой не волнует конфигурация контроля доступа к непубличным файлам и каталогам на веб-сервере.

    Examples of Path TraversalDefenses against Path TraversalВнедрение XML

    Несмотря на внедрение JSON в качестве облегчённого средства передачи данных между сервером и клиентом, XML остаётся популярной альтернативой, API веб-сервисов зачастую поддерживает её параллельно с JSON. Также XML применяется для обмена данными, использующими XML-схемы: RSS, Atom, SOAP и RDF и т. д.


    XML вездесущ: его можно найти в серверах веб-приложений, в браузерах (в качестве предпочтительного формата для запросов и откликов XMLHttpRequest) и браузерных расширениях. Учитывая его распространённость и обработку по умолчанию таким популярным парсером, как libxml2, используемым PHP в DOM и в расширениях SimpleXML и XMLReader, XML стал целью для атак с внедрением. Когда браузер активно участвует в XML-обмене, необходимо учитывать, что посредством XSS авторизованные пользователи могут передавать XML-запросы, созданные на самом деле злоумышленниками.

    Внедрение внешней XML-сущности (XXE)

    Такие атаки существуют из-за того, что библиотеки парсинга XML часто поддерживают использование ссылок на кастомные сущности. Вы познакомитесь со стандартным XML-дополнением сущностей, его применяют для представления специальных символов разметки наподобие > , < ; и &apos ;. XML позволяет расширять набор стандартных сущностей, определяя посредством самого XML-документа кастомные сущности. Их можно определять, напрямую включая в опциональный DOCTYPE. Представляемое ими расширенное значение может ссылаться на внешний ресурс, который должен быть включён. XXE-атаки стали популярны именно благодаря возможности ординарного XML хранить кастомные ссылки, которые могут увеличиваться за счёт содержимого внешних ресурсов. При обычных условиях ненадёжные входные данные никогда не должны непредвиденным образом взаимодействовать с нашей системой. А большинство программистов XXE почти однозначно не предвидят XXE-атаки, что вызывает особую озабоченность.


    Давайте, к примеру, определим новую кастомную сущность harmless:



    XML-документ с этим определением теперь может ссылаться на сущность &harmless; везде, где вообще разрешены сущности:


    This result is &harmless;

    Когда XML-парсер вроде PHP DOM будет интерпретировать этот XML, он обработает эту кастомную сущность сразу же, как загрузится документ. Поэтому при запросе соответствующего текста вернёт следующее:


    This result is completely harmless


    Очевидно, что кастомные сущности имеют преимущество в представлении повторяющегося текста и XML с более короткими именами сущностей. Нередко бывает так, что XML должен следовать определённой грамматике, а кастомные сущности упрощают редактирование. Однако, учитывая наше недоверие к внешним входным данным, необходимо быть очень осторожными со всеми XML, потребляемыми нашим приложением. Например, это вовсе не безопасная разновидность:


    &harmless;

    В зависимости от содержимого запрошенного локального файла данные могут использоваться при расширении сущности &harmless; . А потом расширенный контент может быть извлечён из XML-парсера и включён в исходящие данные веб-приложения для анализа атакующим. Например, для раскрытия информации. Извлечённый файл будет интерпретирован как XML, хотя специальных символов, инициирующих такое интерпретирование, нет. Это ограничивает масштаб раскрытия содержимого локального файла. Если файл интерпретирован как XML, но не содержит корректного XML, то наверняка мы получим ошибку, что предотвратит раскрытие содержимого. Однако в PHP доступен аккуратный трюк, позволяющий обойти ограничение масштаба, поэтому удалённые HTTP-запросы влияют на веб-приложение, даже если возвращённый ответ нельзя передать обратно атакующему.


    В PHP часто встречаются три метода парсинга и использования XML: PHP DOM, SimpleXML и XMLReader. Все они применяют расширение libxml2, поддержка внешних сущностей включена по умолчанию. Как следствие, в PHP по умолчанию есть уязвимость к XXE-атакам, которую очень легко пропустить при рассмотрении безопасности веб-приложения или библиотеки, использующей XML.


    Не забывайте также, что XHTML и HTML 5 могут быть сериализованы как корректный XML. А значит, некоторые XHTML-страницы или XML-сериализованный HTML 5 могут парситься как XML, с использованием DOMDocument::loadXML() вместо DOMDocument::loadHTML() . Такое применение XML-парсера тоже уязвимо к внедрению внешних XML-сущностей. Помните, что libxml2 пока даже не распознаёт HTML 5 DOCTYPE, поэтому не может проверить его как XHTML DOCTYPES.

    Примеры внедрения внешних XML-сущностей Содержимое файла и раскрытие информации

    Выше мы рассмотрели пример раскрытия информации, отметив, что кастомная XML-сущность может ссылаться на внешний файл.


    &harmless;

    В данном случае кастомная сущность &harmless; будет расширена содержимым файлов. Поскольку все подобные запросы выполняются локально, это позволяет раскрыть содержимое всех файлов, которые может считать приложение. То есть когда расширенная сущность будет включена в исходящие данные приложения, атакующий сможет изучить файлы, находящиеся в закрытом доступе. Правда, в данном случае есть серьёзное ограничение: файлы должны быть либо XML-формата, либо формата, который не приведёт к возникновению ошибки XML-парсера. Но дело в том, что это ограничение можно полностью проигнорировать в PHP:


    &harmless;

    PHP даёт доступ к обёртке в виде URI, одного из протоколов, принимаемого стандартными функциями по работе с файловой системой: file_get_contents() , require() , require_once() , file() , copy() и многими другими. Обёртка PHP поддерживает ряд фильтров, которые можно применять к конкретному ресурсу, чтобы результаты возвращались вызовом функции. В приведённом выше примере мы применяем к целевому файлу, который хотим прочесть, фильтр convert.base-64-encode .


    Это означает, что атакующий может прочитать любой доступный для PHP файл, независимо от текстового формата. Достаточно просто декодировать исходящие из приложения данные, а потом безнаказанно их препарировать. Хотя это не наносит прямого вреда конечным пользователям или бэкенду приложения, зато позволяет атакующим хорошо изучить структуру приложения в попытке найти иные уязвимости. Причём риск, что атакующих обнаружат, минимален.

    Обход контроля доступа

    Доступ контролируется разными способами. Поскольку XXE-атаки проводятся на бэкенде веб-приложения, использовать сессию текущего пользователя не получится. Но атакующий всё ещё может обойти контроль доступа к бэкенду с помощью запросов от локального сервера. Рассмотрим такой примитивный контроль доступа:


    if (isset($_SERVER["HTTP_CLIENT_IP"]) || isset($_SERVER["HTTP_X_FORWARDED_FOR"]) || !in_array(@$_SERVER["REMOTE_ADDR"], array("127.0.0.1", "::1",))) { header("HTTP/1.0 403 Forbidden"); exit("You are not allowed to access this file."); }

    Этот кусок PHP, как и несметное количество ему подобных, ограничивает доступ к определённым PHP-файлам на локальном сервере, т. е. localhost. Однако XXE-атака на фронтенде приложения даёт атакующему точные учётные данные, необходимые для обхода этого контроля доступа, потому что все HTTP-запросы XML-парсера будут делаться из localhost.


    &harmless;

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

    DOS-атаки

    Для DOS-атак можно использовать почти всё, что диктует потребление серверных ресурсов. С помощью внедрения внешней XML-сущности атакующий получает возможность делать произвольные HTTP-запросы, которые при подходящих условиях истощают серверные ресурсы.


    Позднее мы поговорим о других потенциальных DOS-применениях XXE-атак с точки зрения расширения XML-сущностей.

    Защита от внедрения внешних XML-сущностей

    Такие атаки очень популярны, так что вас удивит, как просто от них защититься. Поскольку DOM, SimpleXML и XMLReader опираются на libxml2, можно всего лишь применить функцию libxml_disable_entity_loader() , отключающую использование внешних сущностей. Правда, это не отключит кастомные сущности, заранее определённые в DOCTYPE, потому что они не используют внешние ресурсы, требующие выполнения HTTP-запроса или операции в файловой системе.


    $oldValue = libxml_disable_entity_loader(true); $dom = new DOMDocument(); $dom->loadXML($xml); libxml_disable_entity_loader($oldValue);

    Это нужно сделать для всех операций, подразумевающих загрузку XML из строковых, файлов или удалённых URI.


    Там, где приложению никогда не требуются внешние сущности, а также для большинства его запросов можно вообще отключить загрузку внешних ресурсов на более глобальном уровне. В большинстве случаев это куда предпочтительнее для определения всех «точек» загрузки XML, учитывая, что многие библиотеки имеют врождённые уязвимости к XXE-атакам:


    libxml_disable_entity_loader(true);

    Только не забывайте возвращать значение TRUE после каждого временного включения загрузки внешних ресурсов. Она может понадобиться для таких безобидных задач, как преобразование Docbook XML в HTML, когда применение XSL-стилей зависит от внешних сущностей.


    Однако отключающая libxml2 функция - не панацея. Проанализируйте другие расширения и PHP-библиотеки, которые парсят или как-то ещё обрабатывают XML, чтобы найти их «выключатели» применения внешних сущностей.


    Если это сделать невозможно, то дополнительно проверьте, объявляет ли XML-документ DOCTYPE. Если да и если при этом внешние сущности отключены, то просто выкиньте XML-документ, запретив ненадёжный XML-доступ к потенциально уязвимому парсеру и журналируя его как вероятную атаку. Это необходимый шаг, потому что других признаков не будет - ни ошибок, ни исключений. Встройте эту проверку в свою обычную валидацию входных данных. Но этот метод далеко не идеален, так что крайне рекомендуется в корне решить проблему внешних сущностей.


    /** * Attempt a quickie detection */ $collapsedXML = preg_replace("/[:space:]/", "", $xml); if(preg_match("/