Рецепты Nginx: basic авторизация с капчей / Хабр

Introduction

When setting up a web server, there are often sections of the site that you wish to restrict access to. Web applications often provide their own authentication and authorization methods, but the web server itself can be used to restrict access if these are inadequate or unavailable.

In this guide, we’ll demonstrate how to password protect assets on an Nginx web server running on Ubuntu 14.04.

1: установка инструментов apache

Для настройки аутентификации необходима команда htpassword. Эта программа включена в пакет apache2-utils. Установите этот пакет:

sudo apt-get install apache2-utils

2: создание учётных данных

Теперь нужно создать пароль.

Пароль и связанное с ним имя пользователя будут храниться в указанном вами файле. Пароль будет зашифрован. Файл можно назвать как угодно; в данном руководстве файл называется /etc/nginx/.htpasswd, имя пользователя – nginx.

Чтобы создать пароль, введите команду:

sudo htpasswd -c /etc/nginx/.htpasswd nginx

Проверьте содержимое нового файла; там появятся учётные данные.

cat /etc/nginx/.htpasswdnginx:$apr1$ilgq7ZEO$OarDX15gjKAxuxzv0JTrO/

3: обновление настроек nginx

Итак, теперь у вас есть учётные данные; настройте Nginx для их поддержки.

Step 4 — testing the setup

To apply the changes, first reload Nginx.

  1. sudo systemctl reload nginx

Глава 6. аутентификация – книга рецептов nginx

Задача

Вам требуется сделать безопасным ваше приложение или содержимое посредством базовой аутентификации HTTP.

Решение

Создайте некий файл с приводимым ниже форматом, в котором пароль зашифрован или хэширован в одном из допустимых
форматов:


# comment
name1:password1
name2:password2:comment
name3:password3
 	   

Значением имени пользователя является самое первое поле, в качетсве пароля выступает второе поле, а разделителем
является двоеточие. Существует необязательный третий параметр, который вы можете применять в качестве комментария для
каждого пользователя. NGINX способен понимать несколько различных форматов для паролей, причём один из них состоит в том
зашифрован ли пароль при помощи функции C crypt(). Эта функция выставляется в
командной строке через соотвествующую команду openssl passwdю Когда установлен
openssl, вы можете создавать строки зашифрованных паролей с использованием такой
команды:


$ openssl passwd MyPassword1234
 	   

Её выводом будет некая трока, которую NGINX способен применять в вашем файле паролей.

Для включения базовой аутентификации в рамках своей конфигурации NGINX применяйте имеющиеся директивы
auth_basic и auth_basic_user_file:


location / {
    auth_basic "Private site";
    auth_basic_user_file conf.d/passwd;
}
 	   

Вы можете применять директивы auth_basic в контекстах HTTP, server, location.
Эта директива auth_basic получает некий строковый параметр, который отображается
в базовом всплывающем окне аутентификации при появлении пользователя без аутентификации. Значение
auth_basic_user_file определяет путь к надлежащему файлу пользователей.

Обсуждение

Вы можете вырабатывать пароли базовой аутентификации несколькими способами в ряде различных форматов с отличающимися
степенями безопасности. Для выработки паролей также может применяться и команда
htpasswd из Apache. И команда openssl, и команда
htpasswd способны вырабатывать пароли с применением алгоритма apr1,
который также способен понимать и NGINX. Этот пароль также может быть в формате Соли SHA-1, который применяется в LDAP (Lightweight Directory Access Protocol) и Dovecot. NGINX поддерживает
дополнительные форматы и алгоритмы хэширования; однако многие из них рассматриваются как не безопасные, так как они запросто
могут быть побеждены силовой атакой в лоб.

Вы можете применять базовую аутентификацию для защиты своего содержимого всего хоста NGINX целиком, определённых
виртуальных серверов или даже конкретных блоков location. Базовая аутентификация на заменяет аутентификацию пользователя
для веб- приложения, но она способствует поддержанию безопасности частной информации.

Под своим капотом базовая аутентификация выполняется путём возврата самим сервером некого кода HTTP 401 unauthorized
с заголовком отклика WWW-Authenticate. Этот заголовок будет иметь некое значение
Basic realm="your string". Такой отклик вызывает у браузера
приглашение на ввод имени пользователя и пароля. Значения имени пользователя соединяются при помощи разделителя двоеточием,
после этого кодируются в base64 и затем отправляются в неком заголовке запроса с названием
Authorization. Этот заголовок запроса Authorization
определит Basic и зашифрованную строку user:password.
Сам сервер расшифровывает полученный заголовок и проверяет его по предоставленному файлу
auth_basic_user_file. По той причине, что строки имени пользователя и пароля шифруются
лишь base64, рекомендуется для базовой аутентификации использовать HTTPS.

Задача

У вас имеется система аутентификации стороннего производителя для которой вы бы и желали выполнять запросы на
аутентификацию.

Решение

Для осуществления запросов к внешней службе аутентификации на предмет проверки подлинности перед обслуживанием
полученного запроса воспользуйтесь модулем http_auth_request_module:


location /private/ {
    auth_request /auth;
    auth_request_set $auth_status $upstream_status;
}

location = /auth {
    internal;
    proxy_pass http://auth-server;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
}
 	   

Первая директива auth_request получает некий параметр URI, который обязан быть
неким локальным внутренним location. Следующая директива auth_request_set
позволяет вам устанавливать переменные из последующего подзапроса аутентиыфикации.

Обсуждение

Обсуждаемый http_auth_request_module делает возможной аутентификацию всех
обрабатываемых вашим сервером NGINX запросов. Этот модуль выполняет некий подзапрос прежде чем обслуживать первоисточник
для выяснения того имеет ли этот запрос доступ к запрашиваемому ресурсу. Весь первоначальный запрос целиком выставляется
в посредник (прокси) в location такого подзапроса. Имеющийся location аутентификации действует как типичный посредник для
получаемого подзапроса и отправляет сам первоначальный запрос, включая полученное тело первоначального запроса и заголовки.
Получаемое значение кода состояния HTTP данного подзапроса является тем, что определяет будет ли предоставлен доступ или
нет. Если этот подзапрос возвращает некий код состояния HTTP 200, данный процесс аутентификации успешен и сам запрос
выполняется. Если же наш подзапрос возвращает HTTP 401 или 403, это же значение будет возвращено в сам первоначальный
запрос.

Когда ваша служба аутентификации не запрашивает поступившее тело запроса, вы можете откинуть такое тело запроса
с помощью отражённой в примере директивыproxy_pass_request_body. Такая практика
снизит размер и время запроса аутентификации. Так как само тело отклика отбрасывается, его заголовок
Content-Length должен быть снабжён пустой строкой. Если ваша служба аутентификации
нуждается в знании того URI, который запрашивается данным запросом, вы пожелаете помещать это значение в неком
индивидуальном заголовке который будет проверять и подтверждать ваша служба аутентификации. Если имеются некие
моменты, которые вы бы хотели оставлять в своём подзапросе к службе аутентификации, например заголовки отклика или прочая
информация, вы можете воспользоваться директивой auth_request_set для создания
новых переменных из данных отклика.

Задача

Для применения в NGINX Plus вам требуется веб ключ JSON.

Решение

NGINX Plus применяется определённый в соответствующем RFC стандарте формат значения JWK (JSON Web Key). Этот стандарт
допускает внутри такого файла JWK некий массив объектов ключей.

Ниже приводится некий образец того как может выглядеть подобный файл ключей:


{"keys":
  [
    {
        "kty":"oct",
        "kid":"0001",
        "k":"OctetSequenceKeyValue"
    },
    {
        "kty":"EC",
        "kid":"0002"
        "crv":"P-256",
        "x": "XCoordinateValue",
        "y": "YCoordinateValue",
        "d": "PrivateExponent",
        "use": "sig"
    },
    {
        "kty":"RSA",
        "kid":"0003"
        "n": "Modulus",
        "e": "Exponent",
        "d": "PrivateExponent"
    }
  ]
}
 	   

Приведённый файл JWK демонстрирует определённое дерево изначальных типов ключей, упомянутых в самом стандарте
RFC. Описание формата этих ключей также является частью указанного стандарта RFC. Значением атрибута
kty тип ключа. Данный файл отображает три типа ключей: Последовательность
октетов (oct, Octet Sequence), координаты Эллиптической кривой
(EC, EllipticCurve) и значение типа RSA.
Значением атрибута kid является идентификатор ключа. Для получения дополнительных
сведений относительно этих стандартов обращайтесь к соответствующей документации RFC.

Обсуждение

Для генерации необходимого веб ключа JSON существует множество доступных во многих разнообразных языках
программирования библиотек. Рекомендуется создать некую службу ключа, которая выступает центом авторизации JWK для
создания и ротации ваших JWK через систематические интервалы. Для дополнительной безопасности рекомендуется делать ваши
JWK настолько же безопасными, как и ваши сертификаты SSL/ TLS. Обезопасьте свой файл ключей соответствующими полномочиями
пользователя и группы. Наилучшей практикой будет удержание их в оперативной памяти. Вы можете делать это создавая некую
файловую систему в памяти, подобную ramfs. Ротация ключей через систематические интервалы также важно; в качестве варианта
вы можете создать некую службу ключей, которая создаёт общедоступные и частные ключи и предлагает их имеющимся
приложениям и NGINX через некий API.

Также ознакомьтесь

RFC standardization documentation of JSON Web Key

Задача

Вы желаете разгрузить для NGINX Plus удостоверения аутентификации OpenID Connect.

Решение

Для безопасности location или server, а также указания директиве auth_jwt
применять в качестве маркера (token) $cookie_auth_token для удостоверения применяйте
модуль JWT, который поставляется совместно с NGINX Plus:


location /private/ {
    auth_jwt "Google Oauth" token=$cookie_auth_token;
    auth_jwt_key_file /etc/nginx/google_certs.jwk;
}
 	   

Данная конфигурация направляет NGINX Plus защищать свой путь URI /private/
при помощи удостоверения через JWT. Google OAuth 2.0 OpenID Connect применяет значение ключа
auth_token вместо установленного по умолчанию однонаправленного маркера.
Таким образом вы должны указать NGINX о необходимости поиска значения данного маркера (token) в данном куки вместо
использования установленного по умолчанию в самом NGINX Plus location. Местоположение
auth_jwt_key_file настраивается на некий произвольный путь, что является неким
этапом, который мы обсуждаем в рецепте
Получение значения веб ключей JSON из Google.

Обсуждение

Данная конфигурация демонстрирует как вы можете выполнять удостоверение Google OAuth 2.0 OpenID Connect JSON Web
Token через NGINX Plus. Обсуждаемый модуль аутентификации JWT NGINX Plus для HTTP способен выполнять удостоверение
любого JWT, который следует указаниям спецификации RFC for JSON Web Signature, что незамедлительно делает возможными
любые полномочия SSO которые применяют JSON Web Tokens для удостоверения на уровне NGINX Plus. Сам протокол OpenID 1.0
является неким уровнем поверх протокола аутентификации OAuth 2.0, который добавляет идентификацию, позволяя такое применение
JWT для доказательства необходимой идентификации определённого пользователя отправляя соответствующий запрос.
Имея подпись конкретного маркера NGINX Plus способен удостоверить что такой маркер не был изменён так как он был подписан.
Тем самым Google применяет некий асинхронный метод подписи и позволяет распространять общедоступные JWK отслеживая их
частные ключи безопасности JWK.

NGINX Plus способен также контролировать для OpenID Connect 1.0 Поток кода авторизации, включая NGINX Plus в качестве
ретранслятора для OpenID Connect. Такая возможность делает возможной интеграцию с большей частью поставщиков идентификации,
включая CA Single Sign On (ранее SiteMinder), ForgeRock OpenAM, Keycloak, Okta, OneLogin и Ping Identity. Для получения
дополнительных сведений о реализации NGINX Plus в качестве ретранслятора аутентификации OpenID Connect обратитесь к
NGINX Inc OpenID Connect GitHub Repository

Также ознакомьтесь

Detailed NGINX Blog on OpenID Connect

OpenID Connect

Настройка авторизации nginx

Итак, необходимый файл паролей готов. Теперь нужно настроить веб-сервер Nginx для проверки этого файла перед обслуживанием закрытого контента.

Откройте конфигурационный файл блока server (так называется виртуальный хост Nginx) для сайта, доступ к контенту которого нужно ограничить. В данном примере используется стандартный блок default, который поставляется в Ubuntu по умолчанию.

sudo nano /etc/nginx/sites-enabled/default

Раскомментированный файл выглядит так:

server {listen 80 default_server;listen [::]:80 default_server ipv6only=on;root /usr/share/nginx/html;index index.html index.htm;server_name localhost;location / {try_files $uri $uri/ =404;}}

Чтобы настроить авторизацию, нужно выбрать контекст ограничения доступа. Веб-сервер Nginx позволяет ограничивать доступ к контенту на уровне сервера или же по определённому адресу. В данном примере мы ограничим доступ к каталогу document root при помощи блока location (при необходимости укажите другой каталог).

Рецепты nginx: basic авторизация с капчей

Для приготовления авторизации с капчей нам понадобится сам

nginx

и его плагины

encrypted-session

,

form-input

,

ctpp2

,

echo

,

headers-more

,

auth_request

,

auth_basic

,

set-misc

. (Я дал ссылки на свои форки, т.к. делал некоторые изменения, которые пока не удалось пропихнуть в оригинальные репозитории. Можно также воспользоваться

готовым образом

.)

Для начала зададим

encrypted_session_key "abcdefghijklmnopqrstuvwxyz123456";

Дальше, на всякий случай, отключаем авторизационный заголовок

more_clear_input_headers Authorization;

Теперь защищаем всё авторизацией

auth_request /auth;
location =/auth {
    internal;
    subrequest_access_phase on; # разрешаем авторизационную фазу в подзапросе
    auth_request off; # не использовать авторизацию
    set_decode_base64 $auth_decode $cookie_auth; # раскодируем авторизационную куку
    set_decrypt_session $auth_decrypt $auth_decode; # расшифровываем авторизационную куку
    if ($auth_decrypt = "") { return 401 UNAUTHORIZED; } # если не удалось расшифровать, то значит пользователь не авторизован
    more_set_input_headers "Authorization: Basic $auth_decrypt"; # подменить авторизацию на basic (чтобы использовать переменную $remote_user)
    auth_basic_user_file /data/nginx/.htaccess; # задаём файл basic авторизации
    auth_basic Auth; # включаем basic авторизацию
    echo -n OK; # пользователь авторизован
}

Для авторизованных пользователей показываем контент из их папки

location / {
    alias html/$remote_user/;
}

А при отсутствии авторизации показываем авторизационную форму с капчей

error_page 401 = @error401;
location @error401 {
    set_escape_uri $request_uri_escape $request_uri; # кодируем запрос
    return 303 /login?request_uri=$request_uri_escape; # перенаправляем на авторизационную форму с капчей, сохранив запрос
}
location =/login {
    default_type "text/html; charset=utf-8"; # задаём тип
    if ($request_method = GET) { # если только показать авторизационную форму с капчей
        template login.html.ct2; # задаём шаблон
        ctpp2 on; # включаем шаблонизатор
        set_secure_random_alphanum $csrf_random 32; # задаём случайное csrf
        encrypted_session_expires 300; # задаём время жизни csrf 5 минут (5 * 60 = 300)
        set_encrypt_session $csrf_encrypt $csrf_random; # зашифровываем случайное csrf
        set_encode_base64 $csrf_encode $csrf_encrypt; # кодируем зашифрованное csrf
        add_header Set-Cookie "CSRF=$csrf_encode; Max-Age=300"; # помещаем зашифрованное csrf в куку на 5 минут (5 * 60 = 300)
        return 200 "{"csrf":"$csrf_random"}"; # возвращаем json для шаблонизатора
    } # иначе - обработать авторизационную форму с капчей
    set_form_input $csrf_form csrf; # получаем csrf из формы
    set_unescape_uri $csrf_unescape $csrf_form; # раскодируем csrf из формы
    set_decode_base64 $csrf_decode $cookie_csrf; # раскодируем csrf из куки
    set_decrypt_session $csrf_decrypt $csrf_decode; # расшифровываем csrf из куки
    if ($csrf_decrypt != $csrf_unescape) { return 303 $request_uri; } # если csrf из формы не совпадает с csrf из куки, то перенаправить на показ формы снова
    set_form_input $captcha_form captcha; # получаем капчу из формы
    set_unescape_uri $captcha_unescape $captcha_form; # раскодируем капчу из формы
    set_md5 $captcha_md5 "secret${captcha_unescape}${csrf_decrypt}"; # считаем md5
    if ($captcha_md5 != $cookie_captcha) { return 303 $request_uri; } # если md5 не совпадает с капчей из куки, то перенаправить на показ формы снова
    set_form_input $username_form username; # получаем логин из формы
    set_form_input $password_form password; # получаем пароль из формы
    set_unescape_uri $username_unescape $username_form; # раскодируем логин из формы
    set_unescape_uri $password_unescape $password_form; # раскодируем пароль из формы
    encrypted_session_expires 2592000; # задаём время жизни сессии 30 дней (30 * 24 * 60 * 60 = 2592000)
    set $username_password "$username_unescape:$password_unescape"; # задаём basic авторизацию
    set_encode_base64 $username_password_encode $username_password; # кодируем basic авторизацию
    set_encrypt_session $auth_encrypt $username_password_encode; # зашифровываем basic авторизацию
    set_encode_base64 $auth_encode $auth_encrypt; # кодируем зашифрованную basic авторизацию
    add_header Set-Cookie "Auth=$auth_encode; Max-Age=2592000"; # помещаем зашифрованную basic авторизацию в авторизационную куку на 30 дней (30 * 24 * 60 * 60 = 2592000)
    set $arg_request_uri_or_slash $arg_request_uri; # копируем запрос из аргумента
    set_if_empty $arg_request_uri_or_slash "/"; # если аргумент не задан, то начало
    set_unescape_uri $request_uri_unescape $arg_request_uri_or_slash; # раскодируем запрос
    return 303 $request_uri_unescape; # перенаправляем на сохранённый запрос
}

login.html

<html>
    <body>
        <form method="post">
            <input type="hidden" name="csrf" value="<TMPL_var csrf>" />
            username: <input type="text" name="username" placeholder="Enter User Name..." /><br />
            password: <input type="password" name="password" /><br />
            captcha: <img src="/captcha?csrf=<TMPL_var csrf>"/><input type="text" name="captcha" autocomplete="off" /><br />
            <input type="submit" name="submit" value="submit" />
        </form>
    </body>
</html>

Создание файла паролей

Для начала нужно создать файл для хранения паролей тех пользователей, которые могут пройти авторизацию на сайте. Для этого можно использовать утилиты OpenSSL или же специальную утилиту htpasswd, которая входит в пакет apache2-utils.

Примечание: На Nginx файлы паролей используют тот же формат, что и на Apache.

Выберите наиболее подходящий метод и следуйте инструкциям соответствующего раздела.

Создание файла паролей при помощи .htpasswd

Несмотря на то, что OpenSSL отлично справляется с поддержкой файла паролей для Nginx, большинство пользователей предпочитает использовать для этих целей утилиту htpasswd, которая находится в пакете apache2-utils.

Чтобы установить пакет утилит apache2-utils, введите:

sudo apt-get updatesudo apt-get install apache2-utils

Теперь на сервере доступна команда htpasswd, которая позволяет создать файл паролей, необходимый серверу Nginx для авторизации пользователей. Создайте скрытый файл .htpasswd в каталоге /etc/nginx.

При первом запуске этой утилиты нужно использовать флаг –c, который и создаёт необходимый файл. Укажите имя пользователя в конце команды, чтобы создать новую запись в файле:

sudo htpasswd -c /etc/nginx/.htpasswd 8host

Примечание: Замените условное имя 8host именем своего пользователя.

Программа предложит создать и подтвердить пароль для этого пользователя.

Чтобы добавить в файл паролей других пользователей, просто используйте команду htpasswd без флага –с:

Тестирование авторизации

Чтобы убедиться, что нужные каталоги защищены паролем, попробуйте получить доступ к закрытому контенту в браузере. Если настройка прошла успешно, на экране должна появиться форма авторизации, предлагающая ввести имя пользователя и пароль:

Требования

Для выполнения руководства нужен не-root пользователь с правами sudo. Чтобы создать такой аккаунт, обратитесь к этому руководству.

Если сервер Nginx не был установлен ранее, установите его сейчас при помощи команд:

sudo apt-get updatesudo apt-get install nginx

Conclusion

You should now have everything you need to set up basic authentication for your site. Keep in mind that password protection should be combined with SSL encryption so that your credentials are not sent to the server in plain text. To learn how to create a self-signed SSL certificate to use with Nginx, follow this guide. To learn how to install a commercial certificate, follow this guide.

Заключение

Теперь контент сайта защищён паролем. Обратите внимание: защиту паролем следует комбинировать с шифрованием SSL, в противном случае учетные данные будут передаваться  на сервер в виде простого текста, а это очень серьёзная уязвимость для безопасности.

Примечание: Чтобы узнать, как создать SSL-сертификат для Nginx, читайте это руководство.

Tags:

Похожее:  Univention Corporate Server (UCS) — установка простого и удобного LDAP сервера с web-панелью и его связка с Nextcloud / Хабр

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *