Не забудь сделать escape! 4

Posted by Андрей on Октябрь 31, 2010

Когда я только начинал программировать в web, правильно сделать escape данных было непростой задачей: никаких хороших библиотек не было или приходилось писать что-то свое, при этом на каждом шагу не забывая поставить нужный escape. Сегодня отличные библиотеки, такие как Ruby on Rails, позволяют «расслабиться» и забыть о том, что такое escaping (по крайней мере до какой-то степени). Не смотря на это, все еще необходимо понимать, что такое escaping, зачем он нужен, когда и какой.

Отсутствие правильного escaping (впрочем, как и избыточный и неуместный escaping) приводит к ошибкам и уязвимостям (проблемам безопасности) в web-приложениях. Обычно уязвимость состоит в том, что приложение получает данные из различных внешних источников (от пользователя, из других приложений), эти данные приложение вставляет строчку, которая впоследствие будет обработана третьей системой (базой данных, браузером, интерпретатором и т.п.) При этом при передаче особым образом подготовленных данных удается совершить действие, которое не должно было произойти.

SQL

Типичная уязвимость: SQL Injection.

Пример кода (авторизация по логину и паролю):

runQuery("SELECT id FROM users WHERE login='$login' AND password='$password'")

Если значения переменных $login и $password получены от пользователя (например, через форму авторизации), можно в поле password ввести значение вида: ' OR '' = ', тогда после подстановки получится такой запрос:

SELECT id FROM users WHERE login='login' AND password='' OR '' = ''

Условие WHERE всегда истинно, для любой строчки БД. В зависимости от вида запроса, способа авторизации такое поведение приведет к возможности авторизации, не зная пароля.

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

Continue reading…

UDF в MySQL, json или то, как забрать обновления данных из БД 3

Posted by Андрей on Октябрь 30, 2010

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

Одно из возможных решений – сделать «выгрузку» данных из MySQL с помощью UDF (User Defined Functions) и триггеров. Для этого необходимо поставить слейв MySQL, на котором уже повесить на интересующие таблицы триггеры, которые с помощью UDF будут выгружать поток изменений таблиц во внешнюю систему. Слейв необходим, т.к. если триггеры поставить на мастере, то в случае отката транзакции действия, уже сделанные триггерами, откатить не получится, а на слейв попадают только зафиксированные транзакции. Второе,чтобы триггеры работали на слейве, тип репликации должен быть выставлен на STATEMENT-based.

Порывшись в одном интересном архиве UDF для MySQL я нашел несколько функций, которые мне подошли:

  • преобразование строки MySQL в json;
  • интерфейс с memcached.

В результате получился следующий план действий: данные модифицируются на мастере, реплицируются на слейв с помощью STATEMENT-репликации. В процессе репликации на слейве запускаются триггеры, формируют с помощью UDF пакет обновлений в JSON, и передают его во внешнюю очередь (memcacheq) по memcached-протоколу. Конечно, это не единственный возможный способ, но все UDF уже были почти готовы. После доделывания напильником UDF получился вполне стабильно работающий вариант.

Триггеры выглядят примерно следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE FUNCTION kick_photos (row_id INT) RETURNS INT 
BEGIN 
SELECT memc_set('queue_db', (json_object('insert' AS action, 'photos' AS table_name, photos.id AS id, json_members('data', json_object(photos.user_id AS `user_id`,photos.width AS `width`,photos.created_at AS `created_at`,photos.filename AS `filename`,photos.parent_id AS `parent_id`,photos.content_type AS `content_type`,photos.height AS `height`,photos.thumbnail AS `thumbnail`,photos.size AS `size`))))) INTO @dummy FROM photos WHERE id = row_id; 
RETURN @dummy; 
END
 
CREATE TRIGGER photos_INSERT AFTER INSERT ON photos FOR EACH ROW 
SET @dummy = memc_set('queue_db', (json_object('insert' AS action, 'photos' AS table_name, NEW.id AS id, json_members('data', json_object(NEW.user_id AS `user_id`,NEW.parent_id AS `parent_id`,NEW.created_at AS `created_at`,NEW.filename AS `filename`,NEW.width AS `width`,NEW.content_type AS `content_type`,NEW.height AS `height`,NEW.thumbnail AS `thumbnail`,NEW.size AS `size`)))));
 
CREATE TRIGGER photos_DELETE BEFORE DELETE ON photos FOR EACH ROW 
SET @dummy = memc_set('queue_db', (json_object('delete' AS action, 'photos' AS table_name, OLD.id AS id, json_members('data', json_object(OLD.user_id AS `user_id`,OLD.parent_id AS `parent_id`,OLD.created_at AS `created_at`,OLD.filename AS `filename`,OLD.width AS `width`,OLD.content_type AS `content_type`,OLD.height AS `height`,OLD.thumbnail AS `thumbnail`,OLD.size AS `size`)))));
 
CREATE TRIGGER photos_UPDATE AFTER UPDATE ON photos FOR EACH ROW 
BEGIN 
IF json_object(OLD.user_id AS `user_id`,OLD.parent_id AS `parent_id`,OLD.created_at AS `created_at`,OLD.filename AS `filename`,OLD.width AS `width`,OLD.content_type AS `content_type`,OLD.height AS `height`,OLD.thumbnail AS `thumbnail`,OLD.size AS `size`) <> json_object(NEW.user_id AS `user_id`,NEW.parent_id AS `parent_id`,NEW.created_at AS `created_at`,NEW.filename AS `filename`,NEW.width AS `width`,NEW.content_type AS `content_type`,NEW.height AS `height`,NEW.thumbnail AS `thumbnail`,NEW.size AS `size`) THEN 
  SET @dummy = memc_set('queue_db', (json_object('update' AS action, 'photos' AS table_name, OLD.id AS id, json_members('new', json_object(NEW.user_id AS `user_id`,NEW.parent_id AS `parent_id`,NEW.created_at AS `created_at`,NEW.filename AS `filename`,NEW.width AS `width`,NEW.content_type AS `content_type`,NEW.height AS `height`,NEW.thumbnail AS `thumbnail`,NEW.size AS `size`)), json_members('old', json_object(OLD.user_id AS `user_id`,OLD.parent_id AS `parent_id`,OLD.created_at AS `created_at`,OLD.filename AS `filename`,OLD.width AS `width`,OLD.content_type AS `content_type`,OLD.height AS `height`,OLD.thumbnail AS `thumbnail`,OLD.size AS `size`))))); 
END IF; 
END;

Комментарии:

  • функция kick_photos позволяет скопировать строчку таблицы в очередь как пакет обновления типа «вставка», может использоваться для начального наполнения внешней системы;
  • триггеры на удаление и вставку просто формируют соответствующие пакеты;
  • триггер на обновление проверяет, действительно ли в пакете произошли изменения (например, мы можем использовать не все поля в пакете);
  • необходимо учесть, что работе FOREIGN KEY CONSTRAINT триггеры не вызываются (очередной прикол MySQL), т.е., например, при если при выполнении запроса на удаление из таблицы A будут по FOREIGN KEY удалятся записи из таблицы B, то в триггере на удаление из A необходимо отработать этот случай, т.к. триггеры на таблице B не будут вызваны.

Код UDF доступен на github, это – «подпиленный» код из репозитория UDF или собственные разработки:

HighLoad-2010: Приемы разработки высоконагруженных приложений на Twisted/Python 2

Posted by Андрей on Октябрь 28, 2010

25-26 октября состоялся HighLoad-2010, конференция получилось хорошей хотя бы потому, что было мало докладов ни о чем. Неплохой уровень, особенно было приятно увидеть «профессоров» PostgreSQL.

Я выступал с докладом «Приемы разработки высоконагруженных приложений на Twisted/Python». В докладе получилась (вполне сознательно) сборная солянка из советов и приемов о том, как писать приложения на Twisted (и похожих frameworkах). Из-за большого количества разных тем не получилось углубиться ни в одну, каюсь…

Тезисы:

  1. Запуск и шедулинг многих однопоточных процессов на одном сервере.
  2. Key-value storage и приемы работы с ним.
  3. Обслуживание сотен тысяч соединений на одном сервере.
  4. HTTP-сервисы и балансировка нагрузки, локализация нагрузки.
  5. Сбор статистики, интеграция с системой мониторинга.
  6. Шина обмена сообщениями на примере AMQP.
  7. Поиск и устранение memory leak.
  8. Оптимизация по времени отклика и пропускной способности.
  9. Мифы и правда о Python как языке разработки нагруженных приложений.

Презентация:



Утащить:

  • В формате PDF
  • Модуль py-numа, который упоминался в докладе