Модель угрозы
Это обсуждение безопасности баз данных и веб-приложений, основанное на краткой справочной странице на моем сайте . Эта страница становится громоздкой и не позволяет читателям легко взаимодействовать со мной или другими.
Модель угрозы
Весь анализ безопасности должен начинаться с изучения модели угроз . Модель угрозы требует, чтобы вы ответили на четыре вопроса:
- что я пытаюсь защитить?
- от кого?
- На сколько долго?
- и по какой (чистой) стоимости?
Что я пытаюсь защитить?
Это очевидное место для начала … и ваш первый ответ, вероятно, неправильный! Я имею в виду, что вы можете ответить «пароль базы данных», но это не совсем верно. На самом деле вы хотите защитить доступ к базе данных от имени этого пользователя — злоумышленник может найти путь к базе данных без пароля, например, с помощью SQL-инъекции.
Но подождите, это тоже не совсем верно! Наша настоящая задача — не дать злоумышленнику использовать этот доступ для нанесения ущерба, изучения конфиденциальной информации и т. Д. На данный момент мы должны перечислить наши реальные проблемы, например, наша база данных может содержать
- пользовательский контент
- финансовая информация
- аутентификация и авторизация пользователей
- бревна
- статический контент
Способ доступа и использования этой информации различен
- пользовательский контент — нужен постоянный доступ для чтения / записи
- финансовая информация — нужен оракул (для утверждения) и может оставить детали для выполнения процесса
- аутентификация и авторизация пользователя — нужен оракул (для одобрения и авторизации), когда пользователь входит в систему, но никогда после этого (оракул)
- логи — нужен постоянный доступ только для добавления (оракул)
- статический контент — нужен доступ только для чтения при запуске (оракул)
(Весь доступ по модулю требует технического обслуживания.)
Оракул — это автономный метод, который принимает (необязательные) значения и возвращает либо true, либо false . В более общем смысле он может возвращать любой автономный неизменный объект. Хорошим вариантом реализации для оракула является хранимая процедура в базе данных, лучшим выбором будет вызов REST другому веб-приложению, использующему независимую базу данных.
Два примера оракулов:
Аутентификация пользователя: используйте оракула, который принимает имя пользователя и пароль и возвращает логическое значение, указывающее, было ли оно действительным или нет. (Альтернатива: вернуть полную структуру authn / authz при успешном завершении.) Подход без оракула заключается в том, что приложение выполняет запрос к таблицам пользователей и паролей и сравнивает сами пароли.
Аутентификация кредитной карты: используйте оракула, который берет информацию о кредитной карте и сумму покупки и возвращает либо номер подтверждения, либо указание на ошибку. Приложение может полагаться на оракула, хранящего копию ранее предоставленных значений (но не CVV!), Чтобы пользователю не приходилось каждый раз заполнять одну и ту же информацию. , Подход без оракула для приложения, чтобы связать саму информацию.
Смысл, который я здесь подчеркиваю, заключается в том, что решение о том, что должно быть защищено, является архитектурным вопросом, и некоторая предусмотрительность может оказать существенное влияние на модель угрозы. Вам нужно как можно меньше информации о недоверенных пользователях (например, о веб-приложении), и небольшие изменения могут привести к большим изменениям.
Наконец, что не менее важно, есть еще одна вещь, которую следует защищать: ваша репутация. Не компания — разработчик. Что вы скажете, когда вам позвонит президент компании с требованием узнать, почему компания будет главной новостью в ночных новостях? Вы не можете защитить от всех атак, но не хотите оставаться в безмолвии, когда кто-то требует знать, почему вы не предприняли основных шагов для защиты системы.
От кого?
Все.
Хорошо, я ребенок. Но есть гораздо более широкий список, чем вы думаете.
- нащупал сотрудников. Мы все сделали это. У них уже есть законный доступ.
- недовольные сотрудники, особенно бывшие сотрудники. У них уже есть законный доступ и мотивация.
- Сценарий детишек. Мы склонны думать о них как о неискушенных, но они могут использовать инструменты взлома, написанные экспертами. Вероятно, они перейдут к более простым целям, если ваш сайт достаточно защищен.
- продвинутые постоянные угрозы (ATP). Это люди, которые имеют сильную мотивацию и сильные технические навыки. Предположим, они войдут.
Этот список далеко не исчерпывающий, и перечисление дополнительных «потенциальных злоумышленников» оставлено в качестве упражнения для читателя.
На сколько долго?
На риск быть очевидным, есть три широкие категории
- Информация или доступ, которые должны быть защищены до относительно скоро определенной даты, а затем становятся открытыми, например, корпоративные финансовые отчеты.
- Информация или доступ, который со временем уменьшается.
- Информация или доступ, которые должны быть защищены навсегда, например, конфиденциальные юридические и медицинские документы.
Первая категория проста, поскольку известны наиболее известные алгоритмы и атаки, а у злоумышленника ограничено время для работы.
Последняя категория сложна, поскольку мы знаем, что не можем предсказать будущие атаки. Некоторые вещи, которые были невозможны 10 лет назад, теперь заурядны. Один хороший совет: вещи, которые мы не храним, — это вещи, которые нам не нужно защищать. Держите как можно меньше, но не меньше.
Полный анализ находится за рамками этой записи в блоге, но это важная проблема, которую не следует упрощать.
По какой (чистой) стоимости?
«Стоимость» является гибкой концепцией, поскольку существует очень много косвенных и предполагаемых затрат. Например, какова стоимость того, чтобы людям было труднее выполнять свою работу … или это дешевле, поскольку система не будет работать в течение нескольких дней после нарушения? Какова стоимость людей, покидающих сайт в отчаянии, по сравнению с преимуществами людей, не покидающих сайт в массовом порядке после взлома вашего сайта, сделанного национальными новостями?
Суть в том, что это в конечном итоге нетехнический вопрос. Все, что вы можете сделать, это определить прямые и косвенные проблемы и позволить властям принять окончательное решение.
Положить его вместе
Суть в том, что модель угрозы в конечном итоге является бизнес-решением. Мы можем предоставить анализ и рекомендации, но окончательное решение должно прийти сверху.
Тем не менее, есть много вещей, которые мы можем сделать по собственной инициативе. Эта дискуссия будет касаться некоторых из них.
SQL-инъекция
Что такое SQL-инъекция?
SQL-инъекция — это способность злоумышленников вставлять произвольные команды SQL в вашу систему.
Образец атаки
Посмотрите на следующий код:
1
2
3
|
ResultSet rs = stmt.execute( 'select * from users where username=' ' + username + '' and password= '' + password + '' '); |
Что может пойти не так? Допустим, мы используем следующие значения:
1
2
|
String username = 'bob' or 1 = 1 ; --'; String password = 'dont care' ; |
Когда мы вызываем предыдущий код, сгенерированный код
1
2
|
select * from users where username= 'bob' or 1 = 1 ; --' and password= 'dont care' |
Это будет список всех пользователей. Некоторые веб-фреймворки будут перечислять всех пользователей в системе. Более тщательно написанные приложения вызовут тревогу, если будет возвращено более одной записи. Это легко исправить
1
2
|
String username = 'bob' or 1 = 1 order by userid limit 1 ; --'; String password = 'dont care' ; |
производить
1
2
|
select * from users where username= 'bob' or 1 = 1 order by userid limit 1 ; -- ' and password=' dont care' |
Раздел ‘order by’ гарантирует, что мы увидим первого пользователя в системе. Это обычно администратор — что-то злоумышленники не забывают.
Неправильный подход
Многие неопытные программисты пытаются обойти эту проблему путем явной очистки введенного пользователем ввода.
1
2
3
4
|
ResultSet rs = stmt.execute( 'select * from users where username=' ' + username.replaceAll( '' ', ' '' ') + '' and password= '' + password.replaceAll( '' ', ' '' ') + ' '' ); |
Это могло бы сработать в 1980-х, но сегодня мир использует больше, чем ASCII. Правильное определение символов кавычек — нетривиальная проблема, и ее следует оставить другим. Авторы JDBC часто имеют для этого специфичные для базы данных методы, но они могут быть не синхронизированы с базой данных и, разумеется, специфичны для базы данных.
Подготовленные заявления и заполнители
Стандартное решение этой проблемы заключается в использовании подготовленных операторов и заполнителей. Это заменяет код
1
2
3
|
ResultSet rs = stmt.execute( 'select * from users where username=' ' + username + '' and password= '' + password + '' '); |
с участием
1
2
3
4
5
|
PreparedStatment stmt = conn.prepareStatement( 'select * from users where username=? and password=? limit 1' ); stmt.setString( 1 , username); stmt.setString( 2 , password); ResultSet rs = stmt.execute() |
Ограничения
Есть моменты, когда подготовленные заявления неуместны. Одним из распространенных примеров являются операторы с несколькими вставками. Это может быть значительно быстрее, чем несколько подготовленных вызовов операторов.
Примером оператора с несколькими вставками является
1
2
3
4
5
6
|
insert into squares(x, y) values ( 1 , 1 ), ( 2 , 4 ), ( 3 , 9 ), ( 4 , 16 ), ( 5 , 25 ); |
Как правило, это не должно использоваться с данными, предоставленными пользователем. Если это абсолютно необходимо, используйте метод для конкретной базы данных, предоставленный вашим провайдером JDBC, а не свое собственное решение.
Анализ выгоды и затрат
Анализ затрат / выгод при использовании подготовленных заполнителей выписок не имеет значения — это одна из тех вещей, которые вам просто нужно сделать.
SQL-инъекция в хранимых процедурах
Что такое хранимые процедуры и CallableStatements?
Хранимые процедуры — это биты кода, хранящиеся в базе данных. Наиболее распространенной формой является SQL-подобный язык сценариев, но поддерживаются дополнительные языки — PERL, tcl, ruby, java и т. Д.
Важно помнить, что хранимые процедуры используются в триггерах базы данных — вы должны знать о них, даже если вы все работаете с Hibernate.
Неправильный подход
Неправильный подход заключается в создании динамического SQL-запроса без очистки.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
DELIMITER $$ DROP PROCEDURE IF EXISTS SP_AUTHENTICATE$$ CREATE PROCEDURE SP_AUTHENTICATE(IN username VARCHAR( 20 ), IN password VARCHAR( 20 ), OUT success INT) BEGIN SET @query = CONCAT('SELECT COUNT(credentials.username) INTO @succ FROM credentials WHERE credentials.username = \ '' , username, '\' AND credentials.password = \'' , password, '\'' ); PREPARE stmt FROM @query ; EXECUTE stmt; SELECT @succ ; SET success = @succ ; END; $$ DELIMITER ; |
(Примечание: этот фрагмент кода является примером из ссылки ниже.)
Эта хранимая процедура не имеет преимуществ перед «неправильным ответом», который мы видели ранее, за исключением очень скромной инкапсуляции.
Sidenote: Это пример оракула . Он возвращает минимальный объем информации об аутентификации пользователя — «большие пальцы» или «большие пальцы вниз». В этой реализации нет утечки информации, поскольку вызывающая сторона уже знает имя пользователя и пароль, но более надежная реализация также может проверить, что учетная запись пользователя не отключена и т. Д.
Хранимые процедуры и параметризация
Первый безопасный подход — это непосредственное выполнение SQL вместо создания динамического SQL.
Второй безопасный подход — параметризация в хранимой процедуре. Это прямо эквивалентно подготовленным Java операторам и заполнителям.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
DELIMITER $$ DROP PROCEDURE IF EXISTS SP_AUTHENTICATE$$ CREATE PROCEDURE SP_AUTHENTICATE(IN username VARCHAR( 20 ), IN password VARCHAR( 20 ), OUT success INT) BEGIN SET @query = 'SELECT COUNT(credentials.username) INTO @succ FROM credentials WHERE credentials.username = ? AND credentials.password = ?'; PREPARE stmt FROM @query ; SET @usernm = username; SET @pass = password; EXECUTE stmt USING @usernm , @pass ; SELECT @succ ; SET success = @succ ; END; $$ DELIMITER ; |
PLPSQL Санитарная обработка
Существует другая альтернатива, если вы хотите быть привязанным к конкретному поставщику базы данных. На практике это обычно не проблема — гибернация обеспечивает некоторую прозрачность базы данных, но хранимые процедуры всегда будут тесно связаны с базой данных.
В plpsql (PostgreSQL) есть две команды, которые можно использовать для очистки ввода: quote_ident и quote_literal . Несомненно, есть подобные команды в других языках хранимых процедур.
Обновляя неправильный ответ выше, мы имеем:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
DELIMITER $$ DROP PROCEDURE IF EXISTS SP_AUTHENTICATE$$ CREATE PROCEDURE SP_AUTHENTICATE(IN username VARCHAR( 20 ), IN password VARCHAR( 20 ), OUT success INT) BEGIN SET @query = CONCAT('SELECT COUNT(credentials.username) INTO @succ FROM credentials WHERE credentials.username = ', quote_literal(username), 'AND credentials.password = ' , quote_literal(password)); PREPARE stmt FROM @query ; EXECUTE stmt; SELECT @succ ; SET success = @succ ; END; $$ DELIMITER ; |
Прямой SQL
Последний безопасный подход — использовать прямые вызовы SQL с минимальным размером параметра. Это упомянуто на веб-сайте CERT, но я не хотел бы использовать его, так как было бы легко ввести небезопасный код случайно.
01
02
03
04
05
06
07
08
09
10
11
12
|
DELIMITER $$ DROP PROCEDURE IF EXISTS SP_AUTHENTICATE$$ CREATE PROCEDURE SP_AUTHENTICATE(IN username VARCHAR( 8 ), IN password VARCHAR( 20 ), OUT success INT) BEGIN SELECT COUNT(credentials.username) INTO success FROM credentials WHERE credentials.username = username AND credentials.password = password; END; $$ DELIMITER ; |
Анализ выгоды и затрат
Хранимые процедуры сложнее использовать, чем голые запросы SQL, но это часто дает людям ложное чувство безопасности. Это следует считать обязательным для конфиденциальной информации (аутентификация пользователя, ведение журнала аудита) и настоятельно рекомендуется во всех других случаях.
Схема владения
Что такое DDL, DML, DCL и TCL?
SQL содержит четыре различных типа операторов.
Язык определения данных
Операторы языка определения данных (DDL) определяют структуру базы данных. Думайте об этом как о хозяине, который строит склад, но передает ключи арендатору.
Заявления:
- создавать — создавать таблицы, представления, индексы и т. д.
- изменить — изменить таблицы, представления, индексы, столбцы и т. д.
- drop — удалить таблицы, представления, индексы и т. д.
- усечь — удалить все записи из таблицы
- comment — добавлять комментарии к таблицам, столбцам, представлениям и т. д.
- переименовать — переименовать таблицу, просмотреть и т. д.
Язык манипулирования данными
Операторы Data Manipulation Language (DML) управляют данными в структуре, созданной DDL. Думайте об этом как о владельце склада — он может использовать склад, но не может разрушить стены.
Заявления:
- выбрать — получить данные
- вставить — вставить новые данные в таблицу
- обновить — обновить существующие данные в таблице
- удалить — удалить данные из таблицы
- call — вызвать PL / SQL или другую хранимую процедуру
- объяснить план — объяснить, как будет выполняться запрос
- заблокировать таблицу — заблокировать таблицу для ограничения параллелизма
Язык управления данными
Операторы языка управления данными (CDL) управляют правами доступа к данным и схеме. Думайте об этом как о замках на дверях, разрешении перемещать стены внутри склада и т. Д.
Заявления:
- Грант — дать пользователю дополнительные привилегии
- отменить — удалить привилегии пользователя
Язык управления транзакциями
Оператор языка управления транзакциями (TCL) используется для управления транзакциями.
Заявления:
- совершить — сохранить выполненную работу
- откат — отменить выполненную работу
- точка сохранения — отметьте точку, к которой мы можем вернуться позже, не обязательно откатывая всю транзакцию
- установить транзакцию — установить параметры транзакции
Использовать другого пользователя базы данных для схемы и владения данными
Схема должна принадлежать одному пользователю базы данных, например app_owner, а данные должны принадлежать другому пользователю базы данных, например app_user .
Владелец должен:
- иметь возможность запускать операторы DDL и DCL
- возможно, не имеют возможности запускать операторы DML
- никогда не будет доступен через веб-приложение
Пользователь должен
- иметь возможность запускать операторы DML и TCL
- не может запускать операторы DDL или DCL
- быть доступным через веб-приложение
Анализ выгоды и затрат
Существует очень выгодное соотношение затрат и выгод для разделения владельца схемы и базы данных. Существует несколько более высокая стоимость при создании и обслуживании базы данных, но это существенно устраняет возможность веб-злоумышленника уничтожить саму схему базы данных. Данные, с другой стороны, все еще могут быть обнулены.
Информация об аутентификации и авторизации пользователя
Аутентификация пользователя ( authn ) — это то, как мы знаем, что пользователь — это тот, кем он себя считает. Как минимум, это имя пользователя и пароль, но они могут включать гораздо больше, если используется двухфакторная аутентификация.
Авторизация пользователя ( authz ) — это то, что мы разрешаем пользователю делать.
Это очень разные вопросы и должны рассматриваться как таковые. Некоторые архитектуры для этого, например, если сайт использует siteminder или аналогичный инструмент, он вообще не имеет доступа к информации authn — он может только добавить authz.
Что такое информация пользователя authn / authz? это
- имя пользователя и / или адрес электронной почты
- пароль
- идентификаторы единого входа (SSO)
- токены безопасности (для двухфакторной аутентификации)
- защитные изображения / фразы (используются для подтверждения того, что ваш сайт является законным для пользователя)
- группы и роли
Что не является информацией пользователя authn / authz?
- контакты
- подписка на контент
- или что-либо еще, что не требуется для аутентификации или авторизации пользователя.
Неправильный подход
Поместите все — пользовательский auth / authz, статический контент и динамический контент — в единую схему базы данных.
Это быстро
Это просто.
Это поведение по умолчанию для инструментов автоматической генерации.
И это очень, очень неправильно, так как любой, кто взломает ваше веб-приложение, также взломал ваши данные пользователя auth / authz. В лучшем случае у вас будет отказ в обслуживании. В худшем случае они могут выдавать себя за других пользователей, могут добавлять свои собственные привилегированные учетные записи и т. Д.
Отдельные схемы и пулы соединений
Самое быстрое решение — создать отдельную схему для пользовательских данных authn / authz и использовать выделенный источник данных (или сеанс Hibernate) при доступе к этим данным. Эта схема должна быть нечитаемой из стандартного источника данных (или сеанса Hibernate). Это дает вам хороший брандмауэр от мира, но не идеален.
На первый взгляд более надежное решение — использовать отдельную базу данных, а не отдельную схему, для пользовательских данных authn / auhtz. Это может защитить вас от неверно настроенных прав, которые позволят источнику данных динамического содержимого получить доступ к источнику данных пользователя.
К сожалению, в некоторых RDMBS нет четкого различия между схемами и базами данных, и соединение с одной «базой данных» все еще может обращаться к другой «базе данных», если предоставлены необходимые права. Вы не можете быть уверены, если у вас нет отдельного экземпляра базы данных для пользователя authn / authz и динамического содержимого. Это не может быть чрезмерным бременем, если в вашей архитектуре есть сервер, выделенный для пользователя authn / authz. Это не лишено смысла с виртуальными серверами или облачным дизайном.
Контейнерная аутентификация
Лучшее решение — аутентификация на основе контейнеров. Полностью удалите пользователя authn / authz из веб-приложения — к тому времени, когда ваше веб-приложение получит запрос, в HttpServletRequest уже будет заполнена вся необходимая информация. Ваше веб-приложение не имеет доступа к информации аутентификации контейнера. (По модулю примечаний выше — вы ничего не получите, если контейнер смотрит на ту же схему, что и ваш динамический контент.)
Вариант этого — фильтры аутентификации, помещенные перед веб-приложением, например, из Spring Security. Это другой механизм, но он служит той же цели — проводить четкое различие между пользовательскими данными и динамическим контентом.
The Glitch — Добавление и обновление пользователей
Здесь есть один большой сбой — как вы добавляете или обновляете информацию о пользователе, если ваше веб-приложение не может получить доступ к таблицам authn / authz пользователя?
Первый подход заключается в создании отдельного веб-приложения, которое обрабатывает это. Ваше основное веб-приложение может прозрачно перенаправить на второе веб-приложение по мере необходимости. Положительным моментом является то, что вы можете иметь единообразный внешний вид, а отрицательным — то, что вы снова предоставляете пользователю информацию о пользователях и аутентификаторах.
Второй подход заключается в создании отдельной службы REST, которая обрабатывает это. Ваше веб-приложение может предоставлять пользовательский интерфейс, но вызывать службу REST вместо стандартного бизнес-уровня. Служба REST может находиться в вашем брандмауэре.
Третий подход — полностью отложить это на контейнер. Это обеспечивает максимальное разделение, но затрудняет постоянный внешний вид.
Ссылки по теме
https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=70288108
Справка: Безопасность баз данных и веб-приложений, часть 1: Модель угроз , безопасность баз данных и веб-приложений, часть 2: внедрение SQL , безопасность баз данных и веб-приложений, часть 3: внедрение SQL в хранимых процедурах , безопасность баз данных и веб-приложений, часть 4: владение схемой , база данных И Безопасность Webapp, Часть 5: Аутентификация пользователя от нашего партнера JCG Bear Giles в блоге Invariant Properties .