Високопроизводителен HTTPS

https-visokoproizvoditelen-siguren

Високопроизводителният и по-сигурен HTTPS винаги е бил цел за повечето системни администратори. В тази статия ще ви покажем как можете да постигнете тази цел с няколко сравнително прости метода. Преди всичко е необходимо да поясним, че следващите стъпки трябва да бъдат направени от човек, който има познания по *nix, системна администрация и работа с команден ред. Напомняме, че е необходимо да направите архиви на конфигурационните файлове, защото следващите примери могат много сериозно да променят работата на вашия сървър.

ВАЖНО!!! В днешната статия ще обърнем внимание на HTTPS, като транспортен слой и ще разгледаме само и изключително TLS. Статията не е насочена към разглеждане на SSL, било то версия 2 или 3. SSL е добър протокол, но се използва повече от 21 години. Когато е проектиран, а след това и изпълнен, е бил най-доброто на пазара.

Много неща обаче са се променили оттогава. Най-простият пример е, че тогавашните супер компютри в днешно време могат да бъдат изпреварени с лекота от съвременните смарт-устройства, като телефони и таблети. Това е причината да бъде въведена забраната за създаването на SSL2 сигурни връзки от 2011 година, а от 2015-та е забранено създаването и на SSL3 сигурни връзки. Днес се използват само следните протоколи – TLS 1.0, TLS 1.1, TLS 1.2, а в близките месеци ще бъде обявен и TLS 1.3, който в момента съществува във версия на чернова.

Най-големият мит е, че HTTPS е бавен. Всъщност съвременният HTTPS е бърз и високопроизводителен, но заради някои негови особености е възможно да се забави работата на целия сайт, ако не бъде конфигуриран правилно.

Още през 2010 година екипът на Google прехвърли целия Gmail към HTTPS, за да може връзката да бъде сигурна винаги, без да оставят опция на потребителя да избира. От Google даже обявиха какво им е коствало товa пренасочване – под 1% CPU, по-малко от 10 KB памет и по-малко от 2% натоварване на мрежата. Това опровергава разпространеното схващане, че SSL/TLS използва много процесорно време.

Ако това са стойности през 2010-та година, с новите процесори и тeхните специални инструкции за ускорение, днес те са още по-ниски.

Почти всички проблеми обаче на HTTPS възникват на ниво ръкостискане (TLS handshake), където сървърът и клиентът се споразумяват за сигурността, обменят сертификати и извършват проверка един на друг.

1.Първи метод за ускоряване

Първият метод за ускоряване е връщането на подписан отговор от издателя на сертификата (OSCP stapling) за проверка на сертификата. При установяването на сигурна връзка клиентът получава сертификата от сървъра, след което клиентът прави връзка с издателя на сертификата, за да провери дали този сертификат не е отменен междувременно (revoked). Това е съпроводено с повече мрежов трафик към издателя – проверка на домейна, изграждане на сигурна връзка към издателя, изпращане на заявка, получаване на заявка. За настолните клиенти това би отнелно около секунда-две (възможно е и повече, в зависимост от връзката), но за мобилните клиенти това е минимум 4-5 секунди (или повече). Затова част от сървърите използват подписан отговор от издателя, като механизъм за ускорение. Реално сървърът се свързва с издателя и получава същия подписан отговор, както за клиента. Отговорът се съхранява временно на сървъра със срок на живот от няколко минути или часове. След изтичането на времето се взема нов подписан отговор, старият се изтрива и вече се използва новият. Когато клиент се свърже със сървъра и поиска установяването на сигурна връзка, сървърът, освен собствения си сертификат, връща и горния отговор. Така клиентът ги получава  едновременно и извършва проверката спрямо данните на издателя, с които разполага локално. Нищо не е безплатно и изпращането на подписания отговор увеличава леко времето за създаване на сигурната връзка със сървъра, защото трябва да се предадат още между 400 и 4000 байта.

Конфигуриране за Apache 2.3.3 и следващи и OpenSSL 0.9.8h минимум:

<IfModule mod_ssl.c>
SSLStaplingCache shmcb:/tmp/stapling_cache(128000)
<VirtualHost *:443>

SSLCACertificateFile /etc/ssl/ca-certs.pem
SSLUseStapling on

</VirtualHost>
</IfModule>

Конфигуриране за nginx 1.3.7 и следващи:

{
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/full_chain.pem;

resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}

Тествайте дали работи:

echo QUIT | openssl s_client -connect delta.bg:443 -status

Ако всичко е наред горната команда ще трябва да върне нещо подобно:

OSCP stapling

където е показана информация, че всичко е наред с отговора, криптографски е подписан правилно, и наличие на датата на издаване и датата на следващата заявка. Ако не работи, ще видите нещо подобно:

OCSP response: no response sent

Това означава липсата на OCSP информацията.

2.Втори метод за ускорение

Втората важна стъпка за ускорението е подбора на правилните транспортни протоколи за сигурна връзка. Нещата тук обаче са малко по-дълбоки и не се изчерпват със забраната на горепосочените SSL2 и SSL3. Има два принципни подхода – единият е малко по-консервативен и не позволява използване на по-стари клиенти (примерно Windows XP, Android 2/3 и други). Другият е по-либерален – позволява използването и на стари клиенти. Нашият логичен избор е по-либералният и конфигурациите за него.

Конфигуриране на Apache:

<IfModule mod_ssl.c>
<VirtualHost *:443>

SSLProtocol all -SSLv3

</VirtualHost>
</IfModule>

Конфигуриране на nginx:

{
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
}

Ако искате да предприемете по-консервативния подход (който не препоръчваме):

За Apache трябва да изглежда ето така:

SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

За nginx трябва да изглежда така:

ssl_protocols TLSv1.2;

Как да тествате какво е налично?

echo QUIT | openssl s_client -connect delta.bg:443 -ssl3
echo QUIT | openssl s_client -connect delta.bg:443 -tls1
echo QUIT | openssl s_client -connect delta.bg:443 -tls1_1
echo QUIT | openssl s_client -connect delta.bg:443 -tls1_2

Ако има грешка ще видите нещо като:

SSL3 error

или това:

SSL3 other error

Във всички останали случаи ще видите всички фази на изграждането на сигурна връзка.

3.Трети метод за ускорение

Третият начин за ускорение е с използването на билети (session tickets) или идентификатори (session identifiers). По време на дългия процес на ръкостискането двете страни се разбират за сертификати, ключове, защити и т.н. Ако връзката се прекъсне, ръкостискането трябва да се състои отново без изключение. Но има начини за подобрение на целия този процес. Така при първото установяване на връзка, сървърът ще генерира номер на билет или идентификатор и ще го изпрати към клиента, а клиентът ще го запази временно в паметта. Така при установяване на нова сигурна връзка клиентът ще изпрати тази информация към сървъра и връзката ще бъде осъществена много по-бързо на база на тази информация. Обикновено билетите/идентификаторите са с кратък живот – около 10 минути и са криптографски защитени.

Как можете да конфигурирате това за Apache:

<IfModule mod_ssl.c>
SSLSessionCache shmcb:/tmp/ssl_cache_data(512000)
<VirtualHost *:443>

SSLSessionTickets on
SSLSessionCacheTimeout 600

</VirtualHost>
</IfModule>

За nginx конфигурацията е нещо като:

{
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_session_tickets on;
}

Най-лесният вариант, с който да проверите дали работи поне един механизъм, е:

echo QUIT | openssl s_client -connect delta.bg:443 -status -reconnect

Ако механизмът сработи, ще видите нещо като Reused, TLSv1/SSLv3, Cipher is във всички връзки освен в първата. Там надписът ще е New, TLSv1/SSLv3, Cipher is. Ако навсякъде виждате New, TLSv1/SSLv3, Cipher is, то тогава механизмите не сработват.

TLS reused session

ВАЖНО!!! Въпреки че билетите и идентификаторите звучат много близки, има няколко много същественни разлики между тях. Ако ги използвате само на един единствен сървър, няма да видите разликата между тях, освен в много натоварени сървъри. Истинската разлика обаче лесно може да се види, когато има екип от сървъри, които отговарят за един уеб сайт едновременно под едно и също име. Тогава е възможно клиент да се е свързал с един сървър в една връзка, а при отваряне на връзката да се свърже с друг сървър. Ако се използват идентификатори, то новият сървър няма да разполага с негово копие и ще се наложи връзката да се изгради като нова. Ако се използват билети, новият сървър ще успее да изгради връзката без дългия процес на ръкостискането.

Четвъртият начин за ускорение е забраняването на всички по-несигурни шифровъчни алгоритми и предоставянето само на няколко от тях за нуждите на връзката. Отделно казвате на сървъра да избира сигурността на връзката, а не клиента. С последното забранявате на клиента да понижава сигурността в движение. Материята е доста обширна, защото има над 100 алгоритъма за криптиране на данните и всеки един има своите особености.

Може да видите пълния списъл по следния начин:

openssl -V

openssl ciphers

openssl other ciphers
и съдържа както 40 битови алгоритми, така и  256 битови. Вие обаче не искате нищо под 128 битов ключ. Отделно ние бихме искали да се възползваме максимално от процесорната мощ със специалните AES-NI инструкции, създадени за ускорение на криптирането. И като последна стъпка – необходимо е защитата с елиптичните криви да бъде с една идея над стандартните 256 бита.

Ето как изглежда конфигурацията за Apache:

<IfModule mod_ssl.c>
<VirtualHost *:443>

SSLCipherSuite          ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
SSLHonorCipherOrder     on
SSLOpenSSLConfCmd ECDHParameters secp384r1

</VirtualHost>
</IfModule>

Ето как изглежда конфигурацията за nginx:

{
ssl_prefer_server_ciphers on;
ssl_ciphers “EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH”;
ssl_ecdh_curve secp384r1;
}

Как можем да тестваме какво поддържа сървърът?

Първо започваме с:

wget http://nmap.org/svn/scripts/ssl-enum-ciphers.nse

и продължаваме с:

nmap –script ssl-enum-ciphers -p 443 delta.bg

резултатът ще бъде:

openssl list site ciphers

И последно – можете да изключите компресирането на сигурната връзка. Технически това ускорява връзката, но конкретно за сайтове и страници не работи ефективно, защото голяма част от ресурсите вече са компресирани с gzip или deflate и тогава компресията, която работи на ниво сигурна връзка се опитва да компресира вече компресирани данни – загуба на време и процесорна мощност. И не на последно място има един механизъм за атака на компресирани данни – CRIME. Затова изключването решава няколко проблема.

За Apache:

<IfModule mod_ssl.c>
SSLCompression off
</IfModule>

За nginx компресията по подразбиране е изключена от версия 1.3.2/1.2.2. Ето и как изглеждат резултатите след настройките:

https://www.ssllabs.com/ssltest/analyze.html?d=delta.bg&s=89.252.247.5
https://www.ssllabs.com/ssltest/analyze.html?d=www.jump.bg
https://www.ssllabs.com/ssltest/analyze.html?d=test.nikolow.me

Веднага се виждат няколко проблема:

Android 2.3.7 – не може да се свърже към два от сървърите, защото те нямат отделни интернет адреси, а операционната система на клиента не поддържа механизъм за указване на сървърното име (TLS SNI). Единият сървър използва и 4096 битови ключове, което също е пречка за осъществяване на сигурна връзка.

Windows XP / IE6 – не може също да се свърже към сървърите. Тук има друг проблем – IE6 изобщо не поддържа AES. Ако използваме други алгоритми за криптиране пак ще видим същия проблем със сървърното име или битовете за криптиране му идват много. Това е само за Internet Explorer. Ако на Windows XP се използва друг браузър няма проблеми.

Windows XP / IE8 – има успех към единия сървър, защото разполага с отделно IP и има метод за криптиране, който не е AES. В останалите случаи има проблем, защото се връща друг сертификат поради неподдържането на съврърното име под XP.

Java 6 – отновно има проблем със сървърните имена. Отделно Java 6 не поддържа връзки, когато параметрите на ръкостискането (DH) са над 1024 бита. Ние подаваме 4096 или 2048.

Java 7 – има проблем само с единия сървър, защото Java 7 поддържа само TLS 1.0 и AES-128, а сървърната конфигурация е указано изрично AES-256.

Всички останали клиенти от теста ще могат да се свържат със сървърите и да обменят данни с тях по сигурна връзка.

Ето и пълни примери как изглеждат перфектните конфигурации за Apache:

<IfModule mod_ssl.c>
<VirtualHost *:443>

SSLEngine on

</VirtualHost>

SSLProtocol all -SSLv3
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
SSLHonorCipherOrder on
SSLOpenSSLConfCmd ECDHParameters secp384r1
SSLCompression off
SSLSessionTickets on

SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
SSLStaplingCache shmcb:/var/run/ocsp(128000)
</IfModule>

И за nginx:

server {
listen 443 ssl;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout  10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers “EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH”;
ssl_ecdh_curve secp384r1;
ssl_session_tickets on;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}

Въпреки че горните конфигурации изглеждат сложни, реално няма нищо трудно в тях. Всичко е въпрос на настройки и само така може да се постигне перфектният баланс между повече сигурност и достъпност за повече клиенти.

Leave a Reply

Your email address will not be published. Required fields are marked *