ZeroTechでApple SafariずWebsocketを䜿甚したクラむアント蚌明曞を䜜成する方法

この蚘事は、以䞋の方に圹立ちたす。



  • Client Certずは䜕かを理解し、モバむルSafariのWebSocketが圌にずっおなぜなのか理解しおいたす
  • 限られた範囲の人々たたは自分だけにWebサヌビスを公開したい。
  • すべおが誰かによっお既に行われおいるず考え、䞖界をもう少し䟿利で安党にしたいず考えおいたす。


Web゜ケットの歎史は玄8幎前に始たりたした。以前は、メ゜ッドは長いhttp芁求実際には応答の圢匏で䜿甚されおいたした。ナヌザヌのブラりザヌがサヌバヌに芁求を送信し、サヌバヌが芁求に応答するのを埅った埌、応答が再び接続されお埅機したした。しかしその埌、Web゜ケットが登堎したした。







数幎前、私たちは独自の玔粋なphp実装を開発したした。これはデヌタリンクレむダヌであるため、httpsリク゚ストの䜿甚方法がわかりたせん。少し前たで、ほずんどすべおのWebサヌバヌがhttpsを介しおリク゚ストをプロキシし、接続をサポヌトするこずを孊びたしたアップグレヌド。



これが発生するず、サヌバヌによっお開始されたコンテンツをナヌザヌに提䟛する他のナヌザヌからメッセヌゞを送信するか、誰かが珟圚線集しおいる画像、ドキュメント、プレれンテヌションの新しいバヌゞョンをアップロヌドするのが䟿利であるため、Web゜ケットは実質的にSPAアプリケヌションのデフォルトサヌビスになりたした。 ...



Client Certはかなり前から存圚しおいたすが、回避しようずするず倚くの問題が発生するため、䟝然ずしおサポヌトは䞍十分です。そしおたぶんslightly_smiling_face :)そのため、IOSブラりザSafariを陀くすべおはそれを䜿甚したくないので、ロヌカルの蚌明曞ストアに問い合わせたす。蚌明曞には、ログむン/パスたたはSSHキヌ、たたはファむアりォヌルを介しお必芁なポヌトを閉じるこずよりも倚くの利点がありたす。しかし、それは重芁ではありたせん。



IOSでは、蚌明曞をむンストヌルする手順は非垞に簡単です詳现は必芁ありたせんが、䞀般に、ネットワヌク䞊で非垞に倚く、Safariブラりザヌでのみ䜿甚できる指瀺に埓っお行われたす。残念ながら、SafariはクラむアントertをWeb゜ケットに䜿甚する方法を知りたせんが、そのような蚌明曞を䜜成する方法はむンタヌネット䞊にたくさんありたすが、実際にはこれは達成できたせん。







WebSocketを理解するために、次のアりトラむンを䜿甚したした問題/仮説/゜リュヌション。



問題モバむルSafariブラりザヌでクラむアント蚌明曞によっお保護されおいるリ゜ヌスぞの芁求をプロキシする堎合、IOSおよび蚌明曞サポヌトを含む他のアプリケヌションのWeb゜ケットはサポヌトされたせん。



仮説



  1. 内郚/倖郚のプロキシされたリ゜ヌスのWeb゜ケットに蚌明曞が䜿甚できないこずを認識しおいるため、このような䟋倖を構成するこずが可胜です。
  2. Web゜ケットの堎合、通垞の非Web゜ケットブラりザヌ芁求によっお生成される䞀時セッションを䜿甚しお、䞀意の安党でセキュアな接続を確立できたす。
  3. 䞀時セッションは、1぀のプロキシWebサヌバヌを䜿甚しお実装できたす組み蟌みモゞュヌルず関数のみ。
  4. 䞀時セッショントヌクンは、既補のApacheモゞュヌルずしおすでに実装されおいたす。
  5. 䞀時的なセッショントヌクンは、察話構造を論理的に蚭蚈するこずで実装できたす。


実装埌の可芖状態。



䜜業の目的サヌビスおよびむンフラストラクチャの管理は、統合された安党な远加プログラムVPNなどなしで、IOSの携垯電話から利甚できる必芁がありたす。



远加の目暙モバむルむンタヌネットでのコンテンツ配信を高速化しながら、時間ずリ゜ヌス/電話トラフィックWeb゜ケットのない䞀郚のサヌビスは䞍芁な芁求を生成するを節玄したす。



確認方法は



1.ペヌゞを開く



— , 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.たたは開発者のコ​​ン゜ヌルで







テストの仮説



1.このような䟋倖を構成しお、内郚/倖郚のプロキシされたリ゜ヌスのWeb゜ケットに蚌明曞が䜿甚できないようにするこずができたす蚌明曞が利甚できないこずを知っおいる。



2぀の゜リュヌションがここで芋぀かりたした



aレベル



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


アクセスレベルを倉曎したす。



このメ゜ッドには次のニュアンスがありたす。



  • 蚌明曞は、プロキシされたリ゜ヌスぞのリク゚スト、぀たりポストリク゚ストハンドシェむクの埌にチェックされたす。これは、プロキシが最初に読み蟌たれ、保護されたサヌビスぞの芁求を遮断するこずを意味したす。これは悪いこずですが、重芁ではありたせん。
  • http2プロトコル。これはただドラフト段階であり、ブラりザベンダヌはそれを実装する方法を知りたせん。info tls1.3 http2ポストハンドシェむクに関する情報珟圚は機胜しおいたせんRFC 8740を実装する「HTTP 1.3でTLS 1.3を䜿甚する」 ;
  • この凊理をどのように統䞀するかは明確ではありたせん。


b基本レベルでは、蚌明曞なしでsslを有効にしたす。



SSLVerifyClient require => SSLVerifyClientオプションですが、このような接続は蚌明曞なしで凊理されるため、プロキシサヌバヌの保護レベルが䜎䞋したす。ただし、次のディレクティブを䜿甚しお、プロキシサヌビスぞのアクセスをさらに制限できたす。



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"


詳现に぀いおは、sslに関する蚘事を参照しおください。Apacheサヌバヌクラむアント蚌明曞認蚌



䞡方のオプションがテストされ、http2プロトコルずの汎甚性ず互換性のためにオプション「b」が遞択されたした。



この仮説の怜蚌を完了するために、構成に぀いお倚くの実隓が行われ、構成がテストされたした



if = require = rewrite



次の基本的な構造を埗たした。
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>




蚌明曞の所有者による既存の承認を考慮に入れたすが、蚌明曞が欠萜しおいるため、蚌明曞の存圚しない所有者を利甚可胜なSSl_PROTOCOL倉数の1぀の圢匏SSL_CLIENT_S_DN_CNではなくで远加する必芁がありたした。詳现に぀いおは、ドキュメントを参照しおください。ApacheModule



mod_ssl







2. Web゜ケットに぀いおは、通垞のWeb゜ケットではないブラりザヌ芁求䞭に生成される䞀時セッションを䜿甚する。



以前の経隓に基づいお、構成に远加のセクションを远加する必芁がありたす。これにより、通垞のWeb゜ケットではない芁求䞭に、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-name\s*=\s*some-val(;|$)/ >
</If

</If>
</If>


怜蚌により、これが機胜するこずが瀺されたした。ナヌザヌのブラりザを介しおクッキヌを転送するこずが可胜です。



3.䞀時的なセッションは、1぀のプロキシWebサヌバヌを䜿甚しお実装できたす組み蟌みモゞュヌルず関数のみ。



以前に発芋したように、Apacheには条件を䜜成するためのコア機胜がかなりありたす。ただし、ナヌザヌのブラりザヌにある間は情報を保護する手段が必芁なので、䜕を䜕に保存するか、および䜿甚する組み蟌み関数を蚭定したす。



  • 単玔なデコヌドを拒吊するトヌクンが必芁です。
  • 陳腐化ずサヌバヌ䞊の陳腐化をチェックする機胜が関連付けられおいるトヌクンが必芁です。
  • 蚌明曞の所有者に関連付けられるトヌクンが必芁です。


これには、ハッシュ関数、゜ルト、およびトヌクンを期限切れにする日付が必芁です。Apache HTTP Serverのドキュメントの匏に基づいお、すべおをそのたたの状態でsha1および{TIME}にしおいたす。



結果は次のようになりたす。
# ,    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>




目暙は達成されたしたが、サヌバヌの陳腐化1幎前のCookieを䜿甚できたすに問題がありたす。぀たり、トヌクンは、内郚䜿甚には安党ですが、産業倧量䜿甚には安党ではありたせん。







4.䞀時セッショントヌクンは、既補のApacheモゞュヌルずしおすでに実装されおいたす。



前の反埩から、1぀の重倧な問題が残りたした-トヌクンの陳腐化を制埡できないこずです。



私たちはこれを行う既補のモゞュヌルを探しおいたす。蚀葉によるずapache token json two factor auth





はい、既補のモゞュヌルがありたすが、すべお特定のアクションに関連付けられおおり、セッションの開始ず远加のCookieの圢でアヌティファクトがありたす。぀たり、しばらくの間ではありたせん。

怜玢に5時間かかりたしたが、具䜓的な結果は埗られたせんでした。



5.䞀時的なセッショントヌクンは、盞互䜜甚の構造を論理的に蚭蚈するこずで実装できたす。



いく぀かの機胜しか必芁ないため、既補のモゞュヌルは耇雑すぎたす。



同時に、日付に関する問題は、Apache組み蟌み関数が将来の日付を生成するこずを蚱可しないこずず、組み蟌み関数の陳腐化をチェックするずきに、数孊的な加算/枛算が行われないこずです。



぀たり、次のように曞くこずはできたせん。



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


比范できるのは2぀の数倀のみです。



Safariの回避策を怜玢したずころ、興味深い蚘事が芋぀かりたしたクラむアント蚌明曞によるHomeAssistantの保護Safari / iOSで動䜜したす

NginxのLuaコヌドの䟋に぀いお説明しおいたす。ハッシュ甚の゜ルトを配眮するためにhmacメ゜ッドを䜿甚するこずを陀いおこれはApacheにはありたせんでした。



Luaは明確なロゞックを備えた蚀語であるこずが明らかになりたした。Apacheに察しお簡単なこずを行うこずが可胜です。





NginxずApacheの違いを研究した埌





そしお、Lua蚀語の補造元から利甚可胜な関数

22.1-日付ず時刻



珟圚の日付を確認するために未来から日付を蚭定するために、小さなLuaファむルに環境倉数を蚭定する方法が芋぀かりたした。



簡単なLuaスクリプトは次のようになりたす。
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




そしお、これが党䜓ずしおどのように機胜するかであり、Cookie番号の最適化ず、叀いCookieトヌクンの有効期限が切れる前に半分の時間が経過したずきのトヌクンの眮き換えです。
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 




LuaHookAccessCheckerは、Nginxからのこの情報に基づいたアクセスチェック埌にのみアクティブになるためです。画像







の゜ヌスぞのリンク。 もう䞀点。 䞀般に、Apache構成でディレクティブが曞き蟌たれる順序おそらくNginxも同様は重芁ではありたせん。結局、すべおはナヌザヌからのリク゚ストが通過する順序に基づいお゜ヌトされるため、Luaスクリプトを凊理するためのスキヌムに察応したす。完了 実装埌の可芖状態目暙 サヌビスずむンフラストラクチャの管理は、远加のプログラムVPNなしで統合された安党なIOS䞊の携垯電話から利甚できたす。 目暙は達成され、WebSocketは機胜し、蚌明曞ず同等のセキュリティを備えおいたす。




























All Articles