Hoe ons by ZeroTech Apple Safari en kliëntsertifikate met websockets verbind het

Die artikel sal nuttig wees vir diegene wat:

  • weet wat Kliëntsertifikaat is en verstaan ​​hoekom dit websockets op mobiele Safari benodig;
  • Ek wil graag webdienste aan 'n beperkte kring mense of net aan myself publiseer;
  • dink dat alles reeds deur iemand gedoen is, en wil graag die wêreld 'n bietjie geriefliker en veiliger maak.

Die geskiedenis van websockets het ongeveer 8 jaar gelede begin. Voorheen is metodes gebruik in die vorm van lang http-versoeke (eintlik antwoorde): die gebruiker se blaaier het 'n versoek na die bediener gestuur en gewag dat dit iets beantwoord, na die antwoord het dit weer gekoppel en gewag. Maar toe verskyn websockets.

Hoe ons by ZeroTech Apple Safari en kliëntsertifikate met websockets verbind het

'n Paar jaar gelede het ons ons eie implementering in suiwer PHP ontwikkel, wat nie https-versoeke kan gebruik nie, aangesien dit die skakellaag is. Nie lank gelede nie, het byna alle webbedieners geleer om proxy-versoeke oor https te gebruik en konneksie: opgradering te ondersteun.

Toe dit gebeur het, het websockets amper die verstekdiens vir SPA-toepassings geword, want hoe gerieflik is dit om inhoud aan die gebruiker te verskaf op die inisiatief van die bediener (stuur 'n boodskap van 'n ander gebruiker af of laai 'n nuwe weergawe van 'n prent, dokument, aanbieding af wat iemand anders tans redigeer) .

Alhoewel kliëntsertifikaat al 'n geruime tyd bestaan, word dit steeds swak ondersteun, aangesien dit baie probleme skep wanneer dit probeer omseil word. En (moontlik :slightly_smiling_face: ) dis hoekom IOS-blaaiers (almal behalwe Safari) dit nie wil gebruik nie en dit van die plaaslike sertifikaatwinkel wil aanvra. Sertifikate het baie voordele in vergelyking met login/pass of ssh sleutels of die sluiting van die nodige poorte deur 'n firewall. Maar dit is nie waaroor dit gaan nie.

Op iOS is die prosedure om 'n sertifikaat te installeer redelik eenvoudig (nie sonder besonderhede nie), maar oor die algemeen word dit volgens instruksies gedoen, waarvan daar baie op die internet is en wat slegs vir die Safari-blaaier beskikbaar is. Ongelukkig weet Safari nie hoe om Client Сert vir websockets te gebruik nie, maar daar is baie instruksies op die internet oor hoe om so 'n sertifikaat te skep, maar in die praktyk is dit onbereikbaar.

Hoe ons by ZeroTech Apple Safari en kliëntsertifikate met websockets verbind het

Om websockets te verstaan, het ons die volgende plan gebruik: probleem/hipotese/oplossing.

probleem: daar is geen ondersteuning vir websockets wanneer versoeke gevolmagtig word na hulpbronne wat beskerm word deur 'n kliëntsertifikaat op die Safari-selfoonblaaier vir IOS en ander toepassings wat sertifikaatondersteuning geaktiveer het nie.

Hipoteses:

  1. Dit is moontlik om so 'n uitsondering op te stel om sertifikate te gebruik (met die wete dat daar geen sal wees nie) na websockets van interne/eksterne gevolmagtigde hulpbronne.
  2. Vir websockets kan jy 'n unieke, veilige en verdedigbare verbinding maak deur tydelike sessies te gebruik wat tydens 'n normale (nie-websocket) blaaierversoek gegenereer word.
  3. Tydelike sessies kan geïmplementeer word met een instaanbediener-webbediener (slegs ingeboude modules en funksies).
  4. Tydelike sessie-tokens is reeds as klaargemaakte Apache-modules geïmplementeer.
  5. Tydelike sessie-tokens kan geïmplementeer word deur die interaksiestruktuur logies te ontwerp.

Sigbare toestand na implementering.

Doel: bestuur van dienste en infrastruktuur moet toeganklik wees vanaf 'n selfoon op IOS sonder bykomende programme (soos VPN), verenig en veilig.

Bykomende doelwit: spaar tyd en hulpbronne/foonverkeer (sommige dienste sonder websockets genereer onnodige versoeke) met vinniger aflewering van inhoud op die mobiele internet.

Hoe om na te gaan?

1. Opening bladsye:

— например, 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. Of in die ontwikkelaarkonsole:

Hoe ons by ZeroTech Apple Safari en kliëntsertifikate met websockets verbind het

Hipotese toetsing:

1. Dit is moontlik om so 'n uitsondering op te stel om sertifikate te gebruik (met die wete dat daar geen sal wees nie) na websockets van interne/eksterne gevolmagtigde hulpbronne.

2 oplossings is hier gevind:

a) Op die vlak

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

verander toegangsvlak.

Hierdie metode het die volgende nuanses:

  • Sertifikaatverifikasie vind plaas na 'n versoek aan die gevolmagtigde hulpbron, dit wil sê, na-versoek-handdruk. Dit beteken dat die gevolmagtigde eers die versoek na die beskermde diens sal laai en dan afsny. Dit is sleg, maar nie krities nie;
  • In die http2-protokol. Dit is nog in konsep, en blaaiervervaardigers weet nie hoe om dit te implementeer nie # info about tls1.3 http2 post handshake (werk nie nou nie) Implementeer RFC 8740 "Gebruik TLS 1.3 met HTTP/2";
  • Dit is nie duidelik hoe om hierdie verwerking te verenig nie.

b) Op 'n basiese vlak, laat ssl toe sonder 'n sertifikaat.

SSLVerifyClient vereis => SSLVerifyClient opsioneel, maar dit verminder die sekuriteitsvlak van die instaanbediener, aangesien so 'n verbinding sonder 'n sertifikaat verwerk sal word. U kan egter verder toegang tot gevolmagtigde dienste weier met die volgende opdrag:

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"

Meer gedetailleerde inligting kan gevind word in die artikel oor ssl: Apache Server Kliënt Sertifikaat Verifikasie

Beide opsies is getoets, opsie "b" is gekies vir sy veelsydigheid en versoenbaarheid met die http2-protokol.

Om die verifikasie van hierdie hipotese te voltooi, het dit baie eksperimente met die konfigurasie geneem; die volgende ontwerpe is getoets:

if = vereis = herskryf

Die resultaat is die volgende basiese ontwerp:

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>

Met inagneming van die bestaande magtiging deur die sertifikaateienaar, maar met 'n ontbrekende sertifikaat, moes ek 'n nie-bestaande sertifikaateienaar byvoeg in die vorm van een van die beskikbare veranderlikes SSl_PROTOCOL (in plaas van SSL_CLIENT_S_DN_CN), meer besonderhede in die dokumentasie:

Apache-module mod_ssl

Hoe ons by ZeroTech Apple Safari en kliëntsertifikate met websockets verbind het

2. Vir websockets kan jy 'n unieke, veilige en beskermde verbinding maak deur tydelike sessies te gebruik wat tydens 'n normale (nie-websocket) blaaierversoek gegenereer word.

Gebaseer op vorige ondervinding, moet jy 'n bykomende afdeling by die konfigurasie voeg om tydelike tekens vir websokverbindings voor te berei tydens 'n gereelde (nie-websok) versoek.

#подготовка передача себе С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>

Toetsing het getoon dat dit werk. Dit is moontlik om koekies na jouself oor te dra deur die gebruiker se blaaier.

3. Tydelike sessies kan geïmplementeer word met behulp van een instaanbediener-webbediener (slegs ingeboude modules en funksies).

Soos ons vroeër uitgevind het, het Apache nogal baie kernfunksies waarmee u voorwaardelike konstrukte kan skep. Ons het egter middele nodig om ons inligting te beskerm terwyl dit in die gebruiker se blaaier is, so ons bepaal wat om te stoor en hoekom, en watter ingeboude funksies ons sal gebruik:

  • Ons het 'n teken nodig wat nie maklik gedekodeer kan word nie.
  • Ons benodig 'n teken wat veroudering ingebou het en die vermoë om veroudering op die bediener na te gaan.
  • Ons benodig 'n teken wat met die eienaar van die sertifikaat geassosieer sal word.

Dit vereis 'n hashing-funksie, 'n sout en 'n datum om die teken te verouder. Gebaseer op die dokumentasie Uitdrukkings in Apache HTTP-bediener ons het dit alles uit die boks sha1 en %{TIME}.

Die resultaat was hierdie ontwerp:

#нет сертификата, и обращение к 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>

Die doelwit is bereik, maar daar is probleme met die veroudering van die bediener (jy kan 'n jaar oue koekie gebruik), wat beteken dat die tokens, hoewel veilig vir interne gebruik, onveilig is vir industriële (massa) gebruik.

Hoe ons by ZeroTech Apple Safari en kliëntsertifikate met websockets verbind het

4. Tydelike sessietokens is reeds as klaargemaakte Apache-modules geïmplementeer.

Een beduidende probleem het oorgebly van die vorige iterasie - die onvermoë om tekenveroudering te beheer.

Ons is op soek na 'n klaargemaakte module wat dit doen, volgens die woorde: apache token json two factor auth

Ja, daar is klaargemaakte modules, maar hulle is almal gekoppel aan spesifieke aksies en het artefakte in die vorm van die begin van 'n sessie en bykomende koekies. Dit wil sê, nie vir 'n rukkie nie.
Dit het ons vyf uur geneem om te soek, wat nie 'n konkrete resultaat opgelewer het nie.

5. Tydelike sessietokens kan geïmplementeer word deur die struktuur van interaksies logies te ontwerp.

Klaargemaakte modules is te kompleks, want ons benodig net 'n paar funksies.

Dit gesê, die probleem met die datum is dat Apache se ingeboude funksies nie toelaat dat 'n datum uit die toekoms gegenereer word nie, en daar is geen wiskundige optelling/aftrekking in die ingeboude funksies wanneer nagegaan word vir veroudering nie.

Dit wil sê, jy kan nie skryf nie:

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

Jy kan net twee getalle vergelyk.

Terwyl ek gesoek het na 'n oplossing vir die Safari-probleem, het ek 'n interessante artikel gevind: Beveilig HomeAssistant met kliëntsertifikate (werk met Safari/iOS)
Dit beskryf 'n voorbeeld van kode in Lua vir Nginx, en wat, soos dit geblyk het, baie herhaal die logika van daardie deel van die konfigurasie wat ons reeds geïmplementeer het, met die uitsondering van die gebruik van die hmac-soutmetode vir hashing ( dit is nie in Apache gevind nie).

Dit het duidelik geword dat Lua 'n taal met duidelike logika is, en dit is moontlik om iets eenvoudig vir Apache te doen:

Nadat u die verskil met Nginx en Apache bestudeer het:

En beskikbare funksies van die Lua-taalvervaardiger:
22.1 – Datum en Tyd

Ons het 'n manier gevind om env-veranderlikes in 'n klein Lua-lêer te stel om 'n datum uit die toekoms te stel om met die huidige een te vergelyk.

Dit is hoe 'n eenvoudige Lua-skrif lyk:

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

En dit is hoe dit alles in totaal werk, met optimalisering van die aantal koekies en vervanging van die token wanneer die helfte van die tyd kom voordat die ou koekie (token) verval:

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 

Omdat LuaHookAccessChecker eers geaktiveer sal word na toegangskontroles gebaseer op hierdie inligting van Nginx.

Hoe ons by ZeroTech Apple Safari en kliëntsertifikate met websockets verbind het

Skakel na bron beeld.

Nog 'n punt.

Oor die algemeen maak dit nie saak in watter volgorde die aanwysings in die Apache (waarskynlik ook Nginx)-konfigurasie geskryf word nie, aangesien alles uiteindelik gesorteer sal word op grond van die volgorde van die versoek van die gebruiker, wat ooreenstem met die skema vir verwerking Lua skrifte.

Voltooiing:

Sigbare toestand na implementering (doelwit):
bestuur van dienste en infrastruktuur is beskikbaar vanaf 'n selfoon op IOS sonder bykomende programme (VPN), verenig en veilig.

Die doelwit is bereik, websockets werk en het 'n vlak van sekuriteit nie minder nie as 'n sertifikaat.

Hoe ons by ZeroTech Apple Safari en kliëntsertifikate met websockets verbind het

Bron: will.com

Voeg 'n opmerking