О магическом подходе к программированию

Jan 28, 2013 00:27


Позволю себе цитату из vit_r:

The great wizard Yasha lives in the Magical World of IT. He discovers Rotten Code by smell and applies the mysterious Refactoring Spell. The software becomes better. At least this is what other great wizards and their magical books say.

И ее же перевод на человеческий язык:

Yasha reads some old source code, he finds dependencies, connections and logical errors which where unknown or overlooked in the time when this code was written, than Yasha simply updates the source code to the level of his current understanding.

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

На днях в комментариях был озвучен другой пример “волшебства”. Для начала: что не так в этом коде (PHP, если кто не догадался)?

$name = isset($_GET['name']) ? $_GET['name'] : 1; $link = new dbLink(); $list = new listWidget($link, "SELECT name, age, class FROM students WHERE name='%s';", $name); $list->show();

Ответ: пока мы не увидим конструктора listWidget и кода, строящего SQL-запрос, мы не можем говорить о корректности этого кода. Правда, комментаторы отчего-то решили, что конструктор listWidget - это что-то типа sprintf, и пришедшее извне $name вставляется в SQL-запрос безо всякой обработки. Более того, в ответ на комментарий, что все тут пучком, kettle безапелляционно заявил:

Данные надо проверять на входе, а не в середине.

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

Для начала - обрисуем проблему. Если $_GET['name'] - это что-то нормальное, типа John или Jack - то при подстановке его вместо %s в шаблон запроса мы получим что-то типа

SELECT name, age, class FROM students WHERE name='John';




Что будет, если зловредные родители назовут своего ребенка каким-то экзотическим именем? Little Bobby Tables, говорят, ходит по нашей земле, и при подстановке простого американского имени Robert’; DROP TABLE students;- мы получим вот такую штуку:

SELECT name, age, class FROM students WHERE name='Robert'; DROP TABLE students;--';

Вместо одного запроса мы получаем два, второй из которых может содержать все, что угодно - например, как тут, удалять одну из таблиц в БД. Проблема эта довольно “популярная”, обросшая большим количеством “доморощенных” решений, и очень неплохо освещенная, например, в презентации SQL Injection Myths and Fallacies.

Таким “решением” в кавычках можно назвать и предложение kettle. Дело в том, что в простой ситуации “принять параметры, сделать запрос к БД, вывести результат” мы имеем не один тип данных - текстовую строку, а три разных - “пользовательский” ввод, запрос к БД и HTML-код выводимой страницы. PHP до неприличия упрощает ситуацию, объединяя эти совершенно разнородные вещи.

SQL Injection не имеет ничего общего с “проверкой входных данных”. И “John”, и “O’Brien”, и “”, и “Robert’; DROP TABLE students;-” - это прекрасные нерусские имена, которые могут и храниться в базе данных, и выводиться на страничку, и фигурировать в запросах пользователей. Задача программиста - сделать так, чтобы и в виде запроса от пользователя, и в БД, и на страничке, выводимой скриптом, они отображались одинаково. Как говорил В. И. Ленин, “величайшей ошибкой было бы думать”, что примеры из книжки “PHP для начинающих” имеют какое-либо отношение к реальности. Использование одних и тех же данных безо всяких преобразований и в роли “ввода”, и в запросах к БД, и для вывода - это чудовищный “антипаттерн”, причем культивируемый во всех учебниках.

Представим себе несколько извращенную и надуманную ситуацию. Предположим, что ввод от пользователя к нам приходит в кодировке КОИ-8, БД работает только с CP866, а для вывода мы должны использовать CP1251. Я думаю, любой русскоговорящий программист поймет, что здесь минимум в двух местах нужно осуществить соответствующие преобразования. Но ведь и в “обычном” случае мы имеем дело с такой ситуацией! “Алфавит” и “семантика” текста в $_GET, SQL-запросах и HTML-коде - совершенно разные, и похожи они лишь отдаленно. Собственно, игнорирование необходимости преобразования между ними - это причина самых распространенных ошибок в веб-программировании.

Кстати, предлагаю посмотреть, как устроены SQL-запросы с параметрами в “более других” драйверах для работы с БД в PHP:

http://bobby-tables.com/php.html

Как легко видеть, я просто “изобрел велосипед”, практически заново переизобретя уже готовые решения. Не вижу в этом ничего дурного :)

Запись опубликована в блоге Шуры Люберецкого. Вы можете оставлять свои комментарии там, используя свое имя пользователя из ЖЖ (вход по OpenID).

программирование

Previous post Next post
Up