Управление списком пользователей и базовая авторизация #
Документация #
Хранение паролей #
Для хранения паролей пользователей в любой информационной системе необходимо предпринимать меры по защите данной информации от потери. В случае потери списка пользователей злоумышленники не должны иметь простой возможности по получению паролей пользователей, т.к. пользователи имеют тенденцию к повторному использованию паролей между различными веб-приложениями.
Пароль также обладает интересной особенностью: не надо сохранять пароль целиком, достаточно иметь возможность сравнить пароль, который был введён пользователем при регистрации, с паролём, который был введён для авторизации в системе. Для решения этой задачи зачастую прибегают к хеш-функциям, которые формируют одинаковую хеш-строку для одинакового ввода данных.
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-выражения проведите преобразование солированного пароля в хеш-значение.
 
 
- Доработайте процедуру добавления нового треугольника.
- На странице добавления добавьте поле для ввода имени пользователя и пароля ученика.
- При добавлении треугольника проверьте, что введённый пароль соответствует указанному ученику. Если пароли не совпадают, то необходимо показать страницу добавления треугольника с сообщением об ошибке.
- Создайте операцию по проверке пароля пользователя.
- Операция должна принимать имя ученика, пароль и соль.
- В рамках операции должно проверяться соответствие имени ученика и хеш-суммы от пароля с солью с сохранёнными данными.