Быстрое создание Web-сайтов с помощью CakePHP, Часть 3: Использование Sanitize для обеспечения защиты

Как защитить приложения с помощью CakePHP Sanitize и Security

CakePHP - это надёжное, готовое к использованию средство для быстрой разработки Web-сайтов на PHP. Данная серия "Быстрое создание Web-сайтов с помощью CakePHP" рассказывает о том, как разработать онлайновый каталог товаров с помощью CakePHP. Первая часть данного руководства рассказывает о том, как установить и запустить CakePHP, а вторая часть показывает использование scaffolding и Bake. Данная статья расскажет об использовании компонентов Sanitize и Security для защиты информации, передаваемой пользователями. Также вы научитесь обрабатывать недопустимые запросы.

Патрик О'Брайен, Программист, Orbtech

Патрик О'Брайен - программист Python, консультант и преподаватель. Он автор PyCrust и разработчик проекта PythonCard. Совсем недавно Патрик руководил группой PyPerSyst, которая переносила Prevayler на Python. Сейчас он продолжает вести этот проект, но для новой интересной области. За более подробной информацией о Патрике и его работе обращайтесь на Orbtech Web site, или пишите ему на pobrien@orbtech.com.



02.05.2007

Введение

Данная серия руководств предназначена для тех разработчиков приложений на PHP, кто хочет начать использовать CakePHP, чтобы облегчить себе жизнь. Прочитав до конца, вы научитесь устанавливать и настраивать конфигурацию CakePHP, изучите основы проектирования Модель-Представление-Контроллер (MVC), узнаете, как проводить валидацию пользовательских данных в CakePHP, как применять помощников CakePHP, и как, используя CakePHP, можно быстро написать и запустить приложение. Может показаться, что нужно изучить очень много, но не переживайте - большую часть CakePHP сделает за вас.

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

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


Системные требования

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

  1. Сервер HTTP с поддержкой сессий (и желательно mod_rewrite). Данное руководство было написано при использовании Apache V1.3 с mod_rewrite.
  2. PHP версии 4.3.2 или выше (включая PHP версии 5). Данное руководство создавалось на основе PHP версии 5.0.4
  3. Поддерживаемое ядро базы данных (на сегодняшний день - MySQL, PostgreSQL или используя надстройку над ADODB). Руководство было написано при использовании MySQL V4.1.15.

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

Самый простой способ получить CakePHP - это зайти на сайт CakeForge.org и загрузить самую последнюю стабильную версию. Данное руководство создавалось на основе версии 1.1.8. (Полные сборки и копии Subversion также доступны для загрузки. Подробное описание приведено в Справочнике по CakePHP - см. Ресурсы)


Знакомьтесь - Tor

В конце второй части, вам был предоставлен шанс применить свои знания на практике, внеся изменения в Tor. Требовалось использовать Bake для создания представлений и контроллера dealers, и затем проверить уникальность имени дилера, устранить недочеты в ACL товаров и сделать так, чтобы кнопки Edit и Delete в представлении products были доступны только тем пользователям, кто имеет право на их использование. Итак, у вас было большое задание. Справились?

Представление login

Создание представлений и контроллера dealers с помощью Bake не должно было вызвать каких-либо затруднений. Из установочной директории CakePHP следовало запустить php cake/scripts/bake.php.

Для создания контроллера в первом меню необходимо ввести C и в качестве имени контроллера указать dealers. Для создания представлений в первом меню необходимо ввести V и указать имя контроллера dealers. Данные шаги практически идентичны тем шагам, которые выполнялись для создания представлений и контроллера products.

Также было необходимо изменить в контроллере dealers функцию add для проверки существования предоставляемого имени дилера. Все это можно было реализовать примерно так:

Листинг 1. Изменение функции add в контроллере dealers
if ($this->Dealer->findByTitle($this->data['Dealer']['title']))
{
  $this->Dealer->invalidate('title');
  $this->set('title_error', 'A dealer with that title already exists.');
} else {
  ...
}

В представлении добавления дилера потребовалось использование переменной title_error, так же, как и в представлении добавления пользователя.

Устранение недочетов в методе product add

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

Используя списки контроля доступа (ACL), вы создали функцию в контроллере products, а затем использовали ее для предоставления права доступа на создание объекту запроса на доступ users (ARO) для каждого объекта контроля доступа dealer (ACO). Затем функция была удалена, так как она больше не понадобится. Далее вы изменили функцию dealer add для того, чтобы предоставить доступ на создание ACO dealer ARO users. В конце концов, в методе products add была организована проверка прав доступа пользователя, или вы реализовали примерно следующее:

Листинг 2. Проверка того, что пользователь зашел в систему при помощи функции users index
function add() {
  $username = $this->Session->read('user');
  if ($username)
  {
   ... the rest of your add function goes here
  } else {
    $this->redirect('/users/login');
  }
}

Кажется знакомым? Так оно и есть. Это та же самая проверка, которая использовалась для проверки того, что пользователь зашел в систему при помощи функции users index. Если человек зашел в систему, то очевидно, что он является пользователем. Решение очень хитрое, но оно показывает, что есть несколько способов проверить пользователя.

ACL дилеров

Получается, что функции дилера абсолютно не защищены. Их нужно изменить так, чтобы только пользователи могли ими пользоваться. В данном случае можно и нужно использовать ACL, хотя их возможности гораздо шире, чем будет представлено в данном руководстве. Рассмотрите следующее:

  • Любой пользователь может добавлять или просматривать дилеров.
  • Только тот пользователь, который создал дилера, может редактировать или удалить этого дилера.

Данное направление можно развить в приложении следующим образом:

  • Если пользователь создал дилера, то он может добавить другого пользователя в дилерское агентство.
  • Любой пользователь из дилерского агентства может изменять любой товар, созданный для агентства.

Все это можно реализовать и другими способами. Не бойтесь замарать рук.

Модернизация представления products

Итак, было необходимо изменить представления products так, чтобы кнопки Edit и Delete отображались только для тех пользователей, кто может редактировать или удалять товар. Вероятно, проще всего, совсем удалить кнопки Edit и Delete из представления index. Можно просто проверять каждый товар на наличие прав пользователя и отображать или скрывать кнопки, но от этого, может снизиться качество функционирования.

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

Листинг 3. Один из вариантов реализации функции view
if ($this->Acl->check($this->Session->read('user'), $id.'-'.$product['Product']['title'], 
'update'))
{
  $this->set('showUpdate', TRUE);
} else {
  $this->set('showUpdate', FALSE);
}
if ($this->Acl->check($this->Session->read('user'), 
$id.'-'.$product['Product']['title'], 
'delete'))
{
  $this->set('showDelete', TRUE);
} else {
  $this->set('showDelete', FALSE);
}

Тогда products/view.thtml должен выглядеть примерно так, как показано в листинге 4.

Листинг 4. Один из вариантов реализации products/view.thtml
<li><?php if ($showUpdate) { echo $html->link('Edit Product',   '/products/edit/' . 
$product['Product']['id']); } ?> </li>
<li><?php if ($showDelete) { echo $html->link('Delete Product', '/products/delete/' 
. $product['Product']['id'], null, 'Are you sure you want to delete: id ' . 
$product['Product']['id'] . '?'); } ?> </li>

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


Защита информации

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

Что подразумевается под защитой данных

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

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

Внедрение в SQL

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

"select * from users where username = '" + $username + "' 
and password = '" + $password + "'"

Подумайте, что произойдет, если пользователь введет вот такой пароль: ' or '1' = '1. Окончательный SQL-запрос будет выглядеть следующим образом:

"select * from users where username = 'wrestler' 
and password = 'secret' or '1' = '1'"

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

Межсайтовый скриптинг

Межсайтовый скриптинг (XSS) - это одна из многочисленных уязвимостей, которая основана на отправке злонамеренного кода доверчивому пользователю. Обычно это происходит в виде передачи злонамеренного JavaScript, который может делать все что угодно от простого надоедания пользователю до сбора информации из cookie-файлов.

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

<script>alert("EXPLETIVES!!!")</script>

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

Мы привели очень простой пример безобидного использования XSS. Однако, XSS способен и не большее. Его использовали для воровства паролей, номеров кредитных карт, опубликования лживых новостей, и так далее. Важно защитить себя и свое приложение от XSS. В этом может помочь CakePHP Sanitize.

Межсайтовая подделка запросов

Межсайтовая подделка запросов (CRSF) возможно не так популярна и известна, как XSS, но это не делает ее менее опасной.

Представьте, что ваше приложение является частью форума, а форум позволяет автору ветки удалить всю ветку. В форуме приложение представлено в виде кнопки, которую может увидеть только автор. Вы даже организовали проверку того, что автор, это действительно тот пользователь, который отправил запрос перед фактическим удалением. Это может быть реализовано отправкой имени поля action со значением delete, и имени поля id, содержащего уникальный ID ветки. В строке запроса будет следующая запись http://localhost/forum.php?action=delete&id=1729.

Теперь, представьте, что если вы отправляете рисунок, или у вас есть возможность указать рисунок как подпись, то в качестве URL будет представлена та же самая строка запроса. В HTML эта строка будет выглядеть следующим образом:

<img src="http://localhost/forum.php?action=delete&id=1729">

Я не могу перейти по URL напрямую, потому что не являюсь автором, и приложение об этом знает. Но если в ветке можно посмотреть мой рисунок, то браузер попытается загрузить рисунок, отправив запрос http://localhost/forum.php?action=delete&id=1729 - а так как именно автор делает запрос, то ветка удаляется.

Это самое простое описание использования и принципов работы CSRF. Защитить может CakePHP компонент Security.


Sanitize

Включите Sanitize - это класс CakePHP, позволяющий работать с защитой данных. В отличие от компонента ACL, описанного во второй части, компонент Sanitize можно включить, добавив строчку в верхнюю часть описания контроллера. Например, если нужно использовать Sanitize в контроллере products, то первая часть описания контроллера должна выглядеть следующим образом:

Листинг 5. Использование Sanitize в контроллере products
<?php
uses('sanitize');
class ProductsController extends AppController
{
...

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

Метод Sanitize paranoid

Данный метод является самым строгим. Метод paranoid очистит строку от всех символов, которые не являются буквенно-цифровыми (a-z, A-Z или 0-9). Метод paranoid принимает входную цепочку символов или любой массив ($allowedChars). Массив $allowedChars может использоваться для передачи массива символов, которые тоже входят в список разрешенных. Например, если необходимо, чтобы были разрешены буквенно-цифровые данные, символ подчеркивания и знак точки, то можно использовать следующее:

$san = new Sanitize();
$clean = $san->paranoid($your_data, array('_','.'));

ПРИМЕЧАНИЕ: Метод paranoid удалит все пробелы, если они не будут указаны ' ' в массиве $allowedChars как разрешенные символы.

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

Метод Sanitize html

Метод html может передавать два параметра: строку, которая является Sanitized и любую булеву метку $remove.

Если $remove задано значение true (истина), то метод html передает строку в PHP-функцию strip_tags, которая, в свою очередь, возвратит строку без всех HTML-тегов. Например, strip_tags("<p>Hello</p>", true) возвратит Hello.

Если $remove задано значение false (ложь), или не задано никакого значения, то метод html заменит некоторые символы на HTML-элементы. А именно:

  • & заменяется на &
  • % заменяется на %
  • < заменяется на <
  • > заменяется на >
  • " заменяется на "
  • ' заменяется на '
  • ( заменяется на (
  • ) заменяется на )
  • + заменяется на +
  • - заменяется на -

Для реализации этих замен метод html использует preg_replace.

Метод Sanitize sql

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

В частности, сначала метод sql проверяет, задано ли magic_quotes_gpc значение false (ложь). Если да, то строка передается в PHP-функцию addslashes, которая изолирует одинарные кавычки, двойные кавычки, обратные слэши и нуль-символы с помощью обратных слэшей. Например, addslashes("O'Brien") возвратит O\'Brien. Если magic_quotes_gpc задано значение true (истина), то PHP автоматически изолирует специальные символы, и, следовательно, нет никакой необходимости в каких-либо дальнейших действиях.

Метод Sanitize cleanArray

Метод cleanArray в качестве входных данных принимает массив и возвращает тот же самый массив, после того, как он был рекурсивно "вычищен,". В данном случае подразумевается, что были выполнены следующие действия:

  • При помощи str_replace все пробелы были приведены к виду " " (каждый пробел заменяется на " ", так же как и каждое появление chr(0xCA).
  • Значение передано через метод html для реализации замены HTML-элементов.
  • Все \$ заменены на $.
  • Удалены все newline (пустые строки).
  • ! заменен на !
  • ' заменено на '
  • Все вхождения двойного кодирования HTML-элементов исправлены (например, ' будет заменено на ').
  • Значение было обработано методом sql для подготовки строки к использованию в SQL-запросе.
  • Все введенные пользователем обратные слэши проанализированы, и в тех местах, где они могли привести к изоляции специализированных символов, обратные слэши были исправлены.

Можно было просто сказать, что "cleanArray проходит весь массив и вычищает каждое значение," но важно представлять и понимать, что выполняется за кулисами для обеспечения защиты информации.

Чтобы получить более полное представление о том, что происходит, можно посмотреть эти методы в cake/libs/sanitize.php. Однако, изменять основные файлы CakePHP не следует, так как это может привести к непредсказуемым последствиям и возникновению проблем с обновлением до более поздних версий.


CakePHP компонент Security

Чтобы использовать CakePHP компонент Security, добавьте его в массив компонентов контроллера, так же как было с ACL.

var $components = array ('Acl', 'Security');

При простом добавлении компонента Security, даже если он не используется для защиты функции, автоматически произойдет следующее:

  • Используя базовый класс Security, создается и записывается в сессию ключ аутентификации. Срок ключа аутентификации истечет в соответствии с настройками в app/config/core.php.
  • В контроллере ключ аутентификации задается в виде переменной класса.
  • Если какое-либо представление, создаваемое контроллером, для создания формы использует $html->formTag(), то в форму будет добавлено скрытое поле с названием _Token/key, в котором хранится ключ аутентификации. При публикации формы, значение данного поля сравнивается со значением ключа аутентификации в сессии для того, чтобы проверить, что отправка формы валидна. Как только проходит валидация, новый ключ аутентификации создается и записывается в сессию.

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

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

Метод requirePost

Security метод requirePost приказывает CakePHP игнорировать всю информацию, передаваемую конкретной функции, в том случае, если не используется метод POST. Методу requirePost передается список функций контроллера, которые необходимо защитить. Например, если нужно использовать requirePost для защиты методов delete и add, то метод beforeFilter должен выглядеть следующим образом:

function beforeFilter()
{
  $this->Security->requirePost('delete', 'add');
}

Предъявив требование, что функция может использовать информацию только из формы post, мы исключили возможность подделки запроса при помощи строки запроса.

Метод requireAuth

Security метод requireAuth приказывает CakePHP проверять валидность отправок всех форм с помощью ключа аутентификации, о котором говорилось ранее. Валидация произойдет только если форма отправлена посредством POST.

Так же как и методу requirePost, requireAuth передается список функций контроллера, которые необходимо защитить. Например, если необходимо использовать requireAuth для защиты методов delete и add, то метод beforeFilter должен выглядеть следующим образом:

<

function beforeFilter()
{
  $this->Security->requireAuth('delete', 'add');
}

Чтобы использовать requireAuth и requirePost, их необходимо определить в методе beforeFilter.

Листинг 6. Определение requireAuth и requirePost
function beforeFilter()
{
  $this->Security->requireAuth('delete', 'add');
  $this->Security->requirePost('delete', 'add');
}

Применение и requireAuth, и requirePost для защиты функции является мощным сочетанием. Однако, можно даже смешивать и отождествлять эти два метода, если для различных методов необходимы разные уровни защиты.

Листинг 8. Смешение и отождествление requireAuth и requirePost
function beforeFilter()
{
  $this->Security->requireAuth('delete');
  $this->Security->requirePost('delete', 'add');
}

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

Немного о кэшировании

Несмотря на все очевидные преимущества использования requireAuth для защиты функций, есть и несколько недостатков, которые следует рассмотреть. Большинство этих недостатков относится к "проблемам кэширования."

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

Что происходит при передаче невалидных форм?

Если запрос отклоняется requirePost или requireAuth, то приложение завершает работу, а пользователю отправляется страница 404. Чтобы изменить данное поведение, задать название функции контроллера можно свойству $blackHoleCallback компонента Security.

Например, если есть функция с названием invalid и соответствующее представление, то можно указать компоненту Security, что невалидные запросы следует отправлять функции invalid. Это можно реализовать, если добавить следующую строчку в начало метода beforeFilter: $this->Security->blackHoleCallback='invalid';.

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


Заполнение пробелов

Теперь, когда вы знаете как использовать компоненты Sanitize и Security, включите их в Tor.

Начните с чистки. Вычистите всю передаваемую информацию о продуктах, пользователях и дилерах. На свое усмотрение вы можете определить какой метод Sanitize следует использовать. Затем, проанализируйте контроллеры и их действия, для того, чтобы определить, какой из них нужно защитить с помощью requireAuth, а какой - с помощью requirePost. Реализуйте и используйте компонент Security. И наконец, для каждого контроллера создайте по функции с названием invalid and и воспользуйтесь методом для оповещения пользователя о том, что отправляемая форма не валидна.


Заключение

CakePHP компоненты Sanitize и Security не являются волшебными таблетками. Их использование вовсе не означает, что можно забыть о защите приложения. Однако, они помогут вам при работе с некоторыми из наиболее стандартных задач защиты. Очистка данных и игнорирование некорректно передаваемой информации являются первыми шагами в организации защиты приложения.

Четвертая часть в основном описывает CakePHP компонент Session, показывая три способа сохранения информации сессий, и компонент Request Handler, который позволяет управлять запросами различных типов (мобильные браузеры, запросы, содержащие XML или HTML, и другие).


Загрузка

ОписаниеИмяРазмер
Part 3 source codeos-php-cake3.source.zip9KB

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=216768
ArticleTitle=Быстрое создание Web-сайтов с помощью CakePHP, Часть 3: Использование Sanitize для обеспечения защиты
publish-date=05022007