Mi, a ZeroTech, hogyan kapcsoltuk össze az Apple Safarit és az ügyféltanúsítványokat websocketekkel

A cikk hasznos lesz azoknak, akik:

  • tudja, mi az a Client Cert, és megérti, miért van szüksége websocketekre a mobil Safariban;
  • Webes szolgáltatásokat szeretnék közzétenni korlátozott körnek vagy csak magamnak;
  • úgy gondolja, hogy valaki már mindent megtett, és szeretné egy kicsit kényelmesebbé és biztonságosabbá tenni a világot.

A websocket története körülbelül 8 évvel ezelőtt kezdődött. Korábban a módszereket hosszú http kérések (valójában válaszok) formájában alkalmazták: a felhasználó böngészője kérést küldött a szervernek, és várta, hogy válaszoljon valamire, a válasz után újra csatlakozott és várt. De aztán megjelentek a websocket-ek.

Mi, a ZeroTech, hogyan kapcsoltuk össze az Apple Safarit és az ügyféltanúsítványokat websocketekkel

Néhány éve kifejlesztettük a saját implementációnkat tiszta PHP-ben, amely nem tud https kéréseket használni, mivel ez a link réteg. Nem sokkal ezelőtt szinte az összes webszerver megtanulta a https-en keresztüli proxy kéréseket, és támogatja a connect:upgrade funkciót.

Amikor ez megtörtént, a websocket szinte az alapértelmezett szolgáltatása lett a SPA alkalmazásoknak, mert mennyire kényelmes a szerver kezdeményezésére tartalmat szolgáltatni a felhasználónak (üzenet továbbítása másik felhasználótól, vagy kép, dokumentum, prezentáció új verziójának letöltése) hogy valaki más éppen szerkeszt) .

Bár az ügyféltanúsítvány már jó ideje létezik, továbbra is gyengén támogatott, mivel sok problémát okoz, amikor megpróbálja megkerülni. És (esetleg :slightly_smiling_face: ) ezért nem akarják az IOS böngészők (a Safari kivételével) használni, és a helyi tanúsítványtárolóból kérik. A tanúsítványok számos előnnyel rendelkeznek a bejelentkezési/átlépési vagy ssh-kulcsokhoz, illetve a szükséges portok tűzfalon keresztüli bezárásához képest. De nem erről van szó.

Az iOS rendszeren a tanúsítvány telepítése meglehetősen egyszerű (nem nélkülözhetetlen), de általában az utasítások szerint történik, amelyekből sok van az interneten, és amelyek csak a Safari böngészőhöz érhetők el. Sajnos a Safari nem tudja, hogyan kell a Client Сert websocketekhez használni, de az interneten számos utasítás található egy ilyen tanúsítvány létrehozására, de a gyakorlatban ez elérhetetlen.

Mi, a ZeroTech, hogyan kapcsoltuk össze az Apple Safarit és az ügyféltanúsítványokat websocketekkel

A websocket megértéséhez a következő tervet használtuk: probléma/hipotézis/megoldás.

probléma: nem támogatja a web socketeket, amikor proxy kéréseket küld olyan erőforrásokhoz, amelyeket ügyféltanúsítvány véd a Safari mobilböngészőben az IOS-hez és más olyan alkalmazásokhoz, amelyek engedélyezték a tanúsítványtámogatást.

Hipotézisek:

  1. Egy ilyen kivétel beállítható úgy, hogy tanúsítványokat használjon (tudván, hogy nem lesz) a belső/külső proxy erőforrások websocketjeihez.
  2. A websocketeknél egyedi, biztonságos és védhető kapcsolatot hozhat létre olyan ideiglenes munkamenetekkel, amelyek normál (nem websocket) böngészőkérés során jönnek létre.
  3. Az ideiglenes munkamenetek egyetlen proxy webszerverrel valósíthatók meg (csak beépített modulok és funkciók).
  4. Az ideiglenes munkamenet-tokeneket már kész Apache modulként implementálták.
  5. Az ideiglenes munkamenet tokenek az interakciós struktúra logikus megtervezésével valósíthatók meg.

Látható állapot megvalósítás után.

A munka célja: a szolgáltatások és az infrastruktúra kezelésének elérhetőnek kell lennie egy mobiltelefonról IOS rendszeren további programok (például VPN) nélkül, egységesen és biztonságosan.

További cél: időt és erőforrásokat/telefonforgalmat takaríthat meg (egyes webaljzatok nélküli szolgáltatások szükségtelen kéréseket generálnak) a tartalom gyorsabb kézbesítésével a mobil interneten.

Hogyan ellenőrizzük?

1. Nyitó oldalak:

— например, 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. Vagy a fejlesztői konzolon:

Mi, a ZeroTech, hogyan kapcsoltuk össze az Apple Safarit és az ügyféltanúsítványokat websocketekkel

Hipotézis tesztelés:

1. Egy ilyen kivétel beállítható úgy, hogy tanúsítványokat használjon (tudván, hogy nem lesz) a belső/külső proxy erőforrások websocketjeihez.

2 megoldást találtunk itt:

a) szinten

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

hozzáférési szintet módosítani.

Ennek a módszernek a következő árnyalatai vannak:

  • A tanúsítvány ellenőrzésére a proxy erőforráshoz intézett kérés, azaz a kérés utáni kézfogás után kerül sor. Ez azt jelenti, hogy a proxy először betölti, majd leállítja a védett szolgáltatás kérését. Ez rossz, de nem kritikus;
  • A http2 protokollban. Még mindig tervezetben van, és a böngészőgyártók nem tudják, hogyan valósítsák meg #info a tls1.3-ról http2 kézfogás után (most nem működik) Az RFC 8740 implementálása „TLS 1.3 használata HTTP/2-vel”;
  • Nem világos, hogyan lehet egységesíteni ezt a feldolgozást.

b) Alapszinten engedélyezze az ssl-t tanúsítvány nélkül.

Az SSLVerifyClient követelmény => SSLVerifyClient opcionális, de ez csökkenti a proxyszerver biztonsági szintjét, mivel az ilyen kapcsolat feldolgozása tanúsítvány nélkül történik. Továbbra is megtagadhatja azonban a proxyszolgáltatásokhoz való hozzáférést a következő direktívával:

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"

További részletek az ssl-ről szóló cikkben találhatók: Apache Server Client Certificate Authentication

Mindkét lehetőséget teszteltük, a „b” opciót a sokoldalúsága és a http2 protokollal való kompatibilitása miatt választottuk.

A hipotézis igazolásának befejezéséhez sok kísérletet kellett végezni a konfigurációval; a következő terveket teszteltük:

ha = megköveteli = átírja

Az eredmény a következő alapterv:

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>

Figyelembe véve a tanúsítvány tulajdonosának meglévő jogosultságát, de hiányzó tanúsítvánnyal, egy nem létező tanúsítványtulajdonost kellett hozzáadnom az egyik elérhető SSl_PROTOCOL változó formájában (az SSL_CLIENT_S_DN_CN helyett), további részletek a dokumentációban:

Apache modul mod_ssl

Mi, a ZeroTech, hogyan kapcsoltuk össze az Apple Safarit és az ügyféltanúsítványokat websocketekkel

2. Websocket esetén egyedi, biztonságos és védett kapcsolatot hozhat létre olyan ideiglenes munkamenetek segítségével, amelyek normál (nem websocket) böngészőkérés során jönnek létre.

A korábbi tapasztalatok alapján egy további szakaszt kell hozzáadnia a konfigurációhoz, hogy ideiglenes tokeneket készítsen a webaljzat-kapcsolatokhoz egy normál (nem webes socket) kérés során.

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

A tesztelés azt mutatta, hogy működik. Lehetőség van cookie-k átvitelére saját magának a felhasználó böngészőjén keresztül.

3. Ideiglenes munkamenetek egy proxy webszerverrel valósíthatók meg (csak beépített modulok és funkciók).

Amint azt korábban megtudtuk, az Apache meglehetősen sok alapvető funkcióval rendelkezik, amelyek lehetővé teszik feltételes konstrukciók létrehozását. Szükségünk van azonban arra, hogy megvédjük adatainkat, amíg azok a felhasználó böngészőjében vannak, ezért meghatározzuk, hogy mit és miért tároljunk, és milyen beépített funkciókat fogunk használni:

  • Olyan tokenre van szükségünk, amelyet nem lehet könnyen dekódolni.
  • Szükségünk van egy tokenre, amelybe az elavulás be van építve, és amely képes ellenőrizni az elavulást a szerveren.
  • Szükségünk van egy tokenre, amely a tanúsítvány tulajdonosához lesz társítva.

Ehhez egy hash-függvényre, egy sóra és egy dátumra van szükség a token öregítéséhez. A dokumentáció alapján Kifejezések az Apache HTTP szerveren mindet a dobozból megkaptuk sha1 és %{TIME}.

Az eredmény a következő design lett:

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

A célt sikerült elérni, de problémák vannak a szerver elavulásával (egy éves Cookie-t használhatsz), ami azt jelenti, hogy a tokenek bár belső használatra biztonságosak, ipari (tömeges) felhasználásra nem biztonságosak.

Mi, a ZeroTech, hogyan kapcsoltuk össze az Apple Safarit és az ügyféltanúsítványokat websocketekkel

4. Az ideiglenes munkamenet-tokeneket már kész Apache modulként implementálták.

Egy jelentős probléma maradt az előző iterációból – a token elöregedésének szabályozásának képtelensége.

Olyan kész modult keresünk, ami ezt a következő szavakkal teszi: apache token json two factor auth

Igen, vannak kész modulok, de mindegyik konkrét műveletekhez van kötve, és tartalmaznak műtermékeket munkamenet indítása és további cookie-k formájában. Vagyis egy darabig nem.
Öt órát vett igénybe a keresés, ami nem adott konkrét eredményt.

5. Az ideiglenes munkamenet tokenek az interakciók szerkezetének logikus megtervezésével valósíthatók meg.

A kész modulok túl bonyolultak, mert csak néhány funkcióra van szükségünk.

Ennek ellenére a dátummal az a probléma, hogy az Apache beépített függvényei nem teszik lehetővé a dátum generálását a jövőből, és a beépített függvényekben nincs matematikai összeadás/kivonás az elavulás ellenőrzésekor.

Vagyis nem írhatod:

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

Csak két számot lehet összehasonlítani.

Miközben a Safari probléma megoldását kerestem, találtam egy érdekes cikket: A HomeAssistant biztosítása ügyféltanúsítványokkal (Safari/iOS rendszerrel működik)
Leír egy példát a Lua for Nginx kódjára, amely, mint kiderült, nagyon megismétli a konfiguráció azon részének logikáját, amelyet már megvalósítottunk, kivéve a hmac sózási módszer használatát a kivonatoláshoz ( ez nem található az Apache-ban).

Világossá vált, hogy a Lua egy tiszta logikával rendelkező nyelv, és az Apache számára is lehet valami egyszerűt tenni:

Az Nginx és az Apache közötti különbség tanulmányozása után:

És elérhető funkciók a Lua nyelv gyártójától:
22.1 – Dátum és idő

Találtunk egy módot az env változók beállítására egy kis Lua fájlban, hogy beállíthassunk egy jövőbeli dátumot a jelenlegivel való összehasonlításhoz.

Így néz ki egy egyszerű Lua szkript:

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

És ez így működik összességében, a Cookie-k számának optimalizálásával és a token cseréjével, amikor a régi Cookie (token) lejárta előtti idő fele eltelik:

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 

Mivel a LuaHookAccessChecker csak az Nginx információi alapján történő hozzáférés-ellenőrzések után aktiválódik.

Mi, a ZeroTech, hogyan kapcsoltuk össze az Apple Safarit és az ügyféltanúsítványokat websocketekkel

Link a forráshoz kép.

Egy másik pont.

Általában nem mindegy, hogy az Apache (valószínűleg az Nginx) konfigurációban milyen sorrendben vannak megírva a direktívák, mivel végül minden a felhasználótól érkező kérés sorrendje alapján lesz rendezve, ami megfelel a feldolgozási sémának. Lua szkriptek.

Befejezés:

Látható állapot megvalósítás után (cél):
a szolgáltatások és az infrastruktúra kezelése IOS-en lévő mobiltelefonról további programok (VPN) nélkül, egységesen és biztonságosan elérhető.

A célt elértük, a web socket-ek működnek, és legalább egy tanúsítvánnyal rendelkeznek.

Mi, a ZeroTech, hogyan kapcsoltuk össze az Apple Safarit és az ügyféltanúsítványokat websocketekkel

Forrás: will.com

Hozzászólás