Много где написано о том, как получить 100% и A+ по тесту от Qualys. При всём при том практически везде директивы ssl_ciphers
и подобные даются как эдакие магические строки, которые нужно просто вставить, и надеяться, что автор не подводит вас под монастырь. Эта статья призвана исправить это недоразумение. По прочтению этой статьи директива ssl_ciphers
потеряет для вас всякую магию, а ECDHE и AES будут как друзья да братья.
Подготовка
Работать будем с Debian 8.7. Если у вас другой дистрибутив, то версии должны быть такие же или более новые.
# lsb_release -d
Description: Debian GNU/Linux 8.7 (jessie)
# openssl version
OpenSSL 1.0.1t 3 May 2016
# nginx -V
nginx version: nginx/1.6.2
Настроим nginx для получения сертификатов от Let's Encrypt по инструкции, но только до получения сертификата для нашего домена.
certbot certonly -d example.com -d www.example.com
Конфиг используем минимальный. Ничего лишнего. Всё по умолчанию.
server {
server_name www.example.com example.com;
listen 443 ssl default_server;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
}
Теперь прогоним тест для этого сервера.
С настройками по умолчанию результат так себе. На четвёрку, если по-нашему. Ругаются на слабые параметры для обмена ключами по алгоритму Диффи — Хеллмана (далее просто DH).
Можно было бы сделать усиленные параметры, но такие параметры и скорости не добавляют, и некоторыми старыми клиентами поддерживаются. Те же старые клиенты поддерживают и протокол DH на эллиптических кривых (ECDHE), значит мы ничего не теряем и даже приобретаем в скорости, если вслед за Google, Facebook, Mozilla и CloudFlare полностью откажемся от медленных EDH шифров в пользу ECDHE.
Дальше просто. Нужен список рекомендованных шифров.
Для сертификата с ключем RSA из того списка нужны шифры только с проверкой подлинности через RSA.
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-RSA-AES128-SHA
ECDHE-RSA-AES256-SHA
ECDHE-RSA-AES128-SHA256
ECDHE-RSA-AES256-SHA384
DHE-RSA-AES128-GCM-SHA256
DHE-RSA-AES256-GCM-SHA384
DHE-RSA-AES128-SHA
DHE-RSA-AES256-SHA
DHE-RSA-AES128-SHA256
DHE-RSA-AES256-SHA256
Из них исключим шифры слабее 256 бит, и шифры DHE. Перечислим оставшиеся в конфигурационной директиве.
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384;
Но одного этого будет недостаточно. Нужен ключ минимум на 4096 бит.
certbot certonly --renew-by-default --rsa-key-size 4096 -d example.com -d www.example.com
И другая кривая. В используемой версии OpenSSL можно выбрать только одну. Выберем самую надёжную из популярных.
ssl_ecdh_curve secp384r1;
Для оценки с плюсом добавим HSTS.
add_header Strict-Transport-Security "max-age=31536000";
Наконец, отключим старые протоколы для 100% оценки по графе Protocol Support.
ssl_protocols TLSv1.2;
Полный конфиг получается следующий.
server {
server_name www.example.com example.com;
listen 443 ssl default_server;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384;
ssl_ecdh_curve secp384r1;
add_header Strict-Transport-Security "max-age=31536000";
ssl_protocols TLSv1.2;
}
Проверим, что никакой ошибки в настройке шифров и протоколов нет.
$ nmap --script ssl-enum-ciphers -p 443 example.com
Starting Nmap 7.40 ( https://nmap.org ) at 2017-03-01 00:00 UTC
Nmap scan report for example.com (1.2.3.5)
Host is up (0.030s latency).
PORT STATE SERVICE
443/tcp open https
| ssl-enum-ciphers:
| TLSv1.2:
| ciphers:
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp384r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp384r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp384r1) - A
| compressors:
| NULL
| cipher preference: server
|_ least strength: A
Nmap done: 1 IP address (1 host up) scanned in 3.22 seconds
Шифры на месте, хоть и не в том порядке. Но это ожидаемо. Можно запускать тест.
Оценка пять с плюсом. Счастье, радость. Казалось бы.
Недостатки
Но есть одна проблема. Если результаты теста прокрутить до раздела Handshake Simulation, то мы увидим что на наш сайт не попадут пользователи IE любых версий младше 11, Java до SE 8, Android до версии 4.3 и даже некоторые версии Safari. Про софт, собранный с OpenSSL версии младше 0.9.8, даже и не говорю.
Есть и ещё одна проблема: производительность. Наш ключ на 4096 бит непосредственно влияет на скорость установки соединения, при этом не слишком усиливает защиту. Шифры на 256 бит тоже не помогают ускорить соединеие, что касается мобильных устройств с маломощными процессорами и прочих клиентов без AES-NI (аппаратного ускорения).
В случае RSA легко убедиться в негативном влиянии на скорость, используя встроенный в OpenSSL тест.
openssl speed rsa2048 rsa4096
На маломощном Celeron 1007U разница очевидна.
sign verify sign/s verify/s
rsa 2048 bits 0.002381s 0.000071s 420.0 14051.0
rsa 4096 bits 0.016790s 0.000260s 59.6 3852.8
Подпись на стороне сервера делается в семь раз медленнее. Проверка подписи на клиенте — в 3.6 раза медленнее. На другом железе тенденция остаётся та же. Этот проигрыш в скорости — плата за усиление стойкости шифра на какие-то 16%.
Аналогичным образом проверим скорость рекомендуемых шифров на OpenSSL 1.1.0e.
for cipher in aes-128-gcm aes-256-gcm chacha20-poly1305
do openssl speed -decrypt -evp $cipher 2>/dev/null | grep ^$cipher
done | column -t
На Celeron 1007U шифр ChaCha20/Poly1305 будет в два раза быстрее AES-128, а последний на треть быстрее AES-256. На Core i7 с AES-NI картина другая: ChaCha20/Poly1305 будет медленнее AES-128 на треть, а AES-256 лишь немного медленнее AES-128. При шифровании картина примерно та же.
Обмен ключами с используемой по умолчанию кривой prime256v1
(она же secp256r1
или NIST P-256) будет существенно быстрее, чем с усиленной кривой secp384r1
(NIST P-384).
$ openssl speed ecdsap256 ecdsap384 ecdhp256 ecdhp384
sign verify sign/s verify/s
256 bit ecdsa (nistp256) 0.0001s 0.0003s 7923.8 3214.4
384 bit ecdsa (nistp384) 0.0006s 0.0023s 1756.6 430.1
op op/s
256 bit ecdh (nistp256) 0.0002s 4893.5
384 bit ecdh (nistp384) 0.0019s 525.9
Работа с P-384 требует в 7-9 раз больше времени в зависимости от операции.
Если сравнить ECDSA и RSA, то видно что в случае ECDSA вычислительная нагрузка ложится больше на клиента, чем на сервер.
Если вы захотите повторить тесты у себя, то перед тестами стоит поднять приоритет текущего процесса.
sudo renice -1 $$
Очевидно одно: к выбору шифров и параметров нужно подходить более взвешено.
Принцип поиска шифров
Перечисление шифров списком — не лучшая идея потому что уже скоро можно будет штатно, без перекомпиляции и сторонних источников, использовать ChaCha20/Poly1305 в nginx. С явным заданием шифров сервер не будет использовать любые новые виды шифрования без перенастройки. Это не то, о чем вы хотите думать каждый день.
Будет лучше выбирать шифры по ключевым словам или тегам, каждое из которых соответствуют какой-то группе шифров. Ключевые слова в разных версиях OpenSSL отличаются, но мы всегда можем их проверить.
Например, тег EECDH соответствует всем шифрам с обменом одноразовыми (эфемерными) ключами по алгориму DH с эллиптическими кривыми.
$ openssl ciphers -v 'EECDH' | column -t
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1
ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256
ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256
ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1
ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1
ECDHE-RSA-RC4-SHA SSLv3 Kx=ECDH Au=RSA Enc=RC4(128) Mac=SHA1
ECDHE-ECDSA-RC4-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=RC4(128) Mac=SHA1
ECDHE-RSA-DES-CBC3-SHA SSLv3 Kx=ECDH Au=RSA Enc=3DES(168) Mac=SHA1
ECDHE-ECDSA-DES-CBC3-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=3DES(168) Mac=SHA1
ECDHE-RSA-NULL-SHA SSLv3 Kx=ECDH Au=RSA Enc=None Mac=SHA1
ECDHE-ECDSA-NULL-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=None Mac=SHA1
Шифры показываются в списке согласно приоритету: клиент выберет первый подходщящий шифр, просматривая список в указанном порядке.
Если бы у нас была самая последняя версия OpenSSL, то для получения того же списка мы бы использовали понятный тег ECDHE, дающий тот же самый список и соответствующий префиксу шифров. В текущей версии такого тега нет, потому используем какой есть.
В полученном списке бросаются в глаза шифры без, собственно, шифрования, которые идут с отметкой Enc=None
. Исключим такие шифры, дающие только аутентификацию. Заодно исключим шифры без аутентификации.
openssl ciphers -v 'EECDH:!aNULL:!eNULL'
Для полного и безвозратного исключения теги групп aNULL и eNULL c неподходящими шифрами упомянуты с отрицанием. В списке не останется шифров с Au=None
или с Enc=None
.
Теги можно сочетать, получая пересечения множеств шифров. Получим шифры, сочетающие ECDHE, AES-256 и GCM.
$ openssl ciphers -v 'EECDH+AES256+AESGCM' | column -t
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
Группы шифров можно переместить ниже по приоритету, указав с плюсом в начале. Понизим приоритет у шифров с AES-256, не удаляя их.
$ openssl ciphers -v 'EECDH+aRSA+AES:+AES256' | column -t
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256
ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1
Можно временно убрать какую-то группу шифров, чтобы потом добавить её в другом виде. Исключим из всех ECDHE шифров шифры с 3DES, а затем вернём их обратно, но только в сочетании с обменом ключами RSA. Такие слабые шифры должны оказаться в самом конце списка.
$ openssl ciphers -v 'EECDH:-3DES:RSA+3DES' | tail -1 | column -t
DES-CBC3-SHA SSLv3 Kx=RSA Au=RSA Enc=3DES(168) Mac=SHA1
Выбираем шифры
Теперь у нас есть всё, чтобы составить список шифров для nginx, которые не будет требовать дополнения при выходе новых версий OpenSSL, теряя старые шифры и приобретая новые без какого-либо участия с нашей стороны. Мы учём необходимость поддержки старых браузеров и требования к скорости.
Начнём с получения уже используемого нам списка шифров, но не будем исключать шифры для сертификатов c EC на случай если мы когда-нибудь захотим использовать такой вид сертификатов.
$ openssl ciphers -v 'EECDH+AES256' | column -t
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1
ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
На деле нам нужнен не столько конкретно AES, а сколько не нужны слабые шифры 3DES и RC4. Последний исключим полностью и навсегда.
$ openssl ciphers -v 'EECDH:-3DES:!NULL:!RC4' | column -t
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1
ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256
ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256
ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1
ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1
Отдадим приоритет более быстрым шифрам, понизив его для AES-256. Совсем удалять AES-256 не будем на случай если кому-то очень будет нужен именно AES-256. (Добавочные биты у него — воображаемые.)
$ openssl ciphers -v 'EECDH:+AES256:-3DES:!NULL:!RC4' | column -t
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256
ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256
ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1
ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1
ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
Такая строка шифров не исключит ChaCha20/Poly1305 в следующей версии OpenSSL.
$ openssl version; openssl ciphers -v 'EECDH:+AES256:-3DES:!NULL:!RC4' | column -t | head -n 6
OpenSSL 1.1.0e 16 Feb 2017
ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH Au=ECDSA Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH Au=RSA Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES128-CCM8 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESCCM8(128) Mac=AEAD
ECDHE-ECDSA-AES128-CCM TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESCCM(128) Mac=AEAD
Для некоторых устаревших клиентов вернём AES без эфемерных ключей.
$ openssl ciphers -v 'EECDH:+AES256:-3DES:RSA+AES:!NULL:!RC4' | column -t
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256
ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256
ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1
ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1
ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
AES256-GCM-SHA384 TLSv1.2 Kx=RSA Au=RSA Enc=AESGCM(256) Mac=AEAD
AES256-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA256
AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1
AES128-GCM-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AESGCM(128) Mac=AEAD
AES128-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA256
AES128-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA1
Наконец, вернём 3DES без эфемерных ключей чисто для IE8/XP. Помочь IE6/XP никак нельзя — он по умолчанию не поддерживает TLS.
$ openssl ciphers -v 'EECDH:+AES256:-3DES:RSA+AES:RSA+3DES:!NULL:!RC4' | column -t
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256
ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256
ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1
ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1
ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
AES256-GCM-SHA384 TLSv1.2 Kx=RSA Au=RSA Enc=AESGCM(256) Mac=AEAD
AES256-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA256
AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1
AES128-GCM-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AESGCM(128) Mac=AEAD
AES128-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA256
AES128-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA1
DES-CBC3-SHA SSLv3 Kx=RSA Au=RSA Enc=3DES(168) Mac=SHA1
Итого
С шифрами закончили. Для поддержки старых клиентов нам нужно вернуть младшие версии TLS и, для скорости, использовать обычную кривую и сертификат на 2048 бит.
server {
server_name www.example.com example.com;
listen 443 ssl default_server;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
add_header Strict-Transport-Security "max-age=31536000";
ssl_ciphers EECDH:+AES256:-3DES:RSA+AES:RSA+3DES:!NULL:!RC4;
}
С таким конфигом мы получаем ровно такой результат как у Google на начало 2017 года.
При этом мы точно знаем что у всех клиентов сайт открывается настолько быстро, насколько это возможно без использования слабых шифров: все современные браузеры используют ECDHE.
Также вам обязательно нужно настроить OCSP stapling и, опционально, кеш TLS сессий.
Комментарии (88)
kay
30.03.2017 10:05+1Каждый год одно и то же :) Однако спасибо за информацию по производительности.
Вместо тысячи слов: https://mozilla.github.io/server-side-tls/ssl-config-generator/
grossws
30.03.2017 10:42И https://wiki.mozilla.org/Security/Server_Side_TLS где описаны ciphersuites для поддержки разных наборов браузеров.
alexkbs
30.03.2017 10:56Вы сами-то пробовали делать всё по рекомендациям там? В типа "совместимом" варианте там сразу после разных вариантов ECDHE идёт DHE-RSA-AES128. С этим шифром вы либо потеряете Java 6u45, либо получите оценку B по тесту. Не совместимость, а смех какой-то.
grossws
30.03.2017 11:03Я базируюсь на их intermediate профиле, а не compatible. Но не суть.
Про совместимость с jre есть отдельные приколы, которые всё равно требуют вмешательства со стороны клиентов на java. Например, если удаленный сервер использует dh_params на 4 килобита (а таких после прошлых публичных воплей про слабые dh_params стало много), надо явно исключать часть алгоритмов в коде.
alexkbs
30.03.2017 11:07Ну вот, пойдите и доказывайте Яндекс.Деньгам что им нужно в коде что-то там убрать чтобы вы смогли принимать платежи на вашем сайте. Как думаете, сможете убедить?
grossws
30.03.2017 11:13Интеграция с легаси — отдельное развлечение. Они, извините, SNI не поддерживают, и это в 2017 году.
idmrty
31.03.2017 08:46-2Вменять в вину отсутствие поддержки SNI можно браузеру, а для сервера это только плюс.
idmrty
31.03.2017 08:52Упс, ссылку не читал, моя вина. Тут ЯД как раз «браузер». Но странность там всё-таки есть: «Доступ по HTTPS от Cloudflare или Let's Encrypt работает по технологии SNI». С Cloudflare понятно, но про Let's Encrypt-то — как сертификат завязан на технологию сервера?
grossws
31.03.2017 14:26+2Вменять в вину отсутствие поддержки SNI можно браузеру, а для сервера это только плюс.
Когда в современном мире сервер не поддерживает SNI — это как минимум странно. Большинство его поддерживает не менее 5 (sic!) лет, некоторые уже более 10. Какой вы видите плюс в отсутствии поддержки SNI сервером?
«Доступ по HTTPS от Cloudflare или Let's Encrypt работает по технологии SNI». С Cloudflare понятно, но про Let's Encrypt-то — как сертификат завязан на технологию сервера?
Никак, просто бумажку писали идиоты. На сервере, использующем сертификат от LE (или любого другого CA) может быть как один (т. е. эффективно без SNI, вне зависимости от поддержки со стороны сервера), так и несколько хостов, имеющих различные сертификаты.
Часть, в которой LE и CF похоже — использование SAN (subjectAltName), но большинство CA его тоже используют (например, вCN
—exmaple.com
, а вSAN
—www.example.com
, так что наиболее вероятным остаются первое предположение.idmrty
31.03.2017 14:39Я имел в виду плюс для клиента, а не для самого сервера. Клиенту ведь безразлично, поддерживает сервер SNI или нет (при условии, что у него всё, что нужно, открывается). А плюс для него один — поддержка XPIE8 (специфический плюс, но кому-то важно).
alexkbs
30.03.2017 10:47Прочитайте здесь: https://habrahabr.ru/post/325230/#nedostatki
Если вы делаете как по ссылке, то вы сами себе создаёте заботу. Вся статья о том чтобы не делать так, как делается в том генераторе.
nikitasius
30.03.2017 10:12+1Это, конечно, круто, но…
ssl_protocols TLSv1.2;
Как ни крути, есть динозавры, которые по TLSv1.2 просто не работают, в итоге нужно указывать 1/1.1/1.2. Если цель была показать цену 100% результата, то вы таки добились этого (мне статья понравилась, в частности того, что я пропустил как-то мимо ушейssl_ecdh_curve
и у вас хорошо расписаны шифры).
И немного вопросов:
- Чем вызван отказ от:
ssl_prefer_server_ciphers on;
? - Почему ssl настроен без http2 (конфиг nginx) и ALPN (с openssl 1.0.2+)?
- У вас не используется
ssl_dhparam
, по логике ECDHE вылезают из DHE, и должны использовать этот файл.
alexkbs
30.03.2017 11:02+1Во второй части статьи для динозавров мы возвращаем старые протоколы и так далее для максимальной совместимости. Посмотрите ещё раз, пожалуйста.
По вопросам:
ssl_prefer_server_ciphers on
в Debian прописано по умолчанию.- потому что даже в Debian testing nginx собран с OpenSSL 1.0.1t без ALPN
ssl_dhparam
не используется потому что не используются шифры DHE
nikitasius
30.03.2017 22:22Это хорошо, что ECDHE не требует dh_param, буду знать.
На тему http2+ALPN — можно скачать исходники nginx и собрать «правильный».
ssl_prefer_server_ciphers по дефолту off;, но я никогда не видел конфига nginx из пакетов, так как никогда его из пакетов не ставил, тут спорит не буду.
Хотя у вас в статье не указано, что последний из пакетов.
idmrty
31.03.2017 10:22Debian Stretch скоро пойдёт в релиз:
# nginx -V
nginx version: nginx/1.10.3
built with OpenSSL 1.1.0d 26 Jan 2017 (running with OpenSSL 1.1.0e 16 Feb 2017)
- Чем вызван отказ от:
grossws
30.03.2017 10:44Из них исключим шифры слабее 256 бит
Этому будет какое-то обоснование? Так вы поднимаете требования к памяти и CPU клиентов, но далеко не факт, что как-то улучшаете ситуацию.
grossws
30.03.2017 10:56И, возможно, будет полезен guard вида
!EXPORT
, чтобы нечаянно не напороться, разрешив что-нибудь лишнее, что включает "экспортные" версии шифровalexkbs
30.03.2017 11:10OpenSSL 1.0.1t ничего про экспортные версии шифров не знает. Нет смысла исключать то, чего нет.
$ openssl ciphers EXPORT Error in cipher list
grossws
30.03.2017 11:15Вашей инструкцией может воспользоваться кто-нибудь с 0.9.9 или не отключенными при сборке экспортными шифрами.
alexkbs
30.03.2017 11:21Тест не даст хороших оценок с экспортными шифрами. Чай разберутся как-нибудь, раз уж разобрались как OpenSSL собирать без отключенных экспортных шифров.
vesper-bot
30.03.2017 11:03+1По-хорошему надо ещё выпилить SSLv3 полностью из протоколов. В последнем конфиге этого не указано, если я правильно его прочел. IE8/XP все равно сумеют связаться с сервером с использованием TLS1.0.
boloto
30.03.2017 12:24Огромное спасибо за подробное объяснение параметров и ключей.
PS: так и хочется просто скопипастить Ваш конфиг в «Итого», однако попробую пройти по всему пути сам.
zooks
30.03.2017 13:35В свое время убил несколько часов на подгон ssl_ciphers под A+ в данном тесте.
Спасибо, будем пробовать.
darken99
30.03.2017 17:13nginx-1.6.2.tar.gz 16-Sep-2014
серьезно?
EduardNikolenko
31.03.2017 03:35+1Это же Debian, ничего удивительного.
nikitasius
31.03.2017 09:27-4«Это же» не мешает скачать с оффициального сайта исходники и самому собрать.
JerleShannara
02.04.2017 22:25+3Лёгким движением ./configure && make && make install любой дистрибутив превращается слакварь
lukashin
31.03.2017 09:50+2Есть же официальный репозиторий или backports.
alexkbs
31.03.2017 10:23Более новая версия ровным счётом ничего не меняет: даже в backports она собрана с не самой свежей OpenSSL.
Можно пересобрать с самой свежей — спору нет. Но тогда прощай
DES-CBC3-SHA
и IE8/XP. Если вам это не важно, то вперёд.darken99
31.03.2017 11:21Если вам не важно наличие 3-х security vulnerabilities в указанной вами версии, то удачи.
alexkbs
31.03.2017 12:24-1Очень интересно, жду пруфлинки. Версия 1.6.2-5+deb8u4.
darken99
31.03.2017 12:33Все доступно на оф. сайте
CVE-2016-4450 - имеется патчA problem was identified in nginx code responsible for saving
client request body to a temporary file. A specially crafted request
might result in worker process crash due to a NULL pointer dereference
while writing client request body to a temporary file (CVE-2016-4450).
The problem affects nginx 1.3.9 — 1.11.0.
The problem is fixed in nginx 1.11.1, 1.10.1.
Patch for nginx 1.9.13 — 1.11.0 can be found here:
http://nginx.org/download/patch.2016.write.txt
Patch for older nginx versions (1.3.9 — 1.9.12):
darken99
31.03.2017 11:19Может я открою страшную тайну, но есть официальный репозиторий:
Debian reposirtoryFor Debian replace codename with Debian distribution codename, and append the following to the end of the /etc/apt/sources.list file:
deb http://nginx.org/packages/debian/ codename nginx
deb-src http://nginx.org/packages/debian/ codename nginx
Pentoxide
30.03.2017 17:44-1Я еще убрал из списка DES-CBC3-SHA, https://tls.imirhil.fr/ показывает что он слабый. Из списка совместимости вылетел IE8/XP, думаю невелика потеря.
Scf
30.03.2017 23:04Как интересно, а есть опыт настройки SSL под http/2? Мне никак не удается настроить forward secrecy c http/2, поэтому мой предел —
A-
.Erelecano
31.03.2017 05:20А в чем проблема?
Вот ssl.conf который у меня инклюдится в файлы всех доменов.
ssl on; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_dhparam /etc/nginx/dhparam.2048.pem; ssl_stapling on; ssl_stapling_verify on; ssl_session_tickets off; resolver 8.8.8.8; add_header Strict-Transport-Security "max-age=31536000";
Ну и
server { listen bla-bla.tld:443 http2; }
в конфиге самого домена
https://www.ssllabs.com/ssltest/analyze.html?d=gorky.media
Forward Secrecy Yes (with most browsers) ROBUST (more info)
ALPN Yes
NPN Yes h2 http/1.1
Настройки оптимальны, ибо даже из под XP с последними для него Хромом и Файрфоксом все работает. Если еще чуть-чуть попробовать закрутить, то ломается XP, а у меня у одного из клиентов много посетителей из под XP, пришлось из-за них гайки слегка развинчивать.
A+, все прекрасно, но XP на сайты попадают.alexkbs
31.03.2017 06:14Старую версию Java потеряли, медленные DHE шифры не исключили. Статью не читали, да?
Erelecano
31.03.2017 13:26-2Статья вредна. Загонять все в 100 будет только дрочер которому нечего делать. А это — пример реального конфига с боевого сервера.
alexkbs
31.03.2017 13:34+2Вы не поверите, но статья именно что о том почему загонять всё в 100% не стоит. С бенчмарками и прочим. Это, конечно, если дальше заголовка прочитать.
grossws
31.03.2017 14:31ssl on;
как пишут нам на сайте nginx.org:
It is recommended to use the ssl parameter of the listen directive instead of this directive.
Erelecano
31.03.2017 14:32Спасибо, файл достаточно старый и просто дополнялся и менялся, а ssl on; как висел, так и висит. Надо посмотреть, скорее всего http2 в listen решает это дело сейчас, тогда просто уберу.
alexkbs
31.03.2017 05:22Под nginx/1.11.10 без ALPN всё настраивается с http/2 как в посте написано. Результат тоже. Проверял здесь.
altervision
30.03.2017 23:43Спасибо за подробное объяснение!
Теперь в SSL Labs только один пункт остался для меня неразрешенным: DNS CAA.
Подскажите, у кого из DNS-провайдеров сервер поддерживает добавление записей CAA? У Яндекса в подключенном к Яндекс.ППП DNS-сервере я такого не отыскал. Что я делаю не так?)alexkbs
31.03.2017 05:28Пока не слишком широкая поддержка этого вида записей.
Public Key Pinning разгадали? Там ничего сложного, но уже точно сделать можно в отличии от CAA.
Komei
31.03.2017 06:12Я что-то совсем запутался. Зачем нужен DNS CAA, если уже вроде есть DNS TLSA (DANE)?
alexkbs
31.03.2017 06:13+2DNS CAA определяет кто может выдавать сертификаты для домена. DANE совсем про другое.
Komei
31.03.2017 19:56А про что тогда DANE? Насколько я понял, DANE может указывать или на конкретный сертификат (или его хеш) или на сертификат CA, который обслуживает данный домен (если говорить точнее, то там можно отдельные значения для разных портов и даже протоколов делать). Я в чём-то не прав?
nikitasius
31.03.2017 09:31Я жду, когда все таки начнут использовать DANE. Это будет началом конца всех центров центров выдачи сертификатов.
alexkbs
31.03.2017 12:49Это наступит не раньше тотального внедрения DNSSEC, а это не похоже что наступит скоро...
Komei
31.03.2017 19:57Дык любая информация из DNS о сектификатах и тому подобном без DNSSEC бессмысленна, т.к. не достоверна. Какой смысл в записи о том, что нужно использовать только CA «abc», если эту запись можно подделать?
alexkbs
02.04.2017 05:50Вы считаете лучше не иметь вообще никаких ограничений по выдаче сертификатов, чем иметь какие-то, хоть не совсем надежные?
Мне кажется вы не понимаете разницы между DNS CAA и DANE. Рад буду ошибаться.
Komei
02.04.2017 06:31Вы считаете лучше не иметь вообще никаких ограничений по выдаче сертификатов, чем иметь какие-то, хоть не совсем надежные?
В данном случае да. Потому что DNS уже массово подменяется. Без DNSSEC это не защита, а дополнительная точка для атаки. Кроме того, нет ничего хуже, чём ложное чувство защищённости, когда её нет. Уж лучше пусть все знают что сейчас проблема с СА есть и её надо решать, чем думают что всё ок и левый сертификат для их домена никто не сможет выпустить.
Мне кажется вы не понимаете разницы между DNS CAA и DANE. Рад буду ошибаться.
Очень может быть. Насколько я понимаю DANE нужна для того, чтобы домен мог БЕЗОПАСНО и НАДЁЖНО сообщить информацию о своём сертификате (в общем случае, можно CA, можно сертификат, можно их хеши). Это позволяет защититься от выпуска левых сертификатов. Причём решение очень гибкое. Подходит и для самоподписных сертификатов.
А что такое DNS CAA, это только ограничение на СА и всё. Возможностей меньше. Без DNSSEC верить обоим типам записей нельзя (если на это реально плевать, так можно и записи DANE передавать без DNSSEC, технически то можно...).
Вот мне и не ясно, зачем нужен ущербный вариант с CAA, если есть уже хороший DANE?alexkbs
02.04.2017 06:54То есть вы считаете что лучше вообще не пользоваться CAA? Вы считаете что от такой записи рядом с вашим доменом ровным счетом никакой пользы?
Komei
02.04.2017 07:15Не совсем. Мне не понятно зачем нужна ещё одна запись, если есть записи типа DANE. Их тоже можно передавать без DNSSEC`а. И будет тоже самое, даже более гибко (я считаю что то что будет — будет фигнёй, но это уже моё личное мнение). Или я ошибаюсь?
alexkbs
02.04.2017 13:05DANE призван полностью устранить СА из уравнения. То есть, получать сертификаты без них. Совершенно новая схема.
CAA призван ограничить полномочия CA. То есть, все то же, что обычно, но с ограничениями.
Komei
02.04.2017 20:47DANE призван полностью устранить СА из уравнения.
Разве? Ведь DANE поддерживает и указание CA для домена, а не только сертификата. Просто если то что вы сказали действительно верно, то я снимаю все свои вопросы.
tankistua
31.03.2017 10:23Кстати по-поводу получения сертификатов, мы тут недавно получали сертификат EV у Комодо и это просто превратилось таки в длительный квест. Нужные сертификаты получились с третьего раза — мы заказывали на три домена, заняло все это мероприятие почти два месяца.
Если кто-то собирается эту процедуру проходить — закладывайте время
idmrty
31.03.2017 10:37+3Важная информация для тех, кому критична поддержка XP+IE8: начиная с версии OpenSSL 1.1.0 по умолчанию выпилен triple-DES. И, например, в грядущем Debian Stretch nginx собран уже именно с 1.1.0. Это означает, что сайт у посетителей с XP+IE8 открываться не будет независимо от конфига nginx.
Пруф: https://www.openssl.org/blog/blog/2016/08/24/sweet32/alexkbs
31.03.2017 12:33Спасибо за ссылку. Всё даже хуже. Так как в nginx по-умолчанию используются шифры из группы HIGH, то вполне возможно уже с версии OpenSSL 1.0.2 шифров 3DES не будет в списке шифров по умолчанию, а значит уже тогда пользователи IE8 останутся без половины интернета.
darken99
31.03.2017 13:13IE8 уже даже в статистике не видно, на уровне статистической погрешности.
Зачем тащить за собой дохлую корову?idmrty
31.03.2017 15:07+1В общем случае вы правы, и все это понимают. Но, как мне кажется, могут существовать сайты, целевая аудитория которых не стремится быть на пике технологического прогресса (одинокие престарелые люди, детские дома, тяжёлые инвалиды, бюджетные учреждения и т. д.). Работает старенький компьютер — и ладно, а перестанет — и без него обойдёмся. Может быть, я лишнего выдумываю…
alexkbs
02.04.2017 05:54Гораздо хуже: есть весьма обеспеченные люди, приносящие бизнесам существенный доход, которые не спешат выбрасывать старый компьютер. Вполне возможно они богатые в том числе потому что не гонятся за новинками. Очень сложно вот взять и отбросить таких клиентов. Конечно, это проблема — не проблема сайтов уровня персонального блога.
navion
31.03.2017 13:29А официальные пакеты собирают с какой версией OpenSSL?
idmrty
31.03.2017 15:14Честно говоря, я вообще nginx беру с Dotdeb, а не из официальных или дебиановских репозиториев, поэтому не знаю.
varnav
31.03.2017 16:06+1Кстати, что до Debian. Я в Debian 8 ставлю nginx вот так:
echo 'deb http://ftp.debian.org/debian jessie-backports main' > /etc/apt/sources.list.d/backports.list apt-get update && apt-get -t jessie-backports install openssl nginx -y && systemctl enable nginx && systemctl start nginx
Получается на выходе nginx/1.9.10 built with OpenSSL 1.0.2j
ALPN есть, HTTP/2 есть, репозиторий официальный, сборка из исходников не нужна, всё хорошо.Erelecano
31.03.2017 19:441.9.10? Беда какая. mainline старый.
nginx надо ставить из родных репозиториев.
For Debian replace codename with Debian distribution codename, and append the following to the end of the
/etc/apt/sources.list file:
deb http://nginx.org/packages/debian/ codename nginx
deb-src http://nginx.org/packages/debian/ codename nginx
For Ubuntu replace codename with Ubuntu distribution codename, and append the following to the end of the /etc/apt/sources.list file:
deb http://nginx.org/packages/ubuntu/ codename nginx
deb-src http://nginx.org/packages/ubuntu/ codename nginx
При этом лучше брать таки mainline, это — официальный совет из доков на самом nginx.org. А брать старый mainline из которого уже вышел stable — дурная идея.varnav
31.03.2017 22:20Наверное это кунг-фу сильнее моего. Я попробую.
Erelecano
31.03.2017 22:46Ну это таки самый правильный вариант. Есть официальные репозитории, в которых всегда свежие версии пакетов, при этом для всех живых веток дистрибутивов. Вот их и надо использовать.
varnav
01.04.2017 11:32Ну, тут можно немного поспорить какой репозиторий официальнее. От производителя ОС, или от производителя ПО. ;)
У нас на проде жёстко центось с родными репами (в которых обычно старьё), и на то есть причины.
alexkbs
01.04.2017 05:36В официальных nginx с какой версией OpenSSL собран, простите?
Что-то мне подсказывает что не с 1.0.2j, а более ранней. А значит никакого ALPN.
saipr
04.04.2017 09:50ECDHE и AES будут как друзья да братья
Очень хочется, чтобо в друзьями да еще лучше братьями были ГОСТ Р 34.10, ГОСТ Р 34.11, ГОСТ 28147, а также Кузнечики и Магма.
l3xx
То что надо, спасибо за статью!