ZeroTech-də Apple Safari və müştəri sertifikatlarını veb-soketlərlə necə birləşdirdik

Məqalə aşağıdakıları edənlər üçün faydalı olacaq:

  • Client Cert-in nə olduğunu bilir və mobil Safari-də veb-soketlərə nə üçün ehtiyac duyduğunu anlayır;
  • Məhdud insanlar dairəsinə və ya yalnız özümə veb xidmətləri dərc etmək istərdim;
  • hər şeyin artıq kimsə tərəfindən edildiyini düşünür və dünyanı bir az daha rahat və təhlükəsiz etmək istəyir.

Websockets tarixi təxminən 8 il əvvəl başladı. Əvvəllər üsullar uzun http sorğuları (əslində cavablar) şəklində istifadə olunurdu: istifadəçinin brauzeri serverə sorğu göndərir və onun nəyəsə cavab verməsini gözləyir, cavabdan sonra yenidən qoşulur və gözləyirdi. Ancaq sonra veb-sockets göründü.

ZeroTech-də Apple Safari və müştəri sertifikatlarını veb-soketlərlə necə birləşdirdik

Bir neçə il əvvəl biz https sorğularından istifadə edə bilməyən PHP-də öz tətbiqimizi inkişaf etdirdik, çünki bu, keçid qatıdır. Bir müddət əvvəl, demək olar ki, bütün veb serverlər https üzərindən proxy sorğuları etməyi və bağlantı: təkmilləşdirməni dəstəkləməyi öyrəndilər.

Bu baş verdikdə, websockets SPA proqramları üçün demək olar ki, standart xidmət oldu, çünki serverin təşəbbüsü ilə istifadəçiyə məzmun təqdim etmək nə qədər rahatdır (başqa istifadəçidən mesaj ötürmək və ya şəklin, sənədin, təqdimatın yeni versiyasını yükləmək). başqasının hazırda redaktə etdiyini).

Müştəri Sertifikatı uzun müddətdir mövcud olsa da, hələ də zəif dəstəklənir, çünki onu keçməyə çalışarkən çoxlu problemlər yaradır. Və (bəlkə :slightly_smiling_face: ) buna görə də IOS brauzerləri (Safari istisna olmaqla) ondan istifadə etmək istəmir və yerli sertifikat mağazasından tələb edirlər. Sertifikatların login/pass və ya ssh düymələri və ya firewall vasitəsilə lazımi portların bağlanması ilə müqayisədə bir çox üstünlükləri var. Amma bu, bununla bağlı deyil.

İOS-da sertifikatın quraşdırılması proseduru olduqca sadədir (xüsusi təfərrüatlar olmadan), lakin ümumiyyətlə, İnternetdə çox şey olan və yalnız Safari brauzeri üçün mövcud olan təlimatlara uyğun olaraq həyata keçirilir. Təəssüf ki, Safari veb-rozetkalar üçün Client Сert-dən necə istifadə edəcəyini bilmir, lakin İnternetdə belə bir sertifikatın necə yaradılması ilə bağlı çoxlu təlimatlar var, lakin praktikada bu mümkün deyil.

ZeroTech-də Apple Safari və müştəri sertifikatlarını veb-soketlərlə necə birləşdirdik

Veb-soketləri başa düşmək üçün aşağıdakı plandan istifadə etdik: problem/hipoteza/həlli.

Problem: IOS və sertifikat dəstəyini aktivləşdirən digər proqramlar üçün Safari mobil brauzerində müştəri sertifikatı ilə qorunan resurslara sorğular göndərilərkən veb rozetkalar üçün dəstək yoxdur.

Hipotezlər:

  1. Sertifikatlardan istifadə etmək üçün belə bir istisnanı konfiqurasiya etmək mümkündür (heç birinin olmayacağını bilməklə) daxili/xarici proksied resursların veb-soketlərinə.
  2. Veb-soketlər üçün adi (veb-soket olmayan) brauzer sorğusu zamanı yaradılan müvəqqəti seanslardan istifadə edərək unikal, təhlükəsiz və müdafiə edilə bilən əlaqə yarada bilərsiniz.
  3. Müvəqqəti seanslar bir proxy veb serverdən istifadə etməklə həyata keçirilə bilər (yalnız daxili modullar və funksiyalar).
  4. Müvəqqəti sessiya tokenləri artıq hazır Apache modulları kimi həyata keçirilib.
  5. Müvəqqəti seans tokenləri qarşılıqlı əlaqə strukturunun məntiqi dizaynı ilə həyata keçirilə bilər.

İcradan sonra görünən vəziyyət.

İşin məqsədi: xidmətlərin və infrastrukturun idarə edilməsinə əlavə proqramlar (məsələn, VPN) olmadan IOS-da mobil telefondan daxil olmaq, vahid və təhlükəsiz olmalıdır.

Əlavə məqsəd: mobil İnternetdə məzmunun daha sürətli çatdırılması ilə vaxta və resurslara/telefon trafikinə qənaət (veb yuvaları olmayan bəzi xidmətlər lazımsız sorğular yaradır).

Nasıl kontrol etməliyəm?

1. Səhifələrin açılması:

— например, https://teamcity.yourdomain.com в мобильном браузере Safari (доступен также в десктопной версии) — вызывает успешное подключение к веб-сокетам.
— например, https://teamcity.yourdomain.com/admin/admin.html?item=diagnostics&tab=webS…— показывает ping/pong.
— например, https://rancher.yourdomain.com/p/c-84bnv:p-vkszd/workload/deployment:danidb:ph…-> viewlogs — показывает логи контейнера.

2. Və ya developer konsolunda:

ZeroTech-də Apple Safari və müştəri sertifikatlarını veb-soketlərlə necə birləşdirdik

Hipotez testi:

1. Belə bir istisnanı daxili/xarici proksi resursların veb yuvalarına sertifikatlardan istifadə etmək üçün (heç birinin olmayacağını bilərək) konfiqurasiya etmək mümkündür.

Burada 2 həll yolu tapıldı:

a) səviyyədə

<Location sock*> SSLVerifyClient optional </Location>
<Location /> SSLVerifyClient require </Location>

giriş səviyyəsini dəyişdirin.

Bu üsul aşağıdakı nüanslara malikdir:

  • Sertifikat yoxlanışı etibarlı resursa sorğudan sonra baş verir, yəni sorğu göndərən əl sıxma. Bu o deməkdir ki, proksi əvvəlcə yüklənəcək və sonra qorunan xidmətə sorğunu kəsəcək. Bu pisdir, lakin kritik deyil;
  • http2 protokolunda. O, hələ də layihədədir və brauzer istehsalçıları bunu necə həyata keçirəcəklərini bilmirlər #tls1.3 http2 post handshake haqqında məlumat (indi işləmir) RFC 8740 "HTTP/1.3 ilə TLS 2 istifadə" tətbiq edin;
  • Bu emalın necə birləşdirilməsi aydın deyil.

b) Əsas səviyyədə ssl-ə sertifikatsız icazə verin.

SSLVerifyClient => SSLVerifyClient tələb edir, lakin bu, proksi serverin təhlükəsizlik səviyyəsini azaldır, çünki belə bir əlaqə sertifikatsız işlənəcək. Bununla belə, siz aşağıdakı direktivlə proksiləşdirilmiş xidmətlərə girişi daha da rədd edə bilərsiniz:

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

Daha ətraflı məlumatı ssl haqqında məqalədə tapa bilərsiniz: Apache Server Müştəri Sertifikatının Doğrulanması

Hər iki variant sınaqdan keçirildi, çox yönlü olması və http2 protokolu ilə uyğunluğu üçün “b” variantı seçildi.

Bu fərziyyənin yoxlanılmasını başa çatdırmaq üçün konfiqurasiya ilə çoxlu təcrübələr aparıldı, aşağıdakı dizaynlar sınaqdan keçirildi:

əgər = tələb etsə = yenidən yaz

Nəticə aşağıdakı əsas dizayndır:

SSLVerifyClient optional
RewriteEngine on
RewriteCond %{SSL:SSL_CLIENT_VERIFY} !=SUCCESS
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule     .? - [F]
#ErrorDocument 403 "You need a client side certificate issued by CAcert to access this site"

#websocket for safari without cert auth
<If "%{SSL:SSL_CLIENT_VERIFY} != 'SUCCESS'">
<If "%{HTTP:Upgrade} = 'websocket'">
...
    #замещаем авторизацию по владельцу сертификата на авторизацию по номеру протокола
    SSLUserName SSl_PROTOCOL
</If>
</If>

Sertifikat sahibinin mövcud icazəsini nəzərə alaraq, lakin çatışmayan sertifikatla mən mövcud dəyişənlərdən biri şəklində mövcud olmayan sertifikat sahibini SSl_PROTOCOL (SSL_CLIENT_S_DN_CN əvəzinə) əlavə etməli oldum, sənədlərdə daha ətraflı:

Apache Modulu mod_ssl

ZeroTech-də Apple Safari və müştəri sertifikatlarını veb-soketlərlə necə birləşdirdik

2. Veb-soketlər üçün adi (veb-soket olmayan) brauzer sorğusu zamanı yaradılan müvəqqəti seanslardan istifadə edərək unikal, təhlükəsiz və qorunan əlaqə yarada bilərsiniz.

Əvvəlki təcrübəyə əsaslanaraq, müntəzəm (veb rozetkadan kənar) sorğu zamanı veb-rozetka qoşulmaları üçün müvəqqəti nişanlar hazırlamaq üçün konfiqurasiyaya əlavə bölmə əlavə etməlisiniz.

#подготовка передача себе Сookie через пользовательский браузер
<If "%{SSL:SSL_CLIENT_VERIFY} = 'SUCCESS'">
<If "%{HTTP:Upgrade} != 'websocket'">
Header set Set-Cookie "websocket-allowed=true; path=/; Max-Age=100"
</If>
</If>

#проверка Cookie для установления веб-сокет соединения
<source lang="javascript">
<If "%{SSL:SSL_CLIENT_VERIFY} != 'SUCCESS'">
<If "%{HTTP:Upgrade} = 'websocket'">
#check for exists cookie

#get and check
SetEnvIf Cookie "websocket-allowed=(.*)" env-var-name=$1

#or rewrite rule
RewriteCond %{HTTP_COOKIE} !^.*mycookie.*$

#or if
<If "%{HTTP_COOKIE} =~ /(^|; )cookie-names*=s*some-val(;|$)/ >
</If

</If>
</If>

Test bunun işlədiyini göstərdi. İstifadəçinin brauzeri vasitəsilə kukiləri özünüzə köçürmək mümkündür.

3. Müvəqqəti seanslar bir proxy veb serverdən istifadə etməklə həyata keçirilə bilər (yalnız daxili modullar və funksiyalar).

Daha əvvəl öyrəndiyimiz kimi, Apache şərti konstruksiyalar yaratmağa imkan verən kifayət qədər çoxlu əsas funksionallığa malikdir. Bununla belə, istifadəçinin brauzerində olarkən məlumatımızı qorumaq üçün vasitələrə ehtiyacımız var, buna görə də nəyi və niyə saxlayacağımızı və hansı daxili funksiyalardan istifadə edəcəyimizi müəyyənləşdiririk:

  • Bizə asanlıqla deşifrə edilə bilməyən bir işarə lazımdır.
  • Bizə köhnəlmə və serverdə köhnəlməni yoxlamaq imkanı olan bir işarə lazımdır.
  • Sertifikat sahibi ilə əlaqələndiriləcək bir nişanə ehtiyacımız var.

Bunun üçün heşinq funksiyası, duz və nişanı yaşlandırmaq üçün tarix tələb olunur. Sənədlərə əsasən Apache HTTP Serverindəki ifadələr bizdə hamısı qutudan kənarda sha1 və %{TIME} var.

Nəticə bu dizayn oldu:

#нет сертификата, и обращение к websocket
<If "%{SSL:SSL_CLIENT_VERIFY} != 'SUCCESS'">
<If "%{HTTP:Upgrade} = 'websocket'">
    SetEnvIf Cookie "zt-cert-sha1=([^;]+)" zt-cert-sha1=$1
    SetEnvIf Cookie "zt-cert-uid=([^;]+)" zt-cert-uid=$1
    SetEnvIf Cookie "zt-cert-date=([^;]+)" zt-cert-date=$1

#только так можно работать с переменными, полученными в env-ах в этот момент времени, более они нигде не доступны для функции хеширования (по отдельности можно, но не вместе, да и ещё с хешированием)
    <RequireAll>
        Require expr %{sha1:salt1%{env:zt-cert-date}salt3%{env:zt-cert-uid}salt2} == %{env:zt-cert-sha1}
        Require expr %{env:zt-cert-sha1} =~ /^.{40}$/
    </RequireAll>
</If>
</If>

#есть сертификат, запрашивается не websocket
<If "%{SSL:SSL_CLIENT_VERIFY} = 'SUCCESS'">
<If "%{HTTP:Upgrade} != 'websocket'">
    SetEnvIf Cookie "zt-cert-sha1=([^;]+)" HAVE_zt-cert-sha1=$1

    SetEnv zt_cert "path=/; HttpOnly;Secure;SameSite=Strict"
#Новые куки ставятся, если старых нет
    Header add Set-Cookie "expr=zt-cert-sha1=%{sha1:salt1%{TIME}salt3%{SSL_CLIENT_S_DN_CN}salt2};%{env:zt_cert}" env=!HAVE_zt-cert-sha1
    Header add Set-Cookie "expr=zt-cert-uid=%{SSL_CLIENT_S_DN_CN};%{env:zt_cert}" env=!HAVE_zt-cert-sha1
    Header add Set-Cookie "expr=zt-cert-date=%{TIME};%{env:zt_cert}" env=!HAVE_zt-cert-sha1
</If>
</If>

Məqsəd əldə edildi, lakin serverin köhnəlməsi ilə bağlı problemlər var (bir illik Cookie-dən istifadə edə bilərsiniz), bu o deməkdir ki, tokenlər daxili istifadə üçün təhlükəsiz olsa da, sənaye (kütləvi) istifadə üçün təhlükəlidir.

ZeroTech-də Apple Safari və müştəri sertifikatlarını veb-soketlərlə necə birləşdirdik

4. Müvəqqəti sessiya tokenləri artıq hazır Apache modulları kimi həyata keçirilib.

Əvvəlki iterasiyadan bir əhəmiyyətli problem qaldı - token yaşlanmasına nəzarət etmək mümkün deyil.

Biz bunu aşağıdakı sözlərə görə yerinə yetirən hazır modul axtarırıq: apache token json two factor auth

Bəli, hazır modullar var, lakin onların hamısı xüsusi hərəkətlərə bağlıdır və sessiyanın başlaması və əlavə kukilər şəklində artefaktlara malikdir. Yəni bir müddət deyil.
Axtarışa beş saat çəkdik, bu da konkret nəticə vermədi.

5. Müvəqqəti seans tokenləri qarşılıqlı əlaqələrin strukturunu məntiqi şəkildə tərtib etməklə həyata keçirilə bilər.

Hazır modullar çox mürəkkəbdir, çünki bizə yalnız bir neçə funksiya lazımdır.

Bununla belə, tarixlə bağlı problem ondan ibarətdir ki, Apache-nin daxili funksiyaları gələcəkdən tarix yaratmağa imkan vermir və köhnəlmə yoxlanarkən daxili funksiyalarda heç bir riyazi əlavə/çıxma yoxdur.

Yəni yaza bilməzsiniz:

(%{env:zt-cert-date} + 30) > %{DATE}

Siz yalnız iki rəqəmi müqayisə edə bilərsiniz.

Safari probleminin həllini axtararkən maraqlı bir məqalə tapdım: HomeAssistant-ın müştəri sertifikatları ilə qorunması (Safari/iOS ilə işləyir)
Nginx üçün Lua kod nümunəsini təsvir edir və məlum oldu ki, hashing üçün hmac duzlama metodunun istifadəsi istisna olmaqla, artıq həyata keçirdiyimiz konfiqurasiya hissəsinin məntiqini çox təkrarlayır ( bu Apache-də tapılmadı).

Aydın oldu ki, Lua aydın məntiqə malik bir dildir və Apache üçün sadə bir şey etmək mümkündür:

Nginx və Apache ilə fərqi öyrəndikdən sonra:

Lua dili istehsalçısının mövcud funksiyaları:
22.1 – Tarix və Saat

Gələcəkdən indiki ilə müqayisə etmək üçün bir tarix təyin etmək üçün kiçik bir Lua faylında env dəyişənlərini təyin etmək üçün bir yol tapdıq.

Sadə bir Lua skripti belə görünür:

require 'apache2'

function handler(r)
    local fmt = '%Y%m%d%H%M%S'
    local timeout = 3600 -- 1 hour

    r.notes['zt-cert-timeout'] = timeout
    r.notes['zt-cert-date-next'] = os.date(fmt,os.time()+timeout)
    r.notes['zt-cert-date-halfnext'] = os.date(fmt,os.time()+ (timeout/2))
    r.notes['zt-cert-date-now'] = os.date(fmt,os.time())

    return apache2.OK
end

Köhnə Kuki (token) bitməzdən əvvəl vaxtın yarısı gələndə kukilərin sayının optimallaşdırılması və işarənin dəyişdirilməsi ilə ümumilikdə hər şey belədir:

SSLVerifyClient optional

#LuaScope thread
#generate event variables zt-cert-date-next
LuaHookAccessChecker /usr/local/etc/apache24/sslincludes/websocket_token.lua handler early

#запрещаем без сертификата что-то ещё, кроме webscoket
RewriteEngine on
RewriteCond %{SSL:SSL_CLIENT_VERIFY} !=SUCCESS
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule     .? - [F]
#ErrorDocument 403 "You need a client side certificate issued by CAcert to access this site"

#websocket for safari without certauth
<If "%{SSL:SSL_CLIENT_VERIFY} != 'SUCCESS'">
<If "%{HTTP:Upgrade} = 'websocket'">
    SetEnvIf Cookie "zt-cert=([^,;]+),([^,;]+),[^,;]+,([^,;]+)" zt-cert-sha1=$1 zt-cert-date=$2 zt-cert-uid=$3

    <RequireAll>
        Require expr %{sha1:salt1%{env:zt-cert-date}salt3%{env:zt-cert-uid}salt2} == %{env:zt-cert-sha1}
        Require expr %{env:zt-cert-sha1} =~ /^.{40}$/
        Require expr %{env:zt-cert-date} -ge %{env:zt-cert-date-now}
    </RequireAll>
   
    #замещаем авторизацию по владельцу сертификата на авторизацию по номеру протокола
    SSLUserName SSl_PROTOCOL
    SSLOptions -FakeBasicAuth
</If>
</If>

<If "%{SSL:SSL_CLIENT_VERIFY} = 'SUCCESS'">
<If "%{HTTP:Upgrade} != 'websocket'">
    SetEnvIf Cookie "zt-cert=([^,;]+),[^,;]+,([^,;]+)" HAVE_zt-cert-sha1=$1 HAVE_zt-cert-date-halfnow=$2
    SetEnvIfExpr "env('HAVE_zt-cert-date-halfnow') -ge %{TIME} && env('HAVE_zt-cert-sha1')=~/.{40}/" HAVE_zt-cert-sha1-found=1

    Define zt-cert "path=/;Max-Age=%{env:zt-cert-timeout};HttpOnly;Secure;SameSite=Strict"
    Define dates_user "%{env:zt-cert-date-next},%{env:zt-cert-date-halfnext},%{SSL_CLIENT_S_DN_CN}"
    Header set Set-Cookie "expr=zt-cert=%{sha1:salt1%{env:zt-cert-date-next}sal3%{SSL_CLIENT_S_DN_CN}salt2},${dates_user};${zt-cert}" env=!HAVE_zt-cert-sha1-found
</If>
</If>

SetEnvIfExpr "env('HAVE_zt-cert-date-halfnow') -ge %{TIME} && env('HAVE_zt-cert-sha1')=~/.{40}/" HAVE_zt-cert-sha1-found=1
работает,

а так работать не будет
SetEnvIfExpr "env('HAVE_zt-cert-date-halfnow') -ge  env('zt-cert-date-now') && env('HAVE_zt-cert-sha1')=~/.{40}/" HAVE_zt-cert-sha1-found=1 

Çünki LuaHookAccessChecker yalnız Nginx-dən bu məlumat əsasında giriş yoxlamalarından sonra aktivləşdiriləcək.

ZeroTech-də Apple Safari və müştəri sertifikatlarını veb-soketlərlə necə birləşdirdik

Mənbəyə keçid Şəkillər.

Başqa bir məqam.

Ümumiyyətlə, Apache (ehtimal ki, Nginx) konfiqurasiyasında direktivlərin hansı ardıcıllıqla yazılmasının əhəmiyyəti yoxdur, çünki sonda hər şey istifadəçidən emal sxeminə uyğun gələn sorğunun sırasına əsasən çeşidlənəcəkdir. Lua skriptləri.

Tamamlanma:

İcradan sonra görünən vəziyyət (məqsəd):
xidmətlərin və infrastrukturun idarə edilməsi birləşdirilmiş və təhlükəsiz, əlavə proqramlar (VPN) olmadan IOS-da mobil telefondan mümkündür.

Məqsəd əldə edildi, veb rozetkalar işləyir və sertifikatdan az olmayan bir təhlükəsizlik səviyyəsinə malikdir.

ZeroTech-də Apple Safari və müştəri sertifikatlarını veb-soketlərlə necə birləşdirdik

Mənbə: www.habr.com

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