Reg.ru: домены и хостинг

Крупнейший регистратор и хостинг-провайдер в России.

Более 2 миллионов доменных имен на обслуживании.

Продвижение, почта для домена, решения для бизнеса.

Более 700 тыс. клиентов по всему миру уже сделали свой выбор.

*Наведите курсор мыши для приостановки прокрутки.

Назад Вперед

Создание простой системы регистрации пользователей на PHP и MySQL

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

И даже после того как вы всё это напишите, пользователи будут регистрироваться неохотно, т.к. это требует определённых усилий с их стороны.

В этом уроке, мы создадим очень простую систему регистрации, которая не требует и не хранит паролей вообще! Результат будет легко изменить и добавить к уже существующему PHP-сайту. Хотите выяснить, как это работает? Читайте ниже.



Вот как наша супер простая система будет работать:

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

После этого создаётся некий случайный уникальный набор символов (токен), который отправляется на указанную пользователем почту в виде ссылки, которая будет актуальна в течение 10 минут;
- По ссылке пользователь переходит на наш сайт. Система определяет наличие токена и авторизует пользователя;

Преимущества такого подхода:

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

Недостатки:

Безопасность аккаунта пользователя. Если кто-то имеет доступ к почте пользователя, он может авторизоваться.
- Email не защищён и может быть перехвачен. Имейте в виду, что этот вопрос актуален и в случае, когда пароль был забыт и его необходимо восстановить, или в любой системе авторизации, которая не использует HTTPS для передачи данных (логин/пароль);
- Пока вы настроите как нужно почтовый сервер, существует шанс, что сообщения со ссылками на авторизацию будут попадать в спам;

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

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

Как пользоваться этой системой

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

Вам нужно скачать исходники, приложенные к уроку
- В архиве найти файл tables.sql Импортируйте его в вашу базу данных используя опцию импорта в phpMyAdmin. Альтернативный способ: открыть этот файл через текстовый редактор, скопировать SQL запрос и выполнить его;
- Открыть includes/main.php и заполнить настройки связи с вашей базой данных (указать пользователя и пароль для связи с базой а также хост и имя базы). В этом же файле, вы также должны указать email, который будет использован в качестве оригинального адреса для сообщений отправляемых системой. Некоторые хосты блокируют исходящие мейлы пока в форме не будет указан настоящий email адрес, который был создан из панели управления хостом, так что укажите реальный адрес;
- Загрузите все файлы index.php , protected.php и папки assets и includes через FTP на ваш хост;
- Добавьте код ниже на каждую PHP-страницу, где нужно отобразить форму авторизации;

Require_once "includes/main.php"; $user = new User(); if(!$user->loggedIn()){ redirect("index.php"); }
- Готово!

Для тех же, кому интересно, как это всё работает - вперёд к чтению ниже!

Первый шаг - написание HTM- кода формы авторизации. Данный код располагается в файле index.php . Этот файл также содержит PHP-код, обрабатывающий данные формы и другие полезные функции системы авторизации. Узнать об этом больше можно в разделе ниже, посвящённом обзору PHP кода.

index.php

Tutorial: Super Simple Registration System With PHP & MySQL Login or Register

Enter your email address above and we will send
you a login link.

Login / Register

В головной секции (между тегами и ) я подключил основные стили (в этом уроке они не разбираются, поэтому вы можете посмотреть их сами. Папка assets/css/style.css). До закрывающего тега я подключил библиотеку jQuery и файл script.js, который мы напишем и разберём чуть ниже.


JavaScript

jQuery отслеживает состояние кнопки "Зарегистрироваться/авторизоваться" с помощью функции e.preventDefault() и отправляет AJAX-запросы. В зависимости от ответа сервера, выводит то или иное сообщение и определяет дальнейшие действия/

assets/js/script.js

$(function(){ var form = $("#login-register"); form.on("submit", function(e){ if(form.is(".loading, .loggedIn")){ return false; } var email = form.find("input").val(), messageHolder = form.find("span"); e.preventDefault(); $.post(this.action, {email: email}, function(m){ if(m.error){ form.addClass("error"); messageHolder.text(m.message); } else{ form.removeClass("error").addClass("loggedIn"); messageHolder.text(m.message); } }); }); $(document).ajaxStart(function(){ form.addClass("loading"); }); $(document).ajaxComplete(function(){ form.removeClass("loading"); }); });

был добавлен в форму для отображения текущего состояния AJAX-запроса (это стало возможным благодаря методам ajaxStart() ) и ajaxComplete() , которые вы сможете найти ближе к концу файла).

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

Схема базы данных

Наша невероятно простая система регистрации использует 2 MySQL таблицы (SQL-код находится в файле tables.sql ). Первая хранит данные об аккаунтах пользователей. Вторая хранит информацию о количестве попыток входа.


Схема таблицы пользователей.

Система не использует паролей, что видно на схеме. На ней же можно увидеть колонку token с токенами, соседствующую с колонкой token_validity . Токен устанавливается как только пользователь подключается к системе, задаёт свой email для отправки сообщения (чуть подробнее об этом в следующем блоке). Колонка token_validity устанавливает время на 10 минут позже, после которого токен перестаёт быть актуальным.


Схема таблицы, считающая количество попыток авторизации.

В обоих таблицах IP-адрес хранится в обработанном виде, с помощью функции ip2long в поле типа integer.

Теперь мы можем написать немножко PHP-кода. Основной функционал системы возложен на класс User.class.php , который вы можете видеть ниже.

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

User.class.php

Class User{ // Частный ORM случай private $orm; /** * Найти пользователя по токену. Только валидные токены, приняты к рассмотрению. Токен генерируется только на 10 минут с того момента как был создан * @param string $token. Это искомый токен * @return User. Вернуть значение функции User */ public static function findByToken($token){ // найти токен в базе и убедиться, что установлен корректный временной штамп $result = ORM::for_table("reg_users") ->where("token", $token) ->where_raw("token_validity > NOW()") ->find_one(); if(!$result){ return false; } return new User($result); } /** * Авторизовать или зарегистрировать пользователя * @param string $email. Пользовательский email-адрес * @return User */ public static function loginOrRegister($email){ // Если такой пользователь уже существует, вернуть значение функции User от заданного email-адреса хранимого в базе if(User::exists($email)){ return new User($email); } // В противном случае создать нового пользователя в базе и вернуть значение функции User::create от указанного email return User::create($email); } /** * Создать нового пользователя и сохранить в базу * @param string $email. Пользовательский email-адрес * @return User */ private static function create($email){ // Записать нового пользователя и вернуть результат функции User от этих значений $result = ORM::for_table("reg_users")->create(); $result->email = $email; $result->save(); return new User($result); } /** * Проверить, существует ли такой пользователь в базе и вернуть булево значение переменной * @param string $email. Пользовательский email-адрес * @return boolean */ public static function exists($email){ // Существует ли пользователь в базе? $result = ORM::for_table("reg_users") ->where("email", $email) ->count(); return $result == 1; } /** * Создать новый пользовательский объект * @param экземпляр $param ORM , id, email or 0 * @return User */ public function __construct($param = null){ if($param instanceof ORM){ // ORM проверка пройдена $this->orm = $param; } else if(is_string($param)){ // Проверка на email пройдена $this->orm = ORM::for_table("reg_users") ->where("email", $param) ->find_one(); } else{ $id = 0; if(is_numeric($param)){ // идентификатору пользователя передаётся значение переменной $param $id = $param; } else if(isset($_SESSION["loginid"])){ // В противном случае смотри сессию $id = $_SESSION["loginid"]; } $this->orm = ORM::for_table("reg_users") ->where("id", $id) ->find_one(); } } /** * Сгенерировать новый SHA1 токен авторизации, записывает в базу и возвращает его значение * @return string */ public function generateToken(){ // Сгенерировать токен для авторизованного пользователя и сохранить его в базу $token = sha1($this->email.time().rand(0, 1000000)); // Сохранить токен в базе // И пометить его, что он актуален только в течение 10 следующих минут $this->orm->set("token", $token); $this->orm->set_expr("token_validity", "ADDTIME(NOW(),"0:10")"); $this->orm->save(); return $token; } /** * Авторизовать пользователя * @return void */ public function login(){ // Отметить пользователя, как авторизованного $_SESSION["loginid"] = $this->orm->id; // Обновить значение поля базы last_login $this->orm->set_expr("last_login", "NOW()"); $this->orm->save(); } /** * Уничтожить сессию и разлогинить пользователя * @return void */ public function logout(){ $_SESSION = array(); unset($_SESSION); } /** * Проверка, заходил ли пользователь * @return boolean */ public function loggedIn(){ return isset($this->orm->id) && $_SESSION["loginid"] == $this->orm->id; } /** * Проверка является ли пользователь администратором * @return boolean */ public function isAdmin(){ return $this->rank() == "administrator"; } /** * Найти тип пользователя, может быть либо administrator либо regular * @return string */ public function rank(){ if($this->orm->rank == 1){ return "administrator"; } return "regular"; } /** * Метод позволяющий получить приватную информацию пользователя в *качестве свойств объекта User * @param string $key Имя свойства, получающего доступ * @return mixed */ public function __get($key){ if(isset($this->orm->$key)){ return $this->orm->$key; } return null; } }

Токены генерируются с помощью SHA1 алгоритма и сохраняются в базе данных. Я использую функции времени MySQL, дабы задать 10-минутное ограничение актуальности токена.

Когда токен проходит процедуру валидации, мы напрямую говорим обработчику, что мы рассматриваем только токены, у которых ещё не истёк срок годности, хранимый в столбце token_validity.

Обратите внимание, что я использую волшебный метод __get библиотеки docs в конце файла, чтобы перехватить доступ к свойствам объекта User.

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


Защищённая страница

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

functions.php

Function send_email($from, $to, $subject, $message){ // Хелпер, отправляющий email $headers = "MIME-Version: 1.0" . "\r\n"; $headers .= "Content-type: text/plain; charset=utf-8" . "\r\n"; $headers .= "From: ".$from . "\r\n"; return mail($to, $subject, $message, $headers); } function get_page_url(){ // Определить URL PHP-файла $url = "http".(empty($_SERVER["HTTPS"])?"":"s")."://".$_SERVER["SERVER_NAME"]; if(isset($_SERVER["REQUEST_URI"]) && $_SERVER["REQUEST_URI"] != ""){ $url.= $_SERVER["REQUEST_URI"]; } else{ $url.= $_SERVER["PATH_INFO"]; } return $url; } function rate_limit($ip, $limit_hour = 20, $limit_10_min = 10){ // Количество попыток входа за последний час по этому IP-адресу $count_hour = ORM::for_table("reg_login_attempt") ->where("ip", sprintf("%u", ip2long($ip))) ->where_raw("ts > SUBTIME(NOW(),"1:00")") ->count(); // Количество попыток входа за последние 10 минут по этому IP-адресу $count_10_min = ORM::for_table("reg_login_attempt") ->where("ip", sprintf("%u", ip2long($ip))) ->where_raw("ts > SUBTIME(NOW(),"0:10")") ->count(); if($count_hour > $limit_hour || $count_10_min > $limit_10_min){ throw new Exception("Too many login attempts!"); } } function rate_limit_tick($ip, $email){ // Создать новую запись в таблице, считающей количество попыток входа $login_attempt = ORM::for_table("reg_login_attempt")->create(); $login_attempt->email = $email; $login_attempt->ip = sprintf("%u", ip2long($ip)); $login_attempt->save(); } function redirect($url){ header("Location: $url"); exit; }

Функции rate_limit и rate_limit_tick следят за количеством попыток авторизации за истёкший период времени с момента первой попытки. Попытка входа записывается в базе в столбец reg_login_attempt. Эти функции вызываются когда происходит обработка и отправка данных формы как вы можете видеть из следующего фрагмента кода.

Код ниже взят из файла index.php и он обрабатывает отправку формы. Он возвращает JSON-ответ, который, в свою очередь, обрабатывается jQuery в файле assets/js/script.js , который мы уже разбирали ранее.

index.php

Try{ if(!empty($_POST) && isset($_SERVER["HTTP_X_REQUESTED_WITH"])){ // Output a JSON header header("Content-type: application/json"); // Является ли этот email-адрес валидным if(!isset($_POST["email"]) || !filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)){ throw new Exception("Please enter a valid email."); } // Проверка. Позволено ли пользователю авторизоваться, не превысил ли он количество допустимых подключений? (файл functions.php для большей информации) rate_limit($_SERVER["REMOTE_ADDR"]); // Записать эту попытку авторизации rate_limit_tick($_SERVER["REMOTE_ADDR"], $_POST["email"]); // Отправить письмо пользователю $message = ""; $email = $_POST["email"]; $subject = "Your Login Link"; if(!User::exists($email)){ $subject = "Thank You For Registering!"; $message = "Thank you for registering at our site!\n\n"; } // Попытка авторизовать или зарегистрировать пользователя $user = User::loginOrRegister($_POST["email"]); $message.= "You can login from this URL:\n"; $message.= get_page_url()."?tkn=".$user->generateToken()."\n\n"; $message.= "The link is going expire automatically after 10 minutes."; $result = send_email($fromEmail, $_POST["email"], $subject, $message); if(!$result){ throw new Exception("There was an error sending your email. Please try again."); } die(json_encode(array("message" => "Thank you! We\"ve sent a link to your inbox. Check your spam folder as well."))); } } catch(Exception $e){ die(json_encode(array("error"=>1, "message" => $e->getMessage()))); }

После успешной авторизации/регистрации код выше отправит пользователю ссылку для авторизации. Токен становится доступным, т.к. он передаётся в качестве переменной в генерируемой ссылке методом $_GET с маркером tkn

index.php

If(isset($_GET["tkn"])){ // Является ли этот токен валидным для авторизации? $user = User::findByToken($_GET["tkn"]); if($user){ // Да, является. Осуществить редирект на защищённую страницу $user->login(); redirect("protected.php"); } // Нет, токен не валидный. Осуществить редирект, на страницу с формой авторизации/регистрации redirect("index.php"); }

$user->login()

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

Похожим образом устроена и обработка функции на выход из системы.

index.php

If(isset($_GET["logout"])){ $user = new User(); if($user->loggedIn()){ $user->logout(); } redirect("index.php"); }

В конце кода, я снова поставил редирект на index.php, таким образом параметр ?logout=1 передаваемый посредством URL не требуется.

Наш файл index.php требует доп. защиты - мы не хотим, чтобы люди, которые когда-либо однажды авторизовались в системе опять видели форму регистрации. Для этих целей, мы используем метод $user->loggedIn() .

index.php

$user = new User(); if($user->loggedIn()){ redirect("protected.php"); }

Наконец-то, вот кусок кода, позволяющий защитить страницы вашего сайта и сделать её доступной только после авторизации.

protected.php

// Чтобы защитить каждую страницу на вашем сайте подключите к ней файл // main.php и создайте новый объект User. Вот как это просто! require_once "includes/main.php"; $user = new User(); if(!$user->loggedIn()){ redirect("index.php"); }

После этой проверки можете быть уверенными, что пользователь был успешно авторизован. Вы также можете получить доступ к хранимой информации в базе с помощью свойств объекта $user . Для вывода email-а пользователя и его статуса используйте этот код:

Echo "Your email: ".$user->email; echo "Your rank: ".$user->rank();

Метод rank() используется здесь потому что в базе обычно хранятся номера (0 для обычного пользователя, 1 для администратора) и нам нужно преобразовать эти данные в статусы, к которым они относятся, в чём нам и помогает этот метод.

Чтобы сделать из обычного пользователя администратора, просто отредактируйте пользовательскую запись через phpMyAdmin (или любую другую программу, позволяющую управлять базами данных). Статус администратора не даёт каких-либо привилегий, в данном примере на странице будет выведено, что вы администратор - и всё.

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

Мы закончили!

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

Материал подготовил Денис Малышок специально для сайта сайт

P.S. Хотите двигаться дальше в освоении PHP и ООП? Обратите внимание на премиум-уроки по различным аспектам сайтостроения, включая программирование на PHP, а также на бесплатный курс по созданию своей CMS-системы на PHP с нуля с использованием ООП:

Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!


Те, кто более-менее серьёзно изучал PHP знают, что существует один очень полезный глобальный массив в PHP , который называется $_SERVER . И вот хотелось бы в этой статье разобрать самые популярные ключи и их значения в этом массиве, так как их знание просто обязательно даже для начинающего PHP-программиста .

Прежде чем приступить к глобальному массиву $_SERVER в PHP , сразу сделаю небольшую подсказку. Есть замечательная функция, встроенная в PHP , которая называется phpinfo() . Давайте сразу приведу пример её использования:

В результате выполнения этого просто скрипта Вы увидите огромную таблицу с различными настройками интерпритатора PHP , в том числе, ближе к концу будет таблица значений глобального массива $_SERVER . Там будут перечислены все ключи и все соответствующие им значения. Чем это может Вам помочь? А тем, что если Вам потребуется то или иное значение, и Вы забудете, как называется ключ, то с помощью функции phpinfo() Вы можете всегда вспомнить его название. В общем, Вы выполните этот скрипт и сразу меня поймёте.

А теперь давайте перейдём к самым популярным ключам массива $_SERVER :

  • HTTP_USER_AGENT - этот ключ позволяет узнать характеристику клиента. В большинстве случаев, это, безусловно, браузер, однако, не всегда. И опять же, если браузер, то какой, вот в этой переменной об этом можно и узнать.
  • HTTP_REFERER - содержит абсолютный путь к тому файлу (PHP-скрипт , HTML-страница ), с которого перешли на данный скрипт. Грубо говоря, откуда пришёл клиент.
  • SERVER_ADDR - IP-адрес сервера.
  • REMOTE_ADDR - IP-адрес клиента.
  • DOCUMENT_ROOT - физический путь к корневой директории сайта. Это опция задаётся через конфигурационный файл сервера Apache .
  • SCRIPT_FILENAME - физический путь к вызванному скрипту.
  • QUERY_STRING - весьма полезное значение, которое позволяет получить строку с запросом, а дальше можно заниматься парсингом этой строки.
  • REQUEST_URI - ещё более полезное значение, которое содержит не только сам запрос, но и вместе с ним относительный путь к вызываемому скрипту от корня. Это очень часто используется для удаления дублирования с index.php , то есть когда у нас такой URL : "http://mysite.ru/index.php " и "http://mysite.ru/ " ведут на одну страницу, а URLы разные, следовательно, дублирование, что плохо скажется на поисковой оптимизации. И вот с помощью REQUEST_URI мы можем определить: с index.php или нет был вызван скрипт. И можем сделать редирект с index.php (если он присутствовал в REQUEST_URI ) на без index.php . В результате, при передаче такого запроса: "http://mysite.ru/index.php?id=5 ", у нас будет происходить редирект на URL : "http://mysite.ru/?id=5 ". То есть мы избавились от дублирования, удалив из URL этот index.php .
  • SCRIPT_NAME - относительный путь к вызываемому скрипту.

Пожалуй, это все элементы глобального массива $_SERVER в PHP , которые используются регулярно. Их надо знать и уметь использовать, когда это необходимо.

Script языка на Apache сервере.

index.php - самый распространенный стартовый файл для начала работы скриптового языка программирования PHP на WEB сервере, в основном Apache .

Для новичка стоит только знать, что большинство сайтов (sites) свою работу начинают именно с этого файла (file). Размещается файл в корне основного хранилища файлов сайта. Также может располагаться внутри директорий (directory ). Тогда Apache сервер будет искать и грузить в первую очередь index.php в запрашиваемой директории, если не прописано что-либо иное в файле настроек сервера htaccess

Как заставить Apache первым загружать index.php (options)

Как правильно делать:

# чтобы работа начиналась только с index.php DirectoryIndex index.php

Многие делают еще так:

# начинать запуск скриптов с проверки указанной последовательности DirectoryIndex index.php index.html index.shtml

Тут стоит немного объяснить алгоритм действий:

  • Пытаемся загрузить index.php .
  • Если index.php directory ), то пытаемся загрузить index.html .
  • Если index.html не обнаружен в корневом директории (directory ), то пытаемся загрузить index.s html
  • Есть много других опций (options) для управления загрузками, но это надо читать более расширенную спецификацию htaccess .

    Используют такой метод, как правило , в том случае, если хотят защитить index.php от сканирования на предмет различных уязвимостей, например:

    Index.php?mode=page&url="%20AND1="1

    Если index.php будет другой, то все, что идет после index.php, не имеет никакого значения. Но существуют другие способы определить, какой именно файл является главным загрузочным на WEB сервере. Так что особой безопасности от этого метода не получится. А вот неудобства использования возрастают. Поэтому к этому методу стоит относиться с особой осторожностью и продуманностью.

    Как скрыть index.php через htaccess

    На WEB сервере Apache должен быть включен модуль mod_rewrite . На сегодняшний день данный модуль включен практически у каждого , но в любом случае в этом нужно убедиться, иначе не получиться работать. Данный метод в основном используют SEO оптимизаторы.

    Код для скрытия index.php

    # включаем модуль mod_rewrite RewriteEngine on # все что после index.php отправлялось на главную командную строку RewriteCond %{THE_REQUEST} ^{3,9}\ /index\.php\ HTTP/ # убираем index.php из командной строки, чтобы оставалась только полезная информация RewriteRule ^index\.php$ http://сайт/

    Как все запросы отправлять через index.php (redirect)

    Тут тоже в общем-то ничего сложного

    # Включаем работу mod_rewrite RewriteEngine on # если запрос не является файлом, то он будет отправляться на index.php # это делается, если ваш файл index.php обрабатывает не все, # что требуется, так как часто требуется разделить проект на более мелкие модули RewriteCond %{REQUEST_FILENAME} !-f # если запрос не является директорией, то он будет отправляться на index.php # это делается, чтобы директории типа /images/ не переадресовывались на index.php RewriteCond %{REQUEST_FILENAME} !-d # все остальное отправлять на index.php и там отрабатывать RewriteRule (.*) index.php [L]

    Как скачать index.php

    При соблюдении WEB мастером правил безопасности скачать index.php нельзя по определению.

    Можно скачать index.php только в случаях, если:

    • Неправильно прописаны настройки в (.htaccess) . Здесь виноват только сам WEB мастер.
    • Имеется доступ к серверу через FTP протокол . Самый распространенный способ. Жертва заманивается на заранее сделанный сайт так как надо под различными предлогами (Вы выиграли миллион, ваш аккаунт заблокирован в социальных сетях и так далее). Как только вы попадаете на необходимый сайт, у вас копируют cookies. После этого пытаются их использовать для входа уже на ваш сайт. Избежать этого легко, достаточно не пользоваться WEB интерфейсами для управления своим сайтом и не устанавливать на свой компьютер каких-либо программ от неизвестных вам авторов с неизвестных источников, например: всякие ускорители интернета или сомнительные toolbar для с сомнительной эффективностью, но очень сильной настойчивостью его установить.
    • На сервере установлен дополнительный скрипт (back door ). Через такой скрипт можно получить любой файл с сервера, при этом чтобы он не был выполнен обработчиком PHP. Такие файлы можно загрузить на сервер через распространенные и известные уязвимости систем управления контентом (CMS). Например: WordPress, Joomla, DLE, Drupal, Codeigniter и так далее. Далее, под видом аватара загружается специально сформированный .gif файл, который в последствии можно вызвать, зная его расположение на сервере жертвы. Если CMS неправильно обрабатывает такие файлы, то через него можно получить необходимые доступы к сайту. Либо на CMS, типа WordPress, Joomla и другие устанавливается широко разрекламированный како-либо plugin (например: php search index ), который вроде бы и выполняет свою функцию, но при этом сливает данные о сайте своим разработчикам, тем самым позволяя им использовать сайт так, как им захочется.

    Вот как-то так. Если у вас есть вопросы, задавайте их в комментариях, и в скором времени разъяснительный ответ появиться в этой статье.

    Сегoдня мы рассмотрим эксплуатацию критической 1day-уязвимости в популярной CMS Joomla, которая прогремела на просторах интернета в конце октября. Речь пойдет об уязвимостях с номерами CVE-2016-8869 , CVE-2016-8870 и CVE-2016-9081 . Все три происходят из одного кусочка кода, который пять долгих лет томился в недрах фреймворка в ожидании своего часа, чтобы затем вырваться на свободу и принести с собой хаос, взломанные сайты и слезы ни в чем не повинных пользователей этой Joomla. Лишь самые доблестные и смелые разработчики, чьи глаза красны от света мониторов, а клавиатуры завалены хлебными крошками, смогли бросить вызов разбушевавшейся нечисти и возложить ее голову на алтарь фиксов.

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

    6 октября 2016 года Дэмис Пальма (Demis Palma) создал топик на Stack Exchange , в котором поинтересовался: а почему, собственно, в Joomla версии 3.6 существуют два метода регистрации пользователей с одинаковым названием register() ? Первый находится в контроллере UsersControllerRegistration , а второй - в UsersControllerUser . Дэмис хотел узнать, используется ли где-то метод UsersControllerUser::register() , или это лишь эволюционный анахронизм, оставшийся от старой логики. Его беспокоил тот факт, что, даже если этот метод не используется никаким представлением, он может быть вызван при помощи сформированного запроса. На что получил ответ от девелопера под ником itoctopus, подтвердившего: проблема действительно существует. И направил отчет разработчикам Joomla.

    Далее события развивались самым стремительным образом. 18 октября разработчики Joomla принимают репорт Дэмиса, который к тому времени набросал PoC, позволяющий регистрировать пользователя. Он опубликовал заметку на своем сайте , где в общих чертах рассказал о найденной проблеме и мыслях по этому поводу. В этот же день выходит новая версия Joomla 3.6.3, которая все еще содержит уязвимый код.

    После этого Давиде Тампеллини (Davide Tampellini) раскручивает баг до состояния регистрации не простого пользователя, а администратора. И уже 21 октября команде безопасности Joomla прилетает новый кейс. В нем речь уже идет о повышении привилегий . В этот же день на сайте Joomla появляется анонс о том, что во вторник, 25 октября, будет выпущена очередная версия с порядковым номером 3.6.3, которая исправляет критическую уязвимость в ядре системы.

    25 октября Joomla Security Strike Team находит последнюю проблему, которую создает обнаруженный Дэмисом кусок кода. Затем в главную ветку официального репозитория Joomla пушится коммит от 21 октября с неприметным названием Prepare 3.6.4 Stable Release , который фиксит злосчастный баг.

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

    27 октября исследователь Гарри Робертс (Harry Roberts) выкладывает в репозиторий Xiphos Research готовый эксплоит , который может загружать PHP-файл на сервер с уязвимой CMS.

    Детали

    Что ж, с предысторией покончено, переходим к самому интересному - разбору уязвимости. В качестве подопытной версии я установил Joomla 3.6.3, поэтому все номера строк будут актуальны именно для этой версии. А все пути до файлов, которые ты увидишь далее, будут указываться относительно корня установленной CMS.

    Благодаря находке Дэмиса Пальмы мы знаем, что есть два метода, которые выполняют регистрацию пользователя в системе. Первый используется CMS и находится в файле /components/com_users/controllers/registration.php:108 . Второй (тот, что нам и нужно будет вызвать), обитает в /components/com_users/controllers/user.php:293 . Посмотрим на него поближе.

    286: /** 287: * Method to register a user. 288: * 289: * @return boolean 290: * 291: * @since 1.6 292: */ 293: public function register() 294: { 295: JSession::checkToken("post") or jexit(JText::_("JINVALID_TOKEN")); ... 300: // Get the form data. 301: $data = $this->input->post->get("user", array(), "array"); ... 315: $return = $model->validate($form, $data); 316: 317: // Check for errors. 318: if ($return === false) 319: { ... 345: // Finish the registration. 346: $return = $model->register($data);

    Здесь я оставил только интересные строки. Полную версию уязвимого метода можно посмотреть в репозитории Joomla.

    Разберемся, что происходит при обычной регистрации пользователя: какие данные отправляются и как они обрабатываются. Если регистрация пользователей включена в настройках, то форму можно найти по адресу http://joomla.local/index.php/component/users/?view=registration .


    Легитимный запрос на регистрацию пользователя выглядит как на следующем скриншоте.


    За работу с пользователями отвечает компонент com_users . Обрати внимание на параметр task в запросе. Он имеет формат $controller.$method . Посмотрим на структуру файлов.

    Имена скриптов в папке controllers соответствуют названиям вызываемых контроллеров. Так как в нашем запросе сейчас $controller = "registration" , то вызовется файл registration.php и его метод register() .

    Внимание, вопрос: как передать обработку регистрации в уязвимое место в коде? Ты наверняка уже догадался. Имена уязвимого и настоящего методов совпадают (register), поэтому нам достаточно поменять название вызываемого контроллера. А где у нас находится уязвимый контроллер? Правильно, в файле user.php . Получается $controller = "user" . Собираем все вместе и получаем task = user.register . Теперь запрос на регистрацию обрабатывается нужным нам методом.


    Второе, что нам нужно сделать, - это отправить данные в правильном формате. Тут все просто. Легитимный register() ждет от нас массив под названием jform , в котором мы передаем данные для регистрации - имя, логин, пароль, почту (см. скриншот с запросом).

    • /components/com_users/controllers/registration.php: 124: // Get the user data. 125: $requestData = $this->input->post->get("jform", array(), "array");

    Наш подопечный получает эти данные из массива с именем user .

    • /components/com_users/controllers/user.php: 301: // Get the form data. 302: $data = $this->input->post->get("user", array(), "array");

    Поэтому меняем в запросе имена всех параметров с jfrom на user .

    Третий наш шаг - это нахождение валидного токена CSRF, так как без него никакой регистрации не будет.

    • /components/com_users/controllers/user.php: 296: JSession::checkToken("post") or jexit(JText::_("JINVALID_TOKEN"));

    Он выглядит как хеш MD5, а взять его можно, например, из формы авторизации на сайте /index.php/component/users/?view=login .


    Теперь можно создавать пользователей через нужный метод. Если все получилось, то поздравляю - ты только что проэксплуатировал уязвимость CVE-2016-8870 «отсутствующая проверка разрешений на регистрацию новых пользователей».

    Вот как она выглядит в «рабочем» методе register() из контроллера UsersControllerRegistration:

    • /components/com_users/controllers/registration.php: 113: // If registration is disabled - Redirect to login page. 114: if (JComponentHelper::getParams("com_users")->get("allowUserRegistration") == 0) 115: { 116: $this->setRedirect(JRoute::_("index.php?option=com_users&view=login", false)); 117: 118: return false; 119: }

    А так в уязвимом:

    • /components/com_users/controllers/user.php:

    Ага, никак.

    Чтобы понять вторую, гораздо более серьезную проблему, отправим сформированный нами запрос и проследим, как он выполняется на различных участках кода. Вот кусок, который отвечает за проверку отправленных пользователем данных в рабочем методе:

    Продолжение доступно только участникам Вариант 1. Присоединись к сообществу «сайт», чтобы читать все материалы на сайте

    Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score!

    На прошлом уроке мы разобрались из каких блоков будет состоять шаблон trip , поэтому можно приступать к работе. Для начала создадим две папки:

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

    ВНИМАНИЕ: В папке images шаблона не размещается графика контента!

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

    Далее можно приступать к созданию самого главного файла index.php , который будет определять визуальное расположение элементов сайта и сообщать CMS Joomla в какой блок поместить различные компоненты и модули. Файл является комбинацией PHP и HTML.

    Я всегда при написании кода использую только Macromedia Dreamweaver . Отличная программа, настоятельно советую ее новичкам, т.к. если в процессе работы над кодом вы сделали ошибку, программа обязательно подсветит ваш косяк.

    На сайте вы найдете самоучитель по Macromedia Dreamweaver . Если вы собираетесь заниматься разработкой сайтов, то программку эту стоит освоить, хотя бы на начальном уровне, чтобы редактировать коды шаблонов без ошибок.

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

    index.php

    Заголовок файла

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

    < ?php
    defined ("_JEXEC" ) or die ;
    JHtml::_("behavior.framework" , true ) ;
    $app = JFactory::getApplication() ;
    ?>
    < ?php echo "< ?" ; ?> xml version= "1.0" encoding= "< ?php echo $this - > _charset ?> " ?>

    DOCTYPE – это очень важный параметр, на основании которого браузер решает, как ему отображать эту страницу и как интерпретировать CSS.

    < ! DOCTYPE html PUBLIC "- / / W3C/ / DTD XHTML 1.0 Strict/ / EN" "http:/ / www.w3.org/ TR/ xhtml1/ DTD/ xhtml1- strict.dtd" >

    Следующий фрагмент извлекает установленный язык из глобальной конфигурации.

    < html xmlns= "http:/ / www.w3.org/ 1999/ xhtml" xml:lang= "< ?php echo $this - > language; ?> " lang= "< ?php echo $this - > language; ?> " dir = "< ?php echo $this - > direction; ?> " >

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

    < head>
    < jdoc:include type= "head" / >

    Следующие строки в заголовке содержат ссылки на основные CSS стили Joomla.

    < link rel= "stylesheet" href= "< ?php echo $this - > baseurl ?> / templates/ system / css/ system .css" type= "text/ css" / >
    < link rel= "stylesheet" href= "< ?php echo $this - > baseurl ?> / templates/ system / css/ general.css" type= "text/ css" / >

    Чтобы задействовать стили оформления шаблона, делаем ссылку на файл, содержащий каскадные таблицы стилей template.css, который лежит в папке CSS . Не важно, что этот файл пока пустой, главное его подключить, оформлением займемся потом, когда инсталлируем шаблон на Joomla. Так будет проще наблюдать за результатом.

    < link rel= "stylesheet" href= "< ?php echo $this - > baseurl ?> / templates/ < ?php echo $this - > template ?> / css/ template.css" type= "text/ css" / >

    Следующий фрагмент кода позволяет нам сворачивать левую или правую колонки, если в позициях «left» и « right» не расположено ни одного модуля. В случае если свернуты обе колонки, то контент занимает 100% ширины страницы. Если включена только одна колонка, то контент занимает 80%. При двух включенных колонках на контент приходится 60% ширины страницы.

    < ?php
    if ($this - > countModules("left and right" ) = = 0) $contentwidth = "100" ;
    if ($this - > countModules("left or right" ) = = 1) $contentwidth = "80" ;
    if ($this - > countModules("left and right" ) = = 1) $contentwidth = "60" ;
    ?>

    Заголовок закрывается

    < / head>

    < body>

    Блок «page» содержит оформление только страницы сайта, именно она и будет шириной 950рх.

    < div id= "page" >

    Блок "top" находится в самом верху страницы и содержит в себе два блока "logo " и "user1".

    < div id= "top" >

    В боке «logo» мы разместим графический файл логотипа, это будет прописано в таблицах стилей. А вот автоматический вывод названия сайта прописываем в файле index.php, причем название помещаем в тег H1, что очень важно для поисковой оптимизации.

    < div id= "logo" >
    < h1> < ?php echo $app - > getCfg("sitename" ) ; ?> < / h1>
    < / div>

    Определим позицию «user1» в одноименном блоке для вывода модуля поиска по сайту.

    < div id= "user1" >
    < jdoc:include type= "modules" name= "user1" style= "xhtml" / >
    < / div>
    < / div> < ! - - конец блока top - - >

    Вывод модуля горизонтального меню в блоке «user2» в позиции «user2» . Блок будет сворачиваться, если в этой позиции не будет модуля.

    < ?php if ($this - > countModules("user2" ) ) : ?>
    < div id= "user2 " >
    < jdoc:include type= "modules" name= "user2" style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    Дальше идет блок шапки сайта «header» . В нем мы определим позицию «header» для вывода модулей. Блок будет сворачиваться, если в этой позиции не будет модуля. Я намеренно расширила возможности этого блока, чтобы иметь возможность разместить в нем не только картинку шапки, но и ротаторы изображений.

    < ?php if ($this - > countModules("header " ) ) : ?>
    < div id= "header " >
    < jdoc:include type= "modules" name= "header " style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    В блоке «user3» определим позицию «user3» для вывода модулей.

    Блок будет сворачиваться, если в этой позиции «user3» не будет выводится модуль.

    < ?php if ($this - > countModules("user3" ) ) : ?>
    < div id= "user3" >
    < jdoc:include type= "modules" name= "user3" style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    Открывается блок левой колонки, которая будет сворачиваться, если в позиции «left» не будет ни одного модуля.

    < ?php if ($this - > countModules("left" ) ) : ?>
    < div id= "left" >
    < jdoc:include type= "modules" name= "left" style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    Открывается самый важный блок контента, который может занимать 100% ширины страницы, 80% и 60%, в зависимости от количества включенных колонок.

    < div id= "content< ?php echo $contentwidth ; ?> " >

    Вывод сообщений в компонентах

    < jdoc:include type= "message" / >

    Вывод содержимого контента.

    < jdoc:include type= "component" style= "xhtml" / >
    < / div> < ! - - конец блока контента- - >

    Открывается блок правой колонки, которая будет сворачиваться, если в позиции «rigth» не будет ни одного модуля.

    < ?php if ($this - > countModules("right" ) ) : ?>
    < div id= "rigth" >
    < jdoc:include type= "modules" name= "right" style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    Вывод блока «footer» , предназначенного для вывода модуля «HTML код» с информацией об авторских правах. Можно также разместить здесь нижнее горизонтальное меню или модуль представления контента. Блок будет сворачиваться, если в этой позиции «footer» не будет выводится не один модуль

    < ?php if ($this - > countModules("footer" ) ) : ?>
    < div id= "footer" >
    < jdoc:include type= "modules" name= "footer" style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    Закрываются блок страницы сайта «page», body и весь код.

    < / div> < ! - - конец блока page- - >
    < / body> < ! - - конец блока body - - >
    < / html> < ! - - конец кода- - >

    Мы создали полноценный файл index.php . Теперь вы знаете, при помощи каких команд, и в какой последовательности выводятся блоки шаблона.

    ВНИМАНИЕ: Для того, чтобы код шаблона читался из админпанели joomla, то файл index.php необходимо открыть в редакторе AkelPad и сохранить в кодировке UTF-8, при этом снять галочку BOM. Если вы использовали для работы с файлом программу Macromedia Dreamweaver , то необходимо в вернем меню выбрать пункт "Изменить" > "Свойства страницы" и выбрать кодировку документа Юникод (utf-8), при этом снять галочку "включить сигнатуры юникода (ВОМ)". Однако настоятельно не советую вам редактировать код из админки Joomla, если, что-то накосячите - обратной дороги нет, в отличии от программы Macromedia Dreamweaver , где всегда можно отменить сделанные изменения.

    Само оформление блоков будет описано в template.css. Но настраивать таблицы стилей мы будем после инсталляции шаблона на Joomla 3 (joomla 2.5), а для этого необходимо создать