Безопасные значения глобальных переменных

 

Всем доброго времени суток. Продолжаем собирать наше ядро для RS-MINI. Сегодня нам предстоит написать еще один класс, который позволит получать безопасные данные из глобальных массивов. Не секрет, что вся работа веб приложения, это получение данных, и на основе этих данных у базы запрашивается какая-то информация. Учитывая то, что база данных у нас на файлах, то беспокоится по поводу sql инъекций не приходится, тем не менее, не очень то хотелось, что бы в поле комментариев кто-то смог записать злобный яваскрипт.

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

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

  • $_SESSION
  • $_COOKIE
  • $_POST
  • $_GET
  • $_REQUEST
  • $_FILES
  • $_SERVER

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

  • обработать как целое число
  • обработать как строку
  • обработать как дробное число
  • обработать как урл — этот метод необходим не для повсеместного использования, а для получение чистого пути без параметров и без домена. Метод будет использоваться только для получение данных из элемента REQUEST_URI массива $_SERVER. Если нужно будет обработать как урл присланное значение с формы, то использовать следует метод «обработать как строку».

Ну и на по следок остается реализовать еще два метода для тех случаев когда:

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

Пишем класс request.class.php

Базовая структура класса

Начнем с структуры класса. Вот код методов без реализации логики (файл называется request.class.php и находится в папке /rs-mini/core/)

<?php

/*
 * @package RS-MINI
 * @copyright (c) 2015 Alexey Glumov aka Rio-Shaman (support@rio-shaman.ru)
 * @license GNU General Public License version 2; see LICENSE.txt
 *
 */
 

namespace core;


if(!defined('RS-MINI')) die();


/*
 * request v 1.0
 *
 * Класс получает данные из глобальных массивов и обрабатывает их
 *
 */

class request
{   
    
    /*
     * хранит значение полученное методами getHttpPost(), getHttpGet(), getHttpRequest(), getHttpFiles(), getHttpCookie()
     *
     * @var    - string
     * @access - private
     */
    
    private $httpData = NULL;
    
    /*
     * получаем данные из глобавльного массива $_SESSION
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
     
    public function getHttpSession($name)
    {

    }
    
    /*
     * получаем данные из глобавльного массива $_COOKIE
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
     
    public function getHttpCookie($name)
    {

    }
    
    /*
     * получаем данные из глобавльного массива $_POST
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
     
    public function getHttpPost($name)
    {

    }
    
    /*
     * получаем данные из глобавльного массива $_GET
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
    
    public function getHttpGet($name)
    {

    }
    
    /*
     * получаем данные из глобавльного массива $_REQUEST
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
    
    public function getHttpRequest($name)
    {

    }
    
    /*
     * получаем данные из глобавльного массива $_FILES
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
    
    public function getHttpFiles($name)
    {

    }
    
    /*
     * получаем данные из глобавльного массива $_SERVER. В основном метод написан для получение HTTP_HOST и REQUEST_URI ключей
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
    
    public function getHttpServer($name)
    {

    }
    
    /*
     * обрабатывает данные в свойстве $this->httpData как стороку
     *
     * @access - public
     * @return - обработанные данные или null если ничего не найдено
     *
     */
    
    public function toString()
    {

    }

    /*
     * обрабатывает данные в свойстве $this->httpData как целое число
     *
     * @access - public
     * @return - обработанные данные или null если ничего не найдено
     *
     */
    
    public function toInteger()
    {
        
    }
    
    /*
     * обрабатывает данные в свойстве $this->httpData как число с плавующей точкой
     *
     * @access - public
     * @return - обработанные данные или null если ничего не найдено
     *
     */
    
    public function toFloat()
    {
        
    }

    /*
     * обрабатывает данные в свойстве $this->httpData как ссылку.
     * использовать метод только для значения глобального массива $_SERVER['REQUEST_URI']
     * получает строку БЕЗ параметров
     *
     * @access - public
     * @return - обработанные данные или null если ничего не найдено
     *
     */
    
    public function toUri()
    {

    }
    
    /*
     * метод принудительно вставляет значение в свойство httpData. После чего его можно обработать методами преобразования
     *
     * @access - public
     * @return - сам себя
     *
     * @param ??? data - присланные данные
     */
    
    public function setData($data)
    {

    }
    
    /*
     * в отличие от остальных методов, просто возвращает полученные данные
     *
     * @access - public
     * @return - данные или null если ничего не найдено
     *
     */
    
    public function getData()
    {

    }
}

Давайте покажу на примере как должен работать наш обработчик. Предположим у нас есть вот такая переменная — $_GET['name'], с значением — Алексей. Это имя, стало быть обработать эту переменную нужно будет с помощью «обработать как строку». Вот код получение безопасного значения:

// предположим что объект request поднят в переменной request
$name = $request->getHttpGet('name')->toString();

Как видите это довольно удобно Улыбаюсь

Методы запроса данных

Давайте реализуем первые семь методов по запросу данных из глобальных массивов. Суть их одна, посмотреть есть ли запрашиваемый элемент запрашиваемого массива, и если есть, то записать его значение в свойство httpData. Если нет то в свойство запишется NULL. Методы возвращают самого себя (а именно эту копию объекта)

Я приведу листинг этих семи методов

    /*
     * получаем данные из глобавльного массива $_SESSION
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
     
    public function getHttpSession($name)
    {
        $this->httpData = (isset($_SESSION[$name])) ? $_SESSION[$name] : NULL;
        return $this;
    }
    
    /*
     * получаем данные из глобавльного массива $_COOKIE
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
     
    public function getHttpCookie($name)
    {
        $this->httpData = (isset($_COOKIE[$name])) ? $_COOKIE[$name] : NULL;
        return $this;
    }
    
    /*
     * получаем данные из глобавльного массива $_POST
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
     
    public function getHttpPost($name)
    {
        $this->httpData = (isset($_POST[$name])) ? $_POST[$name] : NULL;
        return $this;
    }
    
    /*
     * получаем данные из глобавльного массива $_GET
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
    
    public function getHttpGet($name)
    {
        $this->httpData = (isset($_GET[$name])) ? $_GET[$name] : NULL;
        return $this;
    }
    
    /*
     * получаем данные из глобавльного массива $_REQUEST
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
    
    public function getHttpRequest($name)
    {
        $this->httpData = (isset($_REQUEST[$name])) ? $_REQUEST[$name] : NULL;
        return $this;
    }
    
    /*
     * получаем данные из глобавльного массива $_FILES
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
    
    public function getHttpFiles($name)
    {
        $this->httpData = (isset($_FILES[$name])) ? $_FILES[$name] : NULL;
        return $this;
    }
    
    /*
     * получаем данные из глобавльного массива $_SERVER. В основном метод написан для получение HTTP_HOST и REQUEST_URI ключей
     *
     * @access - public
     * @return - сам себя
     *
     * @param string name - ключ элемента массива
     */
    
    public function getHttpServer($name)
    {
        $this->httpData = (isset($_SERVER[$name])) ? $_SERVER[$name] : NULL;
        return $this;
    }

Методы обработки данных

Методы по обработки

  • как строка
  • как целое число
  • и как дробное число

очень простые, поэтому я выведу их в одном листинге

    /*
     * обрабатывает данные в свойстве $this->httpData как стороку
     *
     * @access - public
     * @return - обработанные данные или null если ничего не найдено
     *
     */
    
    public function toString()
    {
        return htmlspecialchars($this->httpData);
    }

    /*
     * обрабатывает данные в свойстве $this->httpData как целое число
     *
     * @access - public
     * @return - обработанные данные или null если ничего не найдено
     *
     */
    
    public function toInteger()
    {
        return (int)$this->httpData;
    }
    
    /*
     * обрабатывает данные в свойстве $this->httpData как число с плавующей точкой
     *
     * @access - public
     * @return - обработанные данные или null если ничего не найдено
     *
     */
    
    public function toFloat()
    {
        return (float)$this->httpData;
    }

Тут вопросов возникнуть не должно. А вот метод по по обработке урла чуть сложнее. Вот его код:

    /*
     * обрабатывает данные в свойстве $this->httpData как ссылку.
     * использовать метод только для значения глобального массива $_SERVER['REQUEST_URI']
     * получает строку БЕЗ параметров
     *
     * @access - public
     * @return - обработанные данные или null если ничего не найдено
     *
     */
    
    public function toUri()
    {
        // пропускаем строку через декодер урлов. чистим от пробелов и приводим у нижнему регистру
        $this->httpData  = urldecode(trim(mb_strtolower($this->httpData)));
        // бьем урл на массив по знаку вопроса
        $uri             = explode('?', $this->httpData);
        // сохраняем все что до знака вопроса
        $this->httpData  = $uri[0];
        
        // если урл равен слешу (главная страница)
        if ($this->httpData == '/')
            // возвращаем его
            return $this->httpData;
                
        // пробиваем по регулярному выражению
        if (!preg_match("/^[-_\.a-z0-9\/]+[\/]{1}$/s", $this->httpData))
            return NULL;
            
        return $this->httpData;
    }

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

Вторая и третья строчка избавляет урл от параметров передаваемые через адресную строку.

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

Если в свойстве не слеш то сверяем значение с регулярным выражением. Наши урлы должны состоять только из

  • букв и цифр
  • слешей, тире и нижнего подчеркивание

и урл обязательно должен заканчиваться на слеш

Методы на другие случаи

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

    /*
     * метод принудительно вставляет значение в свойство httpData. После чего его можно обработать методами преобразования
     *
     * @access - public
     * @return - сам себя
     *
     * @param ??? data - присланные данные
     */
    
    public function setData($data)
    {
        $this->httpData = $data;
        
        return $this;
    }
    
    /*
     * в отличие от остальных методов, просто возвращает полученные данные
     *
     * @access - public
     * @return - данные или null если ничего не найдено
     *
     */
    
    public function getData()
    {
        return $this->httpData;
    }

Заключение

Нам осталось создать объект свеже написанного класса и написать небольшой тест. Объект будет создаваться в нашем приложении. Вот новый код файла application.class.php:

<?php

/*
 * @package RS-MINI
 * @copyright (c) 2015 Alexey Glumov aka Rio-Shaman (support@rio-shaman.ru)
 * @license GNU General Public License version 2; see LICENSE.txt
 *
 */

namespace core;

if(!defined('RS-MINI')) die();

/*
 * application v 1.0
 *
 * Класс стартует систему
 *
 */

class application
{

    /*
     * объект дома
     *
     * @var         - object
     * @access      - private
     */
    
    private static $dom;
    
    /*
     * объект с данными глобального массива
     *
     * @var         - object
     * @access      - private
     */
    
    private static $request;

    /*
     * главный метод. запуск системы
     *
     * @access  - public
     *
     */
     
    public static function run()
    {
        // поднимаем некоторые классы ядра
        // объект по работе с глобальными переменнами
        self::$request    = new \core\request();
        // объект прослойка между контроллерами и вьюшкой
        self::$dom        = new \core\dom();
    }
}

А теперь в методе run() нашего приложения, напишем небольшой тест. Там ничего сложного нет, вот код метода:

    /*
     * главный метод. запуск системы
     *
     * @access  - public
     *
     */
     
    public static function run()
    {
        // поднимаем некоторые классы ядра
        // объект по работе с глобальными переменнами
        self::$request    = new \core\request();
        // объект прослойка между контроллерами и вьюшкой
        self::$dom        = new \core\dom();
        
        // тест класса по работе с глобальными переменнами.
        // переходим вот по такому урлу http://mini.test.ru/?name=<p>Алексей</p>
        ?><xmp><?
        var_dump(self::$request->getHttpGet('name')->toString());
        ?></xmp><?
    }

Как видите я заранее послал данные с html вставками, и обработал эти данные как обычную строку. Вот что я увидел на экране:

результат работы метода toString() класса request

Ну что же, вроде ничего сложного.

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

update. Заметил небольшой глюк в методе toUri(). Исправление в самой статье и в результате работы.

Всего Вам наилучшего. На сегодня у меня все!

Прикрепленные файлы

 

Возможно Вам будут интересны следующие заметки

Комментарии (4)

Ваше имя *
Сайт
Ваш E-mail *
Ваше сообщение *
 
CWTeam, 19 Июля 2015 г. 08:46 пишет:
Читатель
Автор прочти об очень полезной функции и выкини половину своего кода в мусорку
http://php.net/manual/ru/function.filter-var.php
Так же мотоды toString();
Зачем их писать каждый раз не проще обработчик в класс засунуть и вызывать так то так Request::Post("name", true, "") где
Request имя класса
Метод Post это работа с массивом $_POST
name имя переменной массива
true означает что выполнить фильтрацию
"" что вернет в случаее если переменной нет

Обработку напишешь сам, если интересно пиши дам более подробные советы
Алексей, 19 Июля 2015 г. 11:02 пишет:
Автор
http://php.net/manual/ru/function.filter-var.php
Знаю я про эту функцию. Может Вы и правы, и стоило бы писать на основе этого фильтра (я об этом думал). Если и буду что-то переписывать, то не сейчас.
Ответ для пользователя: CWTeam
CWTeam, 19 Июля 2015 г. 15:45 пишет:
Читатель
А почему бы сразу не написать все по уму, раз уж вы перешли на ООП, то можно синглтон использовать дабы было удобнее и избежать объявления класса дважды.

Вообще сугобо не по теме вопрос, много людей пользуются вашими скриптами? Вы хоть какую то статистику имеете?
Ответ для пользователя: Алексей
Алексей, 19 Июля 2015 г. 22:22 пишет:
Автор
то можно синглтон использовать
не хочется что бы по задумке своей класс был доступен из любой точки программы (но объект будет поднят в одном экземпляре). request намерено должен существовать только там где его можно использовать. Да, он будет работать как синглтон, но не на основе статичных методов.
Вообще сугобо не по теме вопрос, много людей пользуются вашими скриптами? Вы хоть какую то статистику имеете?
Нет не имею. Да мне и не зачем, этот блог мое хобби и только...
Ответ для пользователя: CWTeam