Аутентификация пользователя сайта по сертификату

Оригинал: http://sipb.mit.edu/doc/apache-client-certs/

Иногда для некоторых систем (например, банковских) наличие пары «логин-пароль» не является достаточно надежным средством аутентификации, поэтому обычно используют то, что посильнее и поддерживается любым браузером, а именно — SSL-сертификаты. Выданный пользователю сертификат импортируется в цепочку ключей рабочей системы и при осуществлении входа в приложение производится его выбор в качестве элемента идентификации пользователя.

OpenSSL SHA256 setup

Прежде, чем гнуть пальцы, надо правильно настроить openssl, чтобы он использовал приличное шифрование SHA256, а не босяцкое SHA1. Делается это в настройках Openssl:

cat /etc/openssl.cnf
[ CA_default ]
default_md  = sha256    # Change from sha1
...
[ req ]
req_extensions = v3_req     # Uncomment

Теперь серты, несмотря на их самоподписанность, не будут выглядеть лажово.

Создание CA

Для того, чтобы нам начать выдавать сертификаты пользователям нашей системы, необходимо сформировать корневую пару ключей (CA) и сертификат сервера, который также будет проверяться на клиентской стороне. Это делается следующей последовательностью команд:

#!/bin/bash
# openssl genrsa -des3 -out ca.key 4096
# строка выше будет требовать пароля для шифрования 3des. Чтобы обойтись без пароля, убираем "des3", как ниже:
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 365 -key ca.key -out ca.crt

Соответственно, полученный CA.crt может быть импортирован на стороне клиента как доверенный корневой сертификат, что избавит пользователей от назойливых предупреждений о нарушении безопасности. :)

Сертификат сервера

openssl genrsa -out server.key 1024
openssl req -new -key server.key -out server.csr
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

Server.key и Server.crt будут использованы для конфигурации сервера, о чем еще поговорим.

Пользовательские сертификаты

Теперь необходимо снабдить всех наших пользователей сертификатами, которые они будут использовать для аутентификации:

make_client_p12.sh
#!/bin/sh
# usage: make_client_p12.sh first_user_number last_user_number
if [ $# -eq 2 ]
        then
        if [ $1 -lt 2 ]
                then
                        echo "First user number MUST be greater then 1, as #1 is reserved for server"
                        exit 1
        fi
        else
                echo "                Example:
        make_client_p12.sh 2 35
                This will create users certificates from 2 to 35"
                exit 1
fi
 
on_generate()
{
openssl genrsa -out user$USER_NUMBER.key 1024
openssl req -new -key user$USER_NUMBER.key -out user$USER_NUMBER.csr -subj /C=RU/ST=MSK/L=Khimki/O=User$USER_NUMBER/OU=Wiki\ user\ $USER_NUMBER/CN=wiki.chroot.ru/emailAddress=user$USER_NUMBER@chroot.ru/
openssl x509 -req -days 365 -in user$USER_NUMBER.csr -CA ca.crt -CAkey ca.key -set_serial $USER_NUMBER -out user$USER_NUMBER.crt
openssl pkcs12 -export -out user$USER_NUMBER.pfx -inkey user$USER_NUMBER.key -in user$USER_NUMBER.crt -certfile ca.crt
}
 
USER_NUMBER=$1
while [ $USER_NUMBER -le $2 ]
do
on_generate $USER_NUMBER
USER_NUMBER=$(($USER_NUMBER+1))
done

На выходе получаем *.pfx, который можно отдавать пользователю (под честное слово). Единственный нюанс — параметр set_serial должно быть уникальным в рамках CA. Т.е. в нашем случае начиная с 02 (01 зарезервирован за сервером) и так далее.

Настройки Nginx

Итак, все теперь имеют по сертификату и даже сумели установить их себе в браузеры (эту отдельную и очень непростую тему мы оставим на самостоятельное изучение). Осталось настроить наш любимый nginx таким образом, чтобы он пускал только доверенных пользователей и не пускал остальных. Это, в свою очередь, тоже не представляет особой сложности:

server {
    listen 443 ssl;
    server_name private.example.com;
    root /var/www/private;
 
    ssl_certificate /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.pem;
    ssl_client_certificate /etc/nginx/certs/ca.crt;
 
    ssl_verify_client on;
}

Поздравляю. Почти все готово. Впрочем, если Вы ввели пароль для секретного ключа сервера — не жалуйтесь, что при каждом перезапуске nginx-а необходимо будет оный пароль вводить. Ибо безопасность; иначе самую первую строку самого первого скрипта надо было писать без -des3.

Источники вдохновения:

Настройки Apache

В конфиг апача (./extra/httpd_vhosts.conf или ./extra/httpd_ssl.conf ) вставляется такое:

## Client Verification
    SSLVerifyClient require
#   SSLVerifyClient optional
    SSLVerifyDepth 3
    SSLCADNRequestPath /etc/ssl/cert_issuer.domain/

# error handling
    RewriteEngine        on
    RewriteCond     %{SSL:SSL_CLIENT_VERIFY} !=SUCCESS
    RewriteRule     .? - [F]
    ErrorDocument 403 "You need a client side certificate issued by Site-authorized CA center to access this site"

А чтобы вообще работал SSL, а основном конфиге включаем следующие модули:

LoadModule socache_shmcb_module libexec/apache24/mod_socache_shmcb.so
LoadModule authn_socache_module libexec/apache24/mod_authn_socache.so
LoadModule ssl_module libexec/apache24/mod_ssl.so
Include extra/httpd-ssl.conf

Указываем ему путь к генератору случайных чисел:

SSLRandomSeed startup file:/dev/urandom 512

При этом, в папке /etc/ssl/cert_issuer.domain/ нужны только сертификаты “Центра сертификации” - т.е. ca.key и CA.crt, полученный на первом этапе и использовавшийся для создания “клиентских” сертификатов на втором этапе.

Можно защищать страницы от части осертификаченых юзеров с помощью .htaccess, в которых указан

AuthType SSLCert
Require user username1 username2

См. https://scripts.mit.edu/faq/15 Здесь еще описан метод аутентификации по списку (в отдельном файле). Но можно использовать CRL (я не экспериментировал)

Test with CGI

Хороший пример реализации нагло стырен с http://linuxconfig.org/apache-web-server-ssl-authentication Использует CGI.

В директорию /usr/local/www/apache24/cgi-bin (которая прописана как alias для cgi-bin в конфиге httpd.conf, втыкается следующий скрипт:

test.cgi
#!/usr/local/bin/perl
use strict;
print "Content-type: text/html\r\n\r\n";
print "Cert ";
print $ENV{"SSL_CLIENT_S_DN_CN"};
print " ";
print $ENV{"SSL_CLIENT_S_DN_O"};

И оно при заходе на наш сервер на страницу /cgi-bin/test.cgi отдаст браузеру полученые из сертификата имя сервера и имя владельца сертификата

Чтобы оно работало (связка apache 2.4 + CGI + FreeBSD) в конфиге апача пишутся (или раскоментируются) следующие блоки:

LoadModule cgi_module libexec/apache24/mod_cgi.so
<IfModule alias_module>
    ScriptAlias /cgi-bin/ "/usr/local/www/apache24/cgi-bin/"
</IfModule>
<Directory "/usr/local/www/apache24/cgi-bin">
    AllowOverride None
    SSLOptions +StdEnvVars
    Options +ExecCGI # -MultiViews +SymLinksIfOwnerMatch #optional parameters
    Require all granted
</Directory>

Также проверяется, что первая (закоментированная и экскламированная) строка строка скрипта показывала на метонахождение perl, которе можно получить из команды which perl. Ибо при ошибке в этой строке оно будет грязно ругаться на внутреннюю ошибку сервера.

Теория о том, как сделать, чтобы оно брало имена юзеров из сертов и персонифицировало (через базу?) со входом в “домашний кабинет”, описано http://wiki.cacert.org/ApacheServerClientCertificateAuthentication

И где втыкать

    <login-config>
        <auth-method>CLIENT-CERT</auth-method>
    </login-config>

Доп. инфо:

unix/apache/apache_client_cert_auth.txt · Last modified: 2017/05/04 20:16 by rybario
About this template
CC Attribution-Share Alike 4.0 International
Powered by PHP Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0 Valid HTML5