Аутентификация пользователя сайта по сертификату
Оригинал: 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>
Доп. инфо: