Управление списком пользователей и базовая авторизация #
Документация #
Хранение паролей #
Для хранения паролей пользователей в любой информационной системе необходимо предпринимать меры по защите данной информации от потери. В случае потери списка пользователей злоумышленники не должны иметь простой возможности по получению паролей пользователей, т.к. пользователи имеют тенденцию к повторному использованию паролей между различными веб-приложениями.
Пароль также обладает интересной особенностью: не надо сохранять пароль целиком, достаточно иметь возможность сравнить пароль, который был введён пользователем при регистрации, с паролём, который был введён для авторизации в системе. Для решения этой задачи зачастую прибегают к хеш-функциям, которые формируют одинаковую хеш-строку для одинакового ввода данных.
flowchart LR login["Пароль регистрации"] auth["Пароль авторизации"] hash("Функция хеширования") log_hash["Хеш регистрации"] log_auth["Хеш авторизации"] login --> hash auth --> hash hash --> log_hash hash --> log_auth
Если применять функции хеширования «в лоб», тогда для взломщика надо будет не просто прочитать хеш, а перебрать строки, хеш-сумма которых будет совпадать с той, что записана в базе данных. Значительное улучшение, однако всё-равно позволяет достаточно легко найти оригинальный пароль.
Для улучшения защиты данных для перебора необходимо выполнить ещё пару улучшений:
- Увеличить количество итераций для выполнения алгоритма. Это затруднит прямой перебор.
- Добавить секретную соль, которой производится расширение данных в пароле.
В результате алгоритм вычисления хеша будет следующим:
- взять пароль от пользователя
- добавить к введённому паролю соль
- применить к полученной строке хеш-функцию определённое количество итераций
Затем вычисленный хеш либо необходимо сохранить в базу данных, либо сравнить с тем, что находится в базе данных.
Сохранение соли для паролей #
Соль для паролей нельзя хранить вместе с паролями, т.к. это полностью лишает её всяческого смысла. Её можно располагать в отдельном хранилище или поместить в настройках приложения. Стоит учитывать, что для корректной работы системы потребуется помимо данных также предоставить корректную соль.
Согласно разрабатываемой архитектуре веб-приложения для хранения данной информации следует воспользоваться подсистемой настроек. При этом данная настройка не является частью настроек веб-сервера или базы данных.
Использование функций в базе данных H2 #
База данных H2 предоставляет ряд функций, которые можно применять для манипулирования над данными. При этом обработка может осуществляться как на данными, которые передаются внутрь БД, так и с теми, которые возвращаются из БД.
Для выполнения хеширования строк предоставляется функция HASH:
CALL HASH('SHA3-256', '12345', 10)
В качестве первого аргумента передаётся название алгоритма для выполнения шифрования. Для обеспечения качества шифрования лучше всего использовать алгоритмы семейства SHA3
: SHA3-224
, SHA3-256
, SHA3-384
и SHA3-512
. Разница между ними заключается в размере формируемой хеш-строки. Чем он больше, тем меньше может быть возможность коллизий, тем надёжнее идентификация строки.
Вызов функций через Ktorm #
Уровень слоя взаимодействия с базой данных путём генерации SQL-выражений из библиотеки Ktorm поддерживает основные операции над данными: извлечение, модификация, подсчёт. Однако расширенные возможности, предоставляемый СУБД, не поддаются обобщению. Для поддержки расширенных возможностей конкретных баз данных предоставляются расширения конкретных диалектов.
Стоит отметить, что даже с этими расширениями поддержать весь спектр возможностей языка SQL Ktorm не в состоянии. Для обеспечения работы с такими ограничениями Ktorm предоставляет возможность выполнения нативного SQL-кода с помощью вызова Database.useConnection
. Данный метод принимает в качестве аргумента подключение к базе данных, объект типа Connection.
С его помощью можно выполнить подготовленный заранее подготовленный SQL-запрос, PreparedStatement
:
class GetDepartmentNameOperation(
private val database: Database,
) {
private val preparedSql = """
select NAME from EMPLOYEE
where DEPARTMENT_ID = ?
order by ID
""".trimIndent()
fun getDepartmentName(departmentId: Int): String? {
database.useConnection { connection ->
connection.prepareStatement(preparedSql).use { statement ->
statement.setInt(1, departmentId)
return statement
.executeQuery()
.asIterable()
.map { row ->
row.getString(1)
}.firstOrNull()
}
}
}
}
Для выполнения запросов на получение данных используется метод PreparedStatement.ExecuteQuery
, для выполнения запросов на изменение данных используется метод PreparedStament.ExecuteUpdate()
.
Задача #
- Реализуйте хранилище для сохранения соли. Сформируйте соль для пароля. Соль должна быть достаточно большой длины, рекомендуется от 100 случайных символов.
- Добавьте новую сущность «Ученик». Он описывается следующими полями: имя пользователя, пароль, дата добавления.
- Добавьте новую миграцию к приложению, описывающего данную сущность. Длина строки под пароль 64 символа.
- Не забудьте добавить уникальный идентификатор.
- Создайте описание таблицы с помощью библиотеки Ktorm.
- Реализуйте процедуру добавления нового ученика.
- Добавьте страницу «управление пользователями», доступную через навигационную панель.
- На странице добавьте кнопку «добавить ученика».
- При нажатии на кнопку пользователю должна открыться форма с вводом данных. На форме должны присутствовать поля: имя пользователя и два поля для ввода пароля.
- При отправке данных с формы на уровне обработки данных формы, необходимо проверить равенство двух паролей, введённых пользователем.
- Создайте операцию по изменению данных в базы.
- В конструктор операции передайте ссылку на подключение к базе данных.
- В конструктор операции передайте строку-соль, полученную из настроек.
- Для передачи соли из настроек передайте соль также в конструктор хранилища операций.
- В момент выполнения операции получите имя пользователя и пароль.
- Перед записью пароля в базу данных объедините её с солью.
- При записи данных в базу внутри SQL-выражения проведите преобразование солированного пароля в хеш-значение.
- Доработайте процедуру добавления нового треугольника.
- На странице добавления добавьте поле для ввода имени пользователя и пароля ученика.
- При добавлении треугольника проверьте, что введённый пароль соответствует указанному ученику. Если пароли не совпадают, то необходимо показать страницу добавления треугольника с сообщением об ошибке.
- Создайте операцию по проверке пароля пользователя.
- Операция должна принимать имя ученика, пароль и соль.
- В рамках операции должно проверяться соответствие имени ученика и хеш-суммы от пароля с солью с сохранёнными данными.