Переработка и укрепление программы
Как было отмечено расширение кода WackoWiki себя исчерпало, и программа будет переписана с нуля с упором на аспекты безопасности. На этой странице можно будет следить за ходом разработки и обсуждать какие-то дизайнерские решения.
Комментарии, замечания и корректировки горячо приветствуются.
Формализация
Профиль использования
- Базовые условия:
- Wiki, свободное участие посетителей в развитии материалов.
- Спорность, неоднозначность многих материалов.
- Враждебность окружения.
- Регулярный веб-хостинг.
- Стандартное окружение и среда исполнения: Linux/BSD, Apache, CGI, Python, MySQL.
- Отсутствие контроля над средой исполнения и хранилищем данных.
- Скрытый сервис сети Tor.
- Высокая вероятность отказа, пропадания ресурса без объявления причин.
- Крайне низкая производительность сетевого соединения.
- Сайт правозащитной направленности.
- Риск навязывания цензуры администрации сайта; цензурирование материалов без ведома администрации.
- Принуждение администрации к раскрытию информации; правовое и внеправовое давление.
Модель угрозы
Пояснения: задача противника (класс атак), вектор атаки, класс защиты, контрмера, отражающая атаку или изменяющая её вектор, рекомендация / вне контроля приложения. Стрелки в обозначениях классов внутри дерева — это ссылки на раскрытые деревья данных классов.
- Искажение информации на страницах сайта
- Правка открытых страниц
- Ограничение доступа на запись с помощью списков ACL
- Повышение привилегий -->
- Повышение привилегий -->
- Ограничение доступа на запись с помощью списков ACL
- Несанкционированный доступ к базе данных -->
- Злоупотребление привилегиями (безосновательное удаление/модификация документов)
- Репликация данных БД между независимыми серверами -->
- Открытая модерация
- Сивиллова атака
- Репутационная система
- Сивиллова атака
- Репликация данных БД между независимыми серверами -->
- Правка открытых страниц
-
Отказ в обслуживании
- SYN-flood и т.п.
- Брандмауэр, тюнинг сетевого стека
- Брандмауэр, тюнинг сетевого стека
- Уничтожение сервера
- Репликация данных БД между независимыми серверами -->
- Репликация данных БД между независимыми серверами -->
- Паразитные запросы
- Кэширование данных
- Flood-control (блокирование паразитирующих хостов)
- Ограниченная балансировка нагрузки между зеркалами
- Кэширование данных
- SYN-flood и т.п.
-
Повышение привилегий
- Использование реквизитов доступа привилегированного пользователя
- Захват идентификатора авторизованной сессии
- Передача идентификатора только через cookie
- Жёсткое ограничение продолжительности сессии
- Привязка к IP
- Cross-site scripting
- Контроль и санитизация вывода
- Контроль и санитизация вывода
- Перехват на канале связи
- SSL
- SSL
- Несанкционированный доступ к базе данных -->
- Хэширование идентификаторов сессий в БД
- Хэширование идентификаторов сессий в БД
- Передача идентификатора только через cookie
- Компрометация пароля
- Привязка к IP
- Перехват на канале связи
- SSL
- Методы аутентификации без раскрытия секрета (по открытому ключу)
- SSL
- Несанкционированный доступ к базе данных -->
- Хранение в БД хэшированных паролей с привязкой (salt)
- Хранение в БД хэшированных паролей с привязкой (salt)
- Полный перебор
- Контроль сложности паролей
- Ограничение неудачных попыток
- Контроль сложности паролей
- Привязка к IP
- Захват идентификатора авторизованной сессии
- Эксплуатация бреши в приложении
- Использование реквизитов доступа привилегированного пользователя
-
Несанкционированный доступ к базе данных
- Клиентское заверение данных с помощью ЭЦП
- Клиентское шифрование данных открытым ключом
- Репликация данных БД между независимыми серверами -->
- SQL-инъекция
- Санитизация пользовательского ввода
- Санитизация пользовательского ввода
- Удалённый доступ к SQL-серверу / административной оболочке (phpMyAdmin) с помощью пароля БД
- Запрет подключений к SQL-серверу от нелокальных хостов
- Модульная реализация, границы доверия, предотвращение выхода критических данных (пароля БД и т.п.) за пределы доверенных зон
- Запрет подключений к SQL-серверу от нелокальных хостов
- Физический доступ к серверу базы данных
- Клиентское заверение данных с помощью ЭЦП
-
Несанкционированная модификация приложения
- Запись на диск и в память, переопределение свойств и функций посредством выполнения произвольного кода через брешь в приложении
- Компартментация кода, границы доверия, предотвращение выхода критических данных за пределы доверенных зон
- Отсутствие процедур сериализации/десериализации объектов за пределами доверенных зон
- Отсутствие вызовов eval() за пределами доверенных зон
- Компартментация кода, границы доверия, предотвращение выхода критических данных за пределы доверенных зон
- Непосредственный доступ к ФС сервера приложения (SSH, физический доступ)
- Запись на диск и в память, переопределение свойств и функций посредством выполнения произвольного кода через брешь в приложении
-
Репликация данных БД между независимыми серверами
- Модификация/компрометация реплицируемых данных на канале связи
- Криптографический контроль целостности и конфиденциальности
- Криптографический контроль целостности и конфиденциальности
- Компрометация нешифрованных данных зеркалами
- Отравление зеркал путём искажения БД мастер-сервера (мастер-сервер — сервер реплицирующий свою БД, независимо от того, централизованная это схема или децентрализованная с равноправными зеркалами)
- Контроль целостности мастер-БД через запрос хэшей изменяемых записей или хэшей записей, предшествующих вставляемым, от зеркал и определение "правильности" по превалирующему значению хэшей
- Несанкционированная модификация приложения -->
- Сговор зеркал для искажения БД мастер-сервера
- Искажение БД всех зеркал с целью блокирования консенсуса
- Несанкционированная модификация приложения -->
- Контроль целостности мастер-БД через запрос хэшей изменяемых записей или хэшей записей, предшествующих вставляемым, от зеркал и определение "правильности" по превалирующему значению хэшей
- Отравление зеркал целенаправленными действиями мастер-сервера
- Заверение операций клиента его открытым ключом, предоставление доказательств зеркалам
- MITM-подмена ключа, впервые загружаемого пользователем, при его передаче от мастера к зеркалам
- Заверение операций клиента его открытым ключом, предоставление доказательств зеркалам
- Модификация/компрометация реплицируемых данных на канале связи
Мысли вслух
Антицензурный механизм
Репликация данных между сетью зеркал — это, по существу, единственный доступный на текущем этапе [ограниченный] антицензурный механизм. Ограничен он тем, что не в состоянии контролировать аутентичность приложения на каждом из зеркал, благодаря чему каждое зеркало в отдельности может лгать пользователям, выдавая не ту информацию, которая содержится в синхронизируемой с другими зеркалами БД. Полноценное решение — это клиентская программа, выполняющая весь ввод/вывод данных в БД зеркал, снимая с них задачу веб-сервера. Но следует отдать себе отчёт в том, что само такое решение не является универсальным и может быть реализовано только на более поздних этапах, если в принципе будет найдено целесообразным.
Текущее ограниченное решение заключается в том, чтобы "сверить часы" (или базы) зеркал перед коммитом изменений от мастер-сервера. Допустим, пользователь зашёл на сайт и внёс изменение в текст документа; вот что примерно должно происходить (это набросок, а не законченный протокол):
- Мастер-сервер (где находится пользователь) извлекает из своей БД записи с n-i по n+i (где n — номер отредактированной пользователем записи, i — некоторое небольшое число), сериализует их и хэширует результат.
- Мастер-сервер рассылает зеркалам параметры операции (n, i, тип выполняемого пользователем действия).
- Зеркала повторяют операцию мастер-сервера со своими БД, заверяют полученные хэши своими закрытыми ключами и возвращают результат мастер-серверу.
- Мастер подсчитывает "голоса" (каждое уникальное значение хэша) и сравнивает превалирующий хэш с собственным, полученным на этапе 1:
- Если хэш мастера совпадает с большинством, он выполняет коммит зеркалам, передавая как введённые пользователем данные, так и все заверенные хэши, полученные от зеркал на этапе 3, и включая собственный. (Зеркала перед вводом данных в БД должны проверить правильность подсчётов мастера и отказать в коммите, если он лжёт.)
- Если хэш мастера не совпадает с большинством, он запрашивает спорные записи у одного из зеркал с "правильным" хэшем, коммитит их в свою базу и перезапускает процесс с этапа 2.
- Если все зеркала не имеют полного консенсуса относительно состояния базы, "меньшевики" по полученным от мастера заверенным хэшам также смогут определить, что у них что-то не так, и запросить "правильную" копию.
(Здесь не учтён ряд пограничных случаев типа спорного голосования пополам, полного отсутствия консенсуса и пр. Также пока непонятно, как быть с race conditions, если все зеркала равноправны, и два пользователя одновременно правят один и тот же документ или два соседних на разных зеркалах.)
Цель этого процесса в том, чтобы зеркала могли обнаружить, когда одно из них по той или иной причине выходит из синхронизации (например, по причине цензуры). Если одно из зеркал остаётся рассинхронизированным в течение долгого срока, другие зеркала могут исключить его из сети.
Вот проблемы и ограничения предложенной схемы:
- Как было отмечено, она не способна решить проблему с модификацией самой программы. В подобном случае одно из зеркал (или несколько) может поддерживать две базы: одну, используемую только для синхронизации, и другую, отцензурированную, данные из которой и отображаются пользователям. Описанный механизм является ограниченной мерой; адекватно устранить это ограничение можно лишь перенеся функции отрисовки и отображения данных на сторону клиента.
- Скрытная модификация на одном из зеркал тех записей БД, которые обычно редко редактируются пользователями (или не редактируются совсем) и не имеют часто редактируемых соседей, так что подобное искажение может никогда не обнаружиться исполнением приведённого выше протокола. Решения:
-
Большое значение i, дабы каждым коммитом охватывать большее число соседних записей. Что, в свою очередь, означает дополнительную нагрузку на узлы при хэшировании большего объёма данных.В любом случае, само по себе такое решение не является полным. - Периодическое блокирование БД, хэширование всего содержимого и сравнение результатов между зеркалами. Собственные проблемы:
- Необходимость в синхронизации этого действа. В принципе, не слишком большая проблема (команду о выполнении полной проверки может рассылать один из узлов, получив которую, все начинают подготовку).
- Выявление спорных участков БД. Тоже решаемая задача: хэшировать блоки записей (скажем, по 100 строк), а затем — сам полученный набор хэшей. Такая схема позволит даже выполнять полную проверку в несколько этапов, дабы не останавливать работу сайта на длительный срок.
- Оптимизированная версия предыдущего варианта. Во-первых, каждая строка БД уже содержит хэш существенных полей данной строки (что избавляет зеркала от необходимости пересчитывать хэши при каждом коммите мастера, который, вероятно, просто пытается их заDoSить). Во-вторых, специальное поле (единственное для таблицы или для всей БД), содержащее XOR всех хэш-значений всех предыдущих коммитов и их счётчик. То есть получается так: когда пользователь вносить изменения в БД, сервер подсчитывает хэш существенных полей данной записи, вносить этот хэш в ту же строку БД, а затем XOR'ит его с текущим значением этого специального поля. В итоге, для полной синхронизации зеркалам нужно только обменяться значениями этого специального поля, сравнить его и счётчик, а полную сверку (описанную в п.2 предыдущего варианта, но с предрассчитанными хэшами) проводить только в случае расхождений.
-
- В принципе, ничто не мешает мастеру коммитить любые изменения, отравляя зеркала, покуда эта отрава будет синхронизирована между зеркалами (т.е. схема защищает только от скрытной модификации БД без ведома админа сервера, но не от целенаправленных действий админа). Решения:
- Зеркала могут проверять вводимые данные против текущего состояния системы (например, мог ли данный пользователь отредактировать данную страницу, учитывая его привилегии и ACL документа), но такой механизм крайне легко обойти: просто лгать (выбрать пользователя с наивысшей репутацией/привилегиями и "править" документ от его имени).
- Аутентификация пользовательского действия ключом PGP (это то, что зеркала могут объективно проверить). Хотя аутентификация большинства действий кажется тривиальной, более серьёзное размышление вырисовывает ряд попутных проблем:
- Атаки с повторной передачей, если заверять один только текст документа (как сейчас) без каких-либо метаданных. Решение:
- Первые строчки документа должны содержать декларацию, логически привязывающую текст к данному применению (например, это может быть адрес документа и номер последней редакции). Такое доказательство зеркала могут принять.
- Трудности с аутентификацией действий, отличных от ввода простого контента, к примеру, изменений настроек или ACL документа. Решение:
- В подобных случаях сервер может сериализовать действие пользователя (представить его в виде понятного пользователю текстового набора команд плюс значения счётчика для защиты от повторной передачи), которое последний и должен заверить цифровой подписью.
- Существует некоторый класс часто повторяемых действий, постоянная аутентификация которых может свести пользователя с ума. В частности, это работа с репутационной системой. (Следует учитывать, что это в немалой степени зависит от конкретного дизайна такой системы. Пока я рассматриваю идею с "плюсами" и "минусами", которые участники могут расставлять объектам системы, оказывая тем самым влияние на рейтинг владельцев этих объектов. Более подробно данный вопрос будет рассмотрен отдельно, поскольку требует учёта ряда "трудных" ситуаций, как то "правильную" оценку редакций документов.) Решение:
- Не требовать аутентифицировать каждое такое действие, а только накапливать их в виде простых команд, которые пользователь в любой момент может заверить скопом. Такой подход требует особого внимания к пользовательскому интерфейсу, поскольку пользователь может покинуть сайт, не произведя заверение команд, и они будут просто потеряны (правда, можно и сохранять их в БД до следующего визита пользователя)...
- Главная сложность возникает на точке входа. Хотя аутентичность последующих изменений БД можно проконтролировать с помощью описанных мер, у зеркал не может быть гарантии, что первая загрузка открытого ключа пользователя в действительности выполнена им самим, и ключ принадлежит пользователю. Ничто не мешает мастеру MITM'ить ключи всех пользователей. Решения:
-
Загрузив ключ на мастер, пользователь может обойти зеркала и проверить, что на них также находится его ключ.Но как он в случае подлога докажет зеркалам, что это и впрямь не его ключ? - На странице загрузки ключа помещать несколько самостоятельных веб-форм, каждая выполняющая POST-запрос на соответствующее зеркало. Дёшево и сердито, зато работает.
-
- Приведённую выше MITM-подмену ключа на точке входа можно обобщить на любые впервые вводимые данные. Так, если правила аутентификации операций над объектом не закреплены мандатно, их добровольная установка может быть подделана мастер-сервером, и зеркала об этом никогда не узнают. (По-русски: предположим, требование о заверении всех операций с помощью ключей не установлено на сайте глобально; пользователь создаёт новый документ и включает это свойство локально для данной страницы. Но, независимо от желания пользователя, мастер-сервер не рассылает это обновление зеркалам, поэтому может и впредь любым образом модифицировать документ, бесконтрольно отправляя зеркалам злоумышленные редакции.) Решение пока неизвестно.
- Атаки с повторной передачей, если заверять один только текст документа (как сейчас) без каких-либо метаданных. Решение:
Репутационная система
"Человек может иметь столько электронных личностей, на создание скольких ему хватит времени и сил".
- J. S. Donath, "Identity and Deception in the Virtual Community"
Если мы исходим из безоговорочного доверия к администрации ресурса, система репутаций имеет смысл лишь с точки зрения функциональности программной платформы, и её реализация представляется опциональной. В то же время, исходя из заявленной модели угрозы, мы не можем всецело положиться на администрацию в части управления контентом и разрешения конфликтов, поскольку такое сделает администрацию единственным рычагом, посредством давления на который противник сможет бесконтрольно оказывать влияние на содержание ресурса.
Поясню, что "администрация" в настоящем контексте — это исключительно сторона, инициировавшая создание ресурса и осуществляющая его техническую поддержку. Администрация не обязана быть одновременно и контрибьютором материалов, хотя подобное сочетание ролей и нельзя назвать уникальным и, тем более, ограничить его какими-либо программными средствами.
Следует оговориться, что в рассматриваемой модели только намерения противника носят статичный характер: прекратить доступ к той или иной информации, предоставляемой ресурсом. Намерения и интересы администрации могут быть проанализированы лишь в динамике (на игровой основе), и имеют в своей основе поддержание или увеличение ядра пользователей и сохранение ресурса во временной перспективе, поскольку предполагаемая цель создания информационного интернет-сайта — предоставление некоторой информации некоторому (по возможности, наиболее широкому) кругу заинтересованных лиц (что стоит за этой целью — идейность или коммерческая заинтересованность — не принципиально, поскольку оба стимула являются достаточными для следования "правилам игры").
Предоставленная сама себе, администрация будет поддерживать баланс этих условий, выражая интересы большинства по модулю своего личного субъективного мнения. Это может выражаться в жёстком цензурировании/удалении наиболее маргинальных материалов (с сопутствующей "текучестью кадров" в группах пользователей, представляющих данные материалы) и мягком цензурировании материалов, не отвечающих позиции администрации (но лишь до той степени, до которой данные материалы не находят поддержку у большинства пользователей; в противном случае администрация сталкивается с риском массового оттока посетителей, что нарушает условия игры и ведёт к проигрышу). Внешнее давление, запугивание и угрозы способны исказить приоритеты администрации с краткосрочного удовлетворения интересов посетителей к долгосрочной выживаемости ресурса, следовательно, навязанная цензура с высокой вероятностью будет принята, несмотря на сопротивление и отток пользователей. (Разумеется, подцензурность значительной доли материалов равносильна уничтожению ресурса. Определить действия администрации в подобной ситуации без учёта всего комплекса "жизненных" факторов не представляется возможным.)
В то же время, нужно подчеркнуть, что сама администрация не может быть заинтересована в установлении и поддержании режима цензуры, противоречащей интересам большинства, поскольку это не будет способствовать выигрышу администрации ни в краткосрочной, ни в долгосрочной перспективе. Следовательно, сама администрация не может являться полноценным нарушителем модели угрозы (за исключением, возможно, предвзятой модерации), и можно рассчитывать на её общее сотрудничество с интересами пользователей. (В ином случае ничто не мешает администрации изначально отказаться от рассматриваемой программной платформы в пользу такой, которая не сможет оказать противодействия любой проводимой политике.)
Отмеченную централизацию власти можно устранить в коллективном управлении контентом. Коллективное управление может выражаться посредством голосований за удаление комментария или документа, запуск/остановку опроса, блокирование пользователя, иные подобные действия. Реализованное в таком виде, коллективное управление открывает простор для сивилловых (sybil) атак: противник, зарегистрировав произвольно большое количество пользователей и используя их для голосований, будет способен влиять на принятие решений и, в итоге, на объём и состав представленный материалов. Модель репутации пользователей способна серьёзно снизить применимость подобной атаки.
Поскольку в компьютеризированном мире затраты времени и энергии на операцию ничтожны (что делает сивилловы атаки возможными), фактором, повышающим порог вхождения следует рассматривать репутацию, основанную на релевантности публикуемой информации с точки зрения людей.
Типы хэндлеров документов (пространства имён)
- WackoWiki имеет характерную концепцию хэндлеров — инструментов работы с документом. Примеры хэндлеров — это show (просмотр страницы), edit (её правка), acls (настройка прав доступа). Достаточно дописать имя хэндлера в конец URL, чтобы получить к нему доступ. Хэндлеры позволяют достаточно эффективно разбить функционал на независимые модули, элегантно вызываемые через URL без всей GET-галиматьи типа pagename?handler=edit.
- Вторая концепция, не получившая развития в оригинальной Ваке — это типы хэндлеров. В Ваке определён только один — page, в который и свалены все хэндлеры для работы с документом. Хотя данную идею легко расширить, определив типы, например, для опросов, административных модулей, комментариев, существенно унифицируя систему и делая её легко расширяемой: чтобы добавить функционал, достаточно определить новое пространство имён, или "класс", если угодно (в виде типа хэндлеров), и "методы" такого "класса" (в виде самих хэндлеров).
- Первая проблема заключается в способе передачи типа хэндлера через URL запроса в систему. Варианты:
- domain.com/[type]/pagename/[handler][:lang]. Ломает все существующие ссылки. Решения: 1) переписать все ссылки при конверсии базы (ссылки с внешних ресурсов всё равно останутся битыми), 2) принять отсутствие типа за тип page. Более глобальная проблема этого подхода в том, что подобное резервирование имён может в дальнейшем выйти боком при последующих расширениях, если новый тип foobar станет конфликтовать с документом foobar на каком-то сайте.
- domain.com/pagename/[type]/[handler][:lang]. Особых сложностей это не создаст, хотя вариант с переопределением одноимённых пользовательских страниц остаётся возможен (пусть и не станет переопределять целые кластеры). Как и сейчас, по умолчанию можно принимать page/show. Учитывая, что и другие типы должны по идее иметь свои хэндлеры show, то он может быть глобальным умолчанием. А так получаем, к примеру, site.com/ВыПересталиБитьЖенуПоУтрам/poll/results, что откроет нам результаты данного опроса.
- domain.com/pagename:[type]:[handler][@lang]. Не вызовет проблем с одноимёнными страницами, поскольку определения типа и хэндлера явно отделены от URL нетипичным символом. Определение языка должно иметь уникальный разделитель, дабы не создавать двусмысленности при опущенных определениях типа и хэндлера. Минус: не слишком элегантный URL с кучей служебных символов (а ведь дальше могут следовать параметры GET-запроса). Приоритетный вариант, если не придумается что-нибудь лучше.
- Вторая проблема — это способ распределения пространств имён в базе данных. Сейчас все страницы лежат в одной таблице pages, там же есть доставшееся по наследству поле handler, заполненное везде константой page. Редакции находятся в таблице revisions, имеющей почти идентичную структуру. Варианты:
- оставить, как есть. Документы из всех пространств лежат в одной таблице. Уникальный ключ каждой записи — это триплет name:type:lang. Плюсы: 1) единообразие и лёгкая расширяемость: опять же, добавление функционала не потребует никаких особых действий, кроме написания/установки модуля; 2) возможность хранить редакции всех документов независимо от типа в одном месте; 3) управление записями таблицы может осуществляться одним базовым набором инструментов. Минусы: 1) таблица может стать о-о-о-очень большой; 2) документы определённых типов могут потребовать особых полей, недоступных в таблице.
- для каждого типа выделить отдельную одноимённую таблицу. Плюсы: 1) высокая оптимизация каждой таблицы под конкретные данные, учитывая специфику документов; 2) распределение объёма базы по набору таблиц, что упрощает операции резервирования. Минусы: 1) расширение функционала затруднено — требуется создавать новые таблицы и поддерживать их структуру в актуальном виде; 2) хранение редакций затруднено (придётся создавать таблицы редакций и для типов документов) или невозможно; 3) в каких-то случаях для управления записями могут потребоваться специальные программные инструменты; 4) степень дублирования структуры и данных в действительности может оказаться довольно высока.
- альтернативы? Первый вариант на мой взгляд выигрышней, несмотря на минусы, которые можно сгладить или обойти.
Предотвращение race conditions. Атомарные операции
- Логические блокировки (мютексы) записей БД при мультитранзакционных операциях.
- Особенно важное условие при действующей структуре БД (пока спасала лишь невысокая посещаемость сайта), но принципиально не удастся избежать всех возможных узких мест даже с переработанной структурой.
- Возможно ли обеспечить блокировку лишь средствами БД (выделив под размещение мютексов специальную таблицу)? Похоже, что LOCK TABLES в MySQL исключит появление race'а уже в установке/проверке самих мютексов разными потоками. С другой стороны, при таком подходе вообще отпадает потребность в самодельных средствах блокировки. Требует дальнейшего изучения. Важно соблюсти баланс производительности и атомарности операций.
Клиентское приложение (плагин Firefox?) для автоматического зашифрования/расшифрования особо важных документов
Реализация на втором этапе (отдалённая перспектива). Характеристики:
- Безопасный IPC-интерфейс для связи с процессом GnuPG (реализован в Enigmail).
- Автоматическая обработка пересылаемых POST-данных (реализовано в Enigform).
- Шифрование передаваемых данных (реализовано в FireGPG).
- Policy-centric-управление посредством подписанных хидеров в HTTP-ответе сервера (обосновать необходимость).