Тонкости при работе с Sanitize в связке с save

    Доброго времени суток уважаемый %username%. Хотелось бы вам рассказать об одной интересной особенности работы с классом Sanitization. Данный класс является частью ядра замечательной фреймворка CakePHP и предназначен для «очистки» входящих данных. К примеру, его можно, нужно использовать для «очистки» данных передаваемых пользователем перед сохранением в базу данных. Официальная документация и примеры использования лежат тут book.cakephp.org/view/1183/Data-Sanitization. Настоятельно рекомендую каждому разработчику использующему CakePHP ознакомиться с этим классом.
    Но у Sanitize есть одна особенность, которая не задокументирована и на выяснение которой у автора ушло достаточно много времени. Вот о ней и хотелось бы рассказать подробнее. Хотя если сесть и хорошенечко подумать, то никакая это не особенность и не загвоздка, просто нужно быть внимательнее и всё оказывается понятным, простым и очевидным. У Sanitize есть 4 статических метода:
    • Sanitize::paranoid — удаляет «лишние» символы из строки
    • Sanitize::html – режет html
    • Sanitize::escape – экранирует строку перед добавлением в базу данных
    • Sanitize::clean — «чистит» переданный массив.

    В данном случае нас больше всего интересует метод clean. Sanitize::clean(mixed $data, mixed $options) принимает два параметра $data — массив и набор опций для очистки (или если угодно правила фильтрации)
    • odd_spaces
    • encode
    • dollar
    • carriage
    • unicode
    • escape
    • backslash

    Каждое из этих правил применяется рекурсивно к каждому элементу из $data. Рассмотрим это на живом примере:
    у нас есть таблица комментариев (id,first_name,last_name,email,comment)
    модель Comment
    контроллер Comments с методами add и view
    Начнём с котроллера функция добавления комментария
    public function add() {
    		 //проверяем на наличие данных в $_POST
    		if (isset($this->data['Comment']) and !empty($this->data['Comment']) ) {
    			App::import('Sanitize');
    			// "чистим" полученные данные
    			$this->data = Sanitize::clean($this->data);
    			$this->Comment->set($this->data);
    			// проверяем валидность входных данных
    			if ($this->Comment->validates()) {
    				// сохранение в бд
    				if($this->Comment->save()){
    					$this->redirect('/comments/');
    				}	
    			}
    		}
    }

    вроде ничего необычного и непонятного.
    Sanitize::clean($this->data)
    как видим мы использовали все фильтры доступные у этого метода.
    Что же добавим первый комментарий
    add comment
    смотрим что получилось,
    view comment
    так стоп стоп стоп, откуда взялся второй слэш в поле last_name? Может это ошибка отображения?
    Посмотрим как выглядят данные в базе.
    in db
    Итак кавычки были закодированные, это понятно. Но вот всё же откуда этот второй слеш в поле last_nme?? Мы с вами %username% прекрасно знаем что при экранировании в строку добавляются (простите за тавтологию) экранирующие слеши, но они присутствуют в строке только до момента попадания в базу. Иными словами экранирующие символы никогда не попадают в базу. Но что же произошло в данном случае? Давайте разберёмся! Для этого уберём в методе add redirect на defaul action (закомментируем строчку $this->redirect('/comments/');), введём те же данные и посмотрим что произойдёт. Смотрим на запрос добавления:
    INSERT INTO `comments` (`first_name`, `last_name`, `email`, `comment`) VALUES ('Lorem ' ipsum "', 'Aliquam\\\\ut/metus', 'consectetur@amet.sit', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus posuere, lectus vel mollis laoreet, mauris est vehicula tortor, non tincidunt purus turpis id ligula. Ut gravida scelerisque nisi in auctor. Sed fringilla eleifend massa in mattis. Suspendisse potenti.')

    Как мы с вами помним Sanitize очищает данные перед сохранением в бд. Значит часть символов была экранирована. Экранирующие слэши не записываются в базу, это и понятно. В нашем случае у нас 4 символа «\» два из них попали в базу, остальные были экранирующими. Стоит предположить что строка была экранирована дважды. На самом деле так оно и есть. Если покопаться в исходниках метода save (да и если просто хорошенечко подумать) то мы можем обнаружить что Model->save() так же экранирует строку перед сохранением. Таким образом если вы используете в связке save + Sanitize отключайте опцию escape в нашем случае
    $this->data = Sanitize::clean($this->data,array('escape' => false));
    Но если для записи данные вы пользуетесь методм Model->query() и Sanitize опция escape должна быть включена.
    Спасибо всем кто дочитал до этого места. Не знаю, но мне самому кажется что это статья их раздела «О привет ребята, я вчера Америку открыл». Что же пусть будет так, но все же я думаю что это тонкость, о которую новичок (плохо читающую документацию) может серьёзно споткнутся. C большим вниманием выслушаю любые замечания и предложения
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 7
      0
      Чтобы понять, как работает Sanitize нужно заглянуть в исходники. Мануал там ни на что не годится… как и перевод мануала…
        0
        всегда можно добавить исправления как в оригинальную документацию, так и в перевод
        +1
        не совсем к теме топика, конечно, но я считаю, что в бд нужно складывать все как есть, а санитизировать при выводе
          0
          не совсем понимаю зачем санитизировать при выводе? Обычная практика это Filter Input (защищаемся от SQL injection и прочего), Escape Output (защита от XSS и тому подобного ). Всё же думаю лучше заранее всё почистить и потом уже быть спокойным за вывод. Часто данные вводятся в одном месте а выводятся во многих (например, в админке и в front-end), вот и получается что проще один раз их санитизировать при вводе. Ну хотя может тут и дело вкуса.
            0
            с sql injection и кэйковский ORM справится, а вот когда данные понадобятся в исходном виде, то никто не поможет
              0
              Соглашусь, все зависит от поставленной задачи.
          0
          не помню как довно не заглядывал в мануал — только исходники

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

          Самое читаемое