Bagaimana kami di ZeroTech menghubungkan Apple Safari dan sertifikat klien dengan soket web

Artikel ini akan bermanfaat bagi mereka yang:

  • mengetahui apa itu Sertifikat Klien dan memahami mengapa ia memerlukan soket web di Safari seluler;
  • Saya ingin mempublikasikan layanan web ke kalangan terbatas atau hanya untuk diri saya sendiri;
  • berpikir bahwa semuanya telah dilakukan oleh seseorang, dan ingin membuat dunia menjadi lebih nyaman dan aman.

Sejarah websockets dimulai sekitar 8 tahun yang lalu. Sebelumnya, metode yang digunakan dalam bentuk permintaan http panjang (sebenarnya respons): browser pengguna mengirim permintaan ke server dan menunggu untuk menjawab sesuatu, setelah respons terhubung kembali dan menunggu. Tapi kemudian soket web muncul.

Bagaimana kami di ZeroTech menghubungkan Apple Safari dan sertifikat klien dengan soket web

Beberapa tahun yang lalu, kami mengembangkan implementasi kami sendiri dalam PHP murni, yang tidak dapat menggunakan permintaan https, karena ini adalah lapisan tautan. Belum lama ini, hampir semua server web mempelajari permintaan proxy melalui https dan mendukung koneksi: peningkatan.

Ketika ini terjadi, websockets hampir menjadi layanan default untuk aplikasi SPA, karena betapa nyamannya menyediakan konten kepada pengguna atas inisiatif server (mengirimkan pesan dari pengguna lain atau mengunduh versi baru dari gambar, dokumen, presentasi yang sedang diedit orang lain).

Meskipun Sertifikat Klien telah ada selama beberapa waktu, namun dukungannya masih kurang, karena menimbulkan banyak masalah saat mencoba melewatinya. Dan (mungkin :slightly_smiling_face: ) itulah sebabnya browser iOS (semua kecuali Safari) tidak ingin menggunakannya dan memintanya dari toko sertifikat lokal. Sertifikat memiliki banyak keunggulan dibandingkan dengan kunci login/pass atau ssh atau menutup port yang diperlukan melalui firewall. Tapi masalahnya bukan itu.

Di iOS, prosedur pemasangan sertifikat cukup sederhana (bukan tanpa spesifik), tetapi secara umum dilakukan sesuai petunjuk, yang banyak terdapat di Internet dan hanya tersedia untuk browser Safari. Sayangnya, Safari tidak mengetahui cara menggunakan Sertifikat Klien untuk soket web, tetapi ada banyak instruksi di Internet tentang cara membuat sertifikat semacam itu, tetapi dalam praktiknya hal ini tidak mungkin tercapai.

Bagaimana kami di ZeroTech menghubungkan Apple Safari dan sertifikat klien dengan soket web

Untuk memahami websockets, kami menggunakan rencana berikut: masalah/hipotesis/solusi.

Masalah: tidak ada dukungan untuk soket web saat memproksi permintaan ke sumber daya yang dilindungi oleh sertifikat klien di browser seluler Safari untuk iOS dan aplikasi lain yang telah mengaktifkan dukungan sertifikat.

Hipotesis:

  1. Dimungkinkan untuk mengonfigurasi pengecualian seperti itu untuk menggunakan sertifikat (mengetahui bahwa tidak akan ada sertifikat) ke soket web sumber daya proksi internal/eksternal.
  2. Untuk websockets, Anda dapat membuat koneksi yang unik, aman, dan dapat dipertahankan menggunakan sesi sementara yang dihasilkan selama permintaan browser normal (non-websocket).
  3. Sesi sementara dapat diimplementasikan menggunakan satu server web proxy (hanya modul dan fungsi bawaan).
  4. Token sesi sementara telah diimplementasikan sebagai modul Apache siap pakai.
  5. Token sesi sementara dapat diimplementasikan dengan merancang struktur interaksi secara logis.

Keadaan terlihat setelah implementasi.

Objektif: pengelolaan layanan dan infrastruktur harus dapat diakses dari ponsel di iOS tanpa program tambahan (seperti VPN), terpadu dan aman.

Tujuan tambahan: menghemat waktu dan sumber daya/lalu lintas telepon (beberapa layanan tanpa soket web menghasilkan permintaan yang tidak perlu) dengan pengiriman konten yang lebih cepat di Internet seluler.

Bagaimana cara memeriksanya?

1. Halaman pembuka:

β€” Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, 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. Atau di konsol pengembang:

Bagaimana kami di ZeroTech menghubungkan Apple Safari dan sertifikat klien dengan soket web

Pengujian hipotesis:

1. Dimungkinkan untuk mengonfigurasi pengecualian tersebut untuk menggunakan sertifikat (mengetahui bahwa tidak akan ada sertifikat) ke soket web sumber daya proksi internal/eksternal.

2 solusi ditemukan di sini:

a) Pada tingkat

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

mengubah tingkat akses.

Metode ini memiliki nuansa sebagai berikut:

  • Verifikasi sertifikat terjadi setelah permintaan ke sumber daya yang diproksi, yaitu jabat tangan pasca permintaan. Ini berarti proxy akan memuat terlebih dahulu dan kemudian memotong permintaan ke layanan yang dilindungi. Ini buruk, tapi tidak kritis;
  • Dalam protokol http2. Ini masih dalam rancangan, dan produsen browser tidak tahu bagaimana menerapkannya #info tentang tls1.3 http2 post handshake (tidak berfungsi sekarang) Menerapkan RFC 8740 "Menggunakan TLS 1.3 dengan HTTP/2";
  • Tidak jelas bagaimana menyatukan proses ini.

b) Pada tingkat dasar, izinkan SSL tanpa sertifikat.

SSLVerifyClient memerlukan => SSLVerifyClient opsional, tetapi ini mengurangi tingkat keamanan server proxy, karena koneksi tersebut akan diproses tanpa sertifikat. Namun, Anda dapat menolak akses lebih lanjut ke layanan proksi dengan arahan berikut:

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"

Informasi lebih detail dapat ditemukan di artikel tentang ssl: Otentikasi Sertifikat Klien Server Apache

Kedua opsi diuji, opsi β€œb” dipilih karena keserbagunaan dan kompatibilitasnya dengan protokol http2.

Untuk melengkapi verifikasi hipotesis ini, diperlukan banyak eksperimen dengan konfigurasi; desain berikut diuji:

jika = memerlukan = tulis ulang

Hasilnya adalah desain dasar berikut:

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>

Dengan mempertimbangkan otorisasi yang ada oleh pemilik sertifikat, tetapi dengan sertifikat yang hilang, saya harus menambahkan pemilik sertifikat yang tidak ada dalam bentuk salah satu variabel yang tersedia SSl_PROTOCOL (bukan SSL_CLIENT_S_DN_CN), lebih detailnya di dokumentasi:

Modul Apache mod_ssl

Bagaimana kami di ZeroTech menghubungkan Apple Safari dan sertifikat klien dengan soket web

2. Untuk websockets, Anda dapat membuat koneksi unik, aman dan terlindungi menggunakan sesi sementara yang dihasilkan selama permintaan browser normal (non-websocket).

Berdasarkan pengalaman sebelumnya, Anda perlu menambahkan bagian tambahan ke konfigurasi untuk menyiapkan token sementara untuk koneksi soket web selama permintaan reguler (soket non-web).

#ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° сСбС Π‘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>

Pengujian menunjukkan bahwa itu berhasil. Dimungkinkan untuk mentransfer Cookie ke diri Anda sendiri melalui browser pengguna.

3. Sesi sementara dapat diimplementasikan menggunakan satu server web proxy (hanya modul dan fungsi bawaan).

Seperti yang kita ketahui sebelumnya, Apache memiliki cukup banyak fungsi inti yang memungkinkan Anda membuat konstruksi bersyarat. Namun, kami memerlukan cara untuk melindungi informasi kami saat berada di browser pengguna, jadi kami menentukan apa yang harus disimpan dan alasannya, serta fungsi bawaan apa yang akan kami gunakan:

  • Kami memerlukan token yang tidak dapat dengan mudah diterjemahkan.
  • Kami memerlukan token yang memiliki keusangan di dalamnya dan kemampuan untuk memeriksa keusangan di server.
  • Kita memerlukan token yang akan dikaitkan dengan pemilik sertifikat.

Ini memerlukan fungsi hashing, garam, dan tanggal untuk menentukan umur token. Berdasarkan dokumentasi Ekspresi di Apache HTTP Server kami memiliki semuanya di luar kotak sha1 dan %{TIME}.

Hasilnya adalah desain ini:

#Π½Π΅Ρ‚ сСртификата, ΠΈ ΠΎΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅ ΠΊ 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>

Tujuannya telah tercapai, tetapi ada masalah dengan keusangan server (Anda dapat menggunakan Cookie yang berumur satu tahun), yang berarti bahwa token tersebut, meskipun aman untuk penggunaan internal, tidak aman untuk penggunaan industri (massal).

Bagaimana kami di ZeroTech menghubungkan Apple Safari dan sertifikat klien dengan soket web

4. Token sesi sementara telah diimplementasikan sebagai modul Apache siap pakai.

Satu masalah signifikan yang tersisa dari iterasi sebelumnya adalah ketidakmampuan untuk mengontrol penuaan token.

Kami mencari modul siap pakai yang melakukan ini, sesuai dengan kata-kata: apache token json two factor auth

Ya, ada modul yang sudah jadi, tetapi semuanya terkait dengan tindakan tertentu dan memiliki artefak dalam bentuk memulai sesi dan Cookie tambahan. Artinya, tidak untuk sementara waktu.
Kami membutuhkan waktu lima jam untuk mencari, tetapi tidak memberikan hasil yang nyata.

5. Token sesi sementara dapat diimplementasikan dengan merancang struktur interaksi secara logis.

Modul yang sudah jadi terlalu rumit, karena kita hanya memerlukan beberapa fungsi.

Meskipun demikian, masalah tanggal adalah fungsi bawaan Apache tidak memungkinkan pembuatan tanggal dari masa depan, dan tidak ada penambahan/pengurangan matematis pada fungsi bawaan saat memeriksa keusangan.

Artinya, Anda tidak dapat menulis:

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

Anda hanya dapat membandingkan dua angka.

Saat mencari solusi untuk masalah Safari, saya menemukan artikel menarik: Mengamankan HomeAssistant dengan sertifikat klien (berfungsi dengan Safari/iOS)
Ini menjelaskan contoh kode di Lua untuk Nginx, dan ternyata, sangat mengulangi logika bagian konfigurasi yang telah kita terapkan, dengan pengecualian penggunaan metode pengasinan hmac untuk hashing ( ini tidak ditemukan di Apache).

Menjadi jelas bahwa Lua adalah bahasa dengan logika yang jelas, dan dimungkinkan untuk melakukan sesuatu yang sederhana untuk Apache:

Setelah mempelajari perbedaannya dengan Nginx dan Apache:

Dan fungsi yang tersedia dari produsen bahasa Lua:
22.1 – Tanggal dan Waktu

Kami menemukan cara untuk menyetel variabel env dalam file Lua kecil untuk menyetel tanggal dari masa depan untuk dibandingkan dengan tanggal saat ini.

Ini adalah tampilan skrip Lua yang sederhana:

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

Dan beginilah cara kerjanya secara total, dengan optimalisasi jumlah Cookies dan penggantian token ketika separuh waktunya tiba sebelum Cookie (token) lama habis masa berlakunya:

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 

Karena LuaHookAccessChecker hanya akan diaktifkan setelah pemeriksaan akses berdasarkan informasi dari Nginx ini.

Bagaimana kami di ZeroTech menghubungkan Apple Safari dan sertifikat klien dengan soket web

Tautan ke sumber Gambar.

Titik lain.

Secara umum, tidak masalah dalam urutan apa arahan ditulis dalam konfigurasi Apache (mungkin juga Nginx), karena pada akhirnya semuanya akan diurutkan berdasarkan urutan permintaan dari pengguna, yang sesuai dengan skema pemrosesan. skrip Lua.

Penyelesaian:

Keadaan yang terlihat setelah implementasi (tujuan):
pengelolaan layanan dan infrastruktur tersedia dari ponsel di iOS tanpa program tambahan (VPN), terpadu dan aman.

Tujuannya telah tercapai, soket web berfungsi dan memiliki tingkat keamanan tidak kurang dari sertifikat.

Bagaimana kami di ZeroTech menghubungkan Apple Safari dan sertifikat klien dengan soket web

Sumber: www.habr.com

Tambah komentar