8 Апрель 2011

Видеокурс по PHP. Часть 14

Приветствую вас, уважаемый читатель, в свежайшем видеоуроке по PHP-фреймворку Kohana.

Основная тема сегодняшнего видеоурока восстановление забытого пользователем пароля.

Благодаря решению основной задачи мы затронем вопрос организации обычных SQL запросов в Kohana и поговорим о том, что такое SQL-инъекции.

Задача восстановления пароля решается следующим образом:

  1. Пользователь переходит по заданному URL на определенный экшен контроллера авторизации.

  2. Данный экшен по умолчанию генерирует страницу с отображением поля ввода адреса эл. почты.

  3. Для восстановления пароля пользователь должен ввести на загрузившейся странице свой e-mail, с которым он ранее регистрировался на сайте.

  4. Введенный пользователем e-mail будет проверен на присутствие в таблице users нашей базы данных.

  5. В случае обнаружения адреса эл. почты в таблице базы данных, будет сгенирирована случайная последовательность символов для составления URL подтверждения запроса на восстановления пароля.

  6. Сгенирированная случайным образом последовательность записывается в дополнительное поле таблицы users, в ту строку, в которой обнаружен введенный e-mail. Основанная на случайной последовательности ссылка отправляется пользователю на эл. почту.

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

  8. Информацию о новом пароле отправить на эл. почту пользователя.

Вот каким образом будет выглядеть код контроллера авторизации (www\application\classes\controller\auth.php), выполняющий 1 — 6 пункты алгоритма:

public function action_hochuvspomnit()
	{
            $data = array();

            if(isset($_POST['btnsubmit']))
            {

                $email = Arr::get($_POST, 'email', '');

                $register = new Model_Register();

                if($register->hochuNoviyParol($email))
                {
                    $data["ok"] = "";
                }
                else
                {
                    $data["error"] = "";
                }
            }
            $this->template->content =  View::factory('rempassview', $data);
        }

В модели Register необходимо описать новый метод hochuNoviyParol, который на вход будет получать адрес эл. почты, введенный нашим забывчивым пользователем на странице внешнего вида и выполнять действия 1 — 6 вышеописанного алгоритма:

public function hochuNoviyParol($email)
	{
            $usertemp = ORM::factory('myuser', array('username'=>$email));

            if(!$usertemp->loaded())
            {
                return FALSE;
            }

            $useful = new Model_Useful();
            $genpass = $useful->generatePassword(18);

            $usertemp->rempass = $genpass;
            $usertemp->save();

            //Отправка эл. почты
            $from = 'obrsistema1@mail.ru';
            $subject = 'Восстановление пароля';
            $message = "Перейдите по ссылке <a href='http://kohana/auth/checkcode/$genpass'>http://kohana1/auth/checkcode/$genpass</a>";
            $useful->sendemail($email, $from, $subject, $message, TRUE);

            return TRUE;
        }

Однако, при попытке сохранить сгенерированный код в поле rempass мы столкнемся с неожиданной ошибкой в валидации. Это проблема, незамеченная мною ранее.

Суть проблемы в следующем: когда мы пытаемся сохранить данные в таблицу users, модель myuser запускает придуманную нами валидацию.Валидация не дает осуществить сохранение, т. к. валидационная функция username_unique (см. файл модели myuser.php) находит в таблице users тот же самый логин, который мы пытаемся сохранить вновь.

Решается данная проблема переписыванием валидационной функции username_unique:

public function username_unique($username)
	{
            $db = Database::instance();

            if ($this->id)
            {
                $query =
                    'SELECT id
                    FROM users
                    WHERE id != '.$this->id.' AND username = '.$db->escape($username);
            }
            else
            {
                $query =
                    'SELECT id
                    FROM users
                    WHERE username = '.$db->escape($username);
            }

            $result = $db->query(Database::SELECT, $query, FALSE)->as_array();
            if (count($result) > 0)
            {
                    return FALSE;
            }
            else
            {
                    return TRUE;
            }
	}

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

В случае, если вы хотите организовать SQL-запрос типа SELECT — вам нужно воспользоваться приведенным мной синтаксисом.

Хочу пояснить назначение функции escape($username).

Escape позволяет нам защититься от SQL-инъекций. Поскольку значение переменной $username мы получаем от посетителя сайта, в нее запросто может попасть хакерский код, который вызовет выполнение нашего запроса и дополнительного враждебного действия.

Подробнее об SQL-инъекциях вы можете прочитать на википедии.

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

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

Осталось разобраться с 7 и 8 пунктом алгоритма.

Но здесь все просто.

Пользователю была отправлена ссылка вида kohana/kontroller/action/id, где в качестве id применяется сгенерированный код.

В нашем случае ссылка вот такая kohana/auth/checkcode/$genpass

Вот так выглядит экшен checkcode:

public function action_checkcode($code)
         {
            $data = array();

            $register = new Model_Register();

            if($register->obnovlenieparolia($code))
            {
                $data["ok"] = "";
            }
            else
            {
                $data["error"] = "";
            }
             $this->template->content =  View::factory('checkcodeview', $data);
         }

Как вы видите, в качестве параметра данного экшена передается значение переменной $code. Kohana устроена таким образом, что в параметр экшена передается то, что указано в URL после названия данного экшена. Иными словами, в $code попадет значение $genpass.

Вот как выглядит метод obnovlenieparolia модели regiser:

public function obnovlenieparolia($code)
	{
            $usertemp = ORM::factory('myuser', array('rempass'=>$code));

            if(!$usertemp->loaded())
            {
               return FALSE;
            }

            $useful = new Model_Useful();
            $genpass = $useful->generatePassword(8);

             //Хеширование пароля
             $auth = Auth::instance();
             $usertemp->password = $auth->hash_password($genpass);

             //Очистка кода восстановления
             $usertemp->rempass = NULL;

             $usertemp->save();

             //Отправка эл. почты
            $email = $usertemp->username;

            $from = 'obrsistema1@mail.ru';
            $subject = 'Авторизационные данные обновлены';
            $message = "Ваш логин: $email Ваш пароль: $genpass";
            $useful->sendemail($email, $from, $subject, $message, FALSE);

            return TRUE;

        }

А теперь видеоурок, поясняющий все вышеописанное

Скачать видеоурок (72.1 МБ, *.wmv).

Скачать исходники (1.14 МБ, *.zip).

С уважением, Андрей Морковин.

<< Предыдущая публикация курса    Следующая публикация курса >>