Контроль доступа через клиентские сертификаты (nginx / showcert).

Сертификаты (обычные, x.509) могут подтверждать не только то, что сервер в самом деле тот, какой вы ожидаете, но и могут подтверждать клиента.

Преимущества защиты через клиентские сертификаты - эта защита снаружи приложения (на уровне nginx). Не требуется никак изменять приложение (это не всегда просто). Кроме того, ваше приложение может быть сколь угодно старым, уязвимым, низкокачественным, написаным неграмотным и невнимательным студентом, работающим на древнем wordpress или php 5-ой версии. Даже такой ужас будет надежно защищен! Любые механизмы защиты которые внутри этого приложения могут иметь проблемы, однако, проверка клиентских сертификатов выполняется веб-сервером, а он написан достаточно хорошо.

Минусы - подходит только если вы можете раздать сертификаты пользователям (это легко если пользователей несколько человек) и установить их.

Создаем свой центр сертификации

Я буду использовать свой любимый showcert - с ним все проще. Рекомендую его, но если не хотите - все можно сделать и через openssl.

Сделаем каталоги CA и certs чтобы хранить все в порядке. Далее:

## generate CA cert
gencert --ca --cert CA/myca.pem --key CA/myca-priv.pem "My CA" --days 3650

Это было несложно, правда?

Выпускаем сертификат для клиента

# issue client certificate
gencert --cacert CA/myca.pem --cakey CA/myca-priv.pem --days 3650 'John Smith' --cert certs/johnsmith.pem

Чтобы вставить его в браузер, надо сконвертировать его в PKCS #12 формат (почему-то браузеры не любят обычный PEM). Но это несложно:

# convert to PKCS#12
openssl pkcs12 -export -certfile CA/myca.pem -in certs/johnsmith.pem -out certs/johnsmith.p12

(Тут он попросит пароль)

Добавляем сертификат в хранилище сертификатов

Добавление сертификата в Firefox

У браузера Firefox - собственное хранилище, В моей версии - about:preferences#privacy, в разделе Certificates жмем “View Certificates…” и на первой же вкладке (Your Certificates) жмем Import, выбираем наш файл .p12, вводим пароль и он появится.

Добавление сертификата в браузеры на основе Chrome (Chromium, Brave)

Не смотря на то, что там есть аналогичный раздел chrome://settings/certificates и даже есть кнопка Import - жать ее не нужно! Добавлять надо в системное хранилище сертификатов.

Windows В винде в проводнике двойной клик на сертификате, и дальше все по умолчанию. Подробная инструкция: https://knowledge.digicert.com/tutorials/gatekeeper-p12-certificate-installation-method

Linux Если хотим протестировать через curl, то все просто:

curl --cert certs/yaroslav.pem https://example.com/

Если хотим чтобы работало из браузеров:

# import client certificate to storage
pk12util -d sql:$HOME/.pki/nssdb -i certs/johnsmith.p12

Дополнительные команды:

# list client certificates
certutil -d sql:$HOME/.pki/nssdb -L

# delete certificate
certutil -d sql:$HOME/.pki/nssdb -D -n 'John Smith'

MacOS

  1. двойной клик на файле
  2. прочитать текст и авторизовать добавление
  3. все.

Настраиваем nginx

Копируем myca.pem на сервер, в /etc/ssl/private. В файле сервера (например, /etc/nginx/sites-available/example.conf) в разделе server {} добавляем:

    ssl_client_certificate /etc/ssl/private/myca.pem;
    ssl_verify_client on;

И все! Несложно. (для применения изменений: nginx -t для проверки и nginx -s reload для загрузки новой конфигурации)

Но можно еще и в логах видеть имя из сертификата. Создаем /etc/nginx/conf.d/certlog.conf со строчкой:

log_format certlog '$remote_addr - $ssl_client_s_dn [$time_local] "$request"';

А в сервере пишем:

access_log /var/log/nginx/example-access.log certlog;

Если сертификат добавился, но не работает

  1. Убедитесь, что добавился, что он есть в списке сертификатов
  2. Перегрузите браузер. Иногда помогает открыть окно инкогнито.
  3. Иногда помогает удалить сайт из истории, если сначала вы зашли на него без сертификата и получили ошибку.

Что мы получили?

Практически абсолютную защиту (ну… насколько защита может быть абсолютной) от посторонних (пока они не могут украсть сертификат (например, из взломанной почты или мессенджера) или подделать его).

comments powered by Disqus