ما در ZeroTech چگونه اپل سافاری و گواهی‌های مشتری را با سوکت‌های وب متصل کردیم

این مقاله برای کسانی مفید خواهد بود که:

  • می داند Client Cert چیست و می داند که چرا به سوکت های وب در سافاری موبایل نیاز دارد.
  • من می خواهم خدمات وب را برای حلقه محدودی از افراد یا فقط برای خودم منتشر کنم.
  • فکر می کند که همه چیز قبلاً توسط کسی انجام شده است و می خواهد دنیا را کمی راحت تر و ایمن تر کند.

تاریخچه وب سوکت ها حدود 8 سال پیش آغاز شد. قبلاً از روش‌هایی مانند درخواست‌های طولانی http (در واقع پاسخ‌ها) استفاده می‌شد: مرورگر کاربر درخواستی را به سرور ارسال می‌کرد و منتظر می‌ماند تا چیزی را پاسخ دهد، پس از پاسخ دوباره متصل شد و منتظر ماند. اما سپس وب سوکت ها ظاهر شدند.

ما در ZeroTech چگونه اپل سافاری و گواهی‌های مشتری را با سوکت‌های وب متصل کردیم

چند سال پیش، پیاده‌سازی خودمان را در PHP خالص توسعه دادیم، که نمی‌تواند از درخواست‌های https استفاده کند، زیرا این لایه پیوند است. چندی پیش، تقریباً همه سرورهای وب یاد گرفتند که درخواست های پروکسی را از طریق https انجام دهند و از Connection:upgrade پشتیبانی کنند.

هنگامی که این اتفاق افتاد، وب‌سوکت‌ها تقریباً به سرویس پیش‌فرض برای برنامه‌های SPA تبدیل شدند، زیرا ارائه محتوا به کاربر به ابتکار سرور چقدر راحت است (انتقال پیام از کاربر دیگری یا دانلود نسخه جدیدی از یک تصویر، سند، ارائه که شخص دیگری در حال ویرایش آن است).

اگرچه گواهی مشتری مدت زیادی است که وجود داشته است، اما همچنان به خوبی پشتیبانی نمی شود، زیرا هنگام تلاش برای دور زدن آن، مشکلات زیادی ایجاد می کند. و (احتمالاً :slightly_smiling_face: ) به همین دلیل است که مرورگرهای IOS (همه به جز سافاری) نمی خواهند از آن استفاده کنند و آن را از فروشگاه گواهی محلی درخواست کنند. گواهینامه ها در مقایسه با کلیدهای login/pass یا ssh یا بستن پورت های لازم از طریق فایروال مزایای زیادی دارند. اما موضوع این نیست.

در iOS، روش نصب گواهی بسیار ساده است (نه بدون جزئیات)، اما به طور کلی طبق دستورالعمل هایی انجام می شود که تعداد زیادی از آنها در اینترنت وجود دارد و فقط برای مرورگر سافاری در دسترس هستند. متأسفانه سافاری نمی داند چگونه از Client Сert برای سوکت های وب استفاده کند، اما دستورالعمل های زیادی در اینترنت در مورد نحوه ایجاد چنین گواهینامه ای وجود دارد، اما در عمل این امر دست نیافتنی است.

ما در ZeroTech چگونه اپل سافاری و گواهی‌های مشتری را با سوکت‌های وب متصل کردیم

برای درک وب سوکت ها، از طرح زیر استفاده کردیم: مسئله/فرضیه/راه حل.

مشکل: هیچ پشتیبانی از سوکت های وب هنگام پراکسی کردن درخواست ها به منابعی که توسط گواهی مشتری در مرورگر تلفن همراه Safari برای IOS و سایر برنامه هایی که پشتیبانی گواهی را فعال کرده اند محافظت می شود، وجود ندارد.

فرضیه ها:

  1. می توان چنین استثنایی را برای استفاده از گواهینامه ها (با دانستن اینکه هیچ گواهی وجود نخواهد داشت) در سوکت های وب منابع داخلی/خارجی پروکسی پیکربندی کرد.
  2. برای سوکت‌های وب، می‌توانید با استفاده از جلسات موقتی که در طول یک درخواست معمولی (غیر وب سوکت) مرورگر ایجاد می‌شوند، یک اتصال منحصر به فرد، ایمن و قابل دفاع ایجاد کنید.
  3. جلسات موقت را می توان با استفاده از یک وب سرور پروکسی (فقط ماژول ها و توابع داخلی) پیاده سازی کرد.
  4. نشانه های جلسه موقت قبلاً به عنوان ماژول های آماده آپاچی پیاده سازی شده اند.
  5. نشانه های جلسه موقت را می توان با طراحی منطقی ساختار تعامل پیاده سازی کرد.

حالت قابل مشاهده پس از اجرا

هدف، واقعگرایانه: مدیریت خدمات و زیرساخت ها باید از طریق تلفن همراه در IOS بدون برنامه های اضافی (مانند VPN)، یکپارچه و ایمن باشد.

هدف اضافی: صرفه جویی در زمان و منابع / ترافیک تلفن (برخی از خدمات بدون سوکت وب درخواست های غیر ضروری ایجاد می کنند) با تحویل سریعتر محتوا در اینترنت تلفن همراه.

چگونه برای چک کردن

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. یا در کنسول توسعه دهنده:

ما در ZeroTech چگونه اپل سافاری و گواهی‌های مشتری را با سوکت‌های وب متصل کردیم

آزمایش فرضیه:

1. امکان پیکربندی چنین استثنایی برای استفاده از گواهینامه ها (با دانستن اینکه هیچ گواهی وجود نخواهد داشت) برای سوکت های وب منابع داخلی/خارجی پروکسی وجود دارد.

2 راه حل در اینجا یافت شد:

الف) در سطح

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

تغییر سطح دسترسی

این روش دارای تفاوت های ظریف زیر است:

  • تأیید گواهی پس از درخواست به منبع پروکسی، یعنی ارسال درخواست دست دادن انجام می شود. این بدان معنی است که پروکسی ابتدا بارگیری می شود و سپس درخواست سرویس محافظت شده را قطع می کند. این بد است، اما انتقادی نیست.
  • در پروتکل http2. هنوز در پیش نویس است و سازندگان مرورگر نمی دانند چگونه آن را پیاده سازی کنند #اطلاعات در مورد tls1.3 http2 post handshake (اکنون کار نمی کند) پیاده سازی RFC 8740 "استفاده از TLS 1.3 با HTTP/2";
  • نحوه یکسان سازی این پردازش مشخص نیست.

ب) در سطح پایه، ssl را بدون گواهینامه مجاز کنید.

SSLVerifyClient نیاز => 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 بیابید: احراز هویت گواهی مشتری سرور آپاچی

هر دو گزینه آزمایش شدند، گزینه "b" به دلیل تطبیق پذیری و سازگاری آن با پروتکل http2 انتخاب شد.

برای تکمیل تأیید این فرضیه، آزمایش‌های زیادی با پیکربندی انجام شد؛ طرح‌های زیر مورد آزمایش قرار گرفتند:

اگر = نیاز = بازنویسی

نتیجه طراحی اساسی زیر است:

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 (به جای SSL_CLIENT_S_DN_CN) اضافه کنم، جزئیات بیشتر در اسناد:

ماژول آپاچی mod_ssl

ما در ZeroTech چگونه اپل سافاری و گواهی‌های مشتری را با سوکت‌های وب متصل کردیم

2. برای سوکت‌های وب، می‌توانید با استفاده از جلسات موقتی که در طول یک درخواست معمولی (غیر وب سوکت) مرورگر ایجاد می‌شوند، یک اتصال منحصر به فرد، ایمن و محافظت شده ایجاد کنید.

بر اساس تجربه قبلی، باید یک بخش اضافی به پیکربندی اضافه کنید تا در طول یک درخواست معمولی (غیر سوکت وب)، توکن‌های موقتی برای اتصالات سوکت وب آماده کنید.

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

آزمایش نشان داد که کار می کند. امکان انتقال کوکی ها به خودتان از طریق مرورگر کاربر وجود دارد.

3. جلسات موقت را می توان با استفاده از یک وب سرور پروکسی (فقط ماژول ها و توابع داخلی) پیاده سازی کرد.

همانطور که قبلا متوجه شدیم، آپاچی دارای عملکردهای اصلی بسیار زیادی است که به شما امکان می دهد ساختارهای شرطی ایجاد کنید. با این حال، ما به ابزارهایی برای محافظت از اطلاعات خود در زمانی که در مرورگر کاربر هستند نیاز داریم، بنابراین مشخص می‌کنیم که چه چیزی را ذخیره کنیم و چرا، و از چه عملکردهای داخلی استفاده خواهیم کرد:

  • ما به یک توکن نیاز داریم که به راحتی قابل رمزگشایی نباشد.
  • ما به یک توکن نیاز داریم که منسوخ بودن در آن تعبیه شده باشد و قابلیت بررسی منسوخ بودن در سرور را داشته باشد.
  • ما به یک نشانه نیاز داریم که با صاحب گواهی مرتبط باشد.

این به یک تابع هش، یک نمک و یک تاریخ برای سن توکن نیاز دارد. بر اساس مستندات عبارات در سرور HTTP آپاچی ما همه را خارج از جعبه 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>

هدف محقق شده است، اما مشکلاتی در مورد فرسودگی سرور وجود دارد (شما می توانید از یک کوکی یک ساله استفاده کنید)، به این معنی که توکن ها، اگرچه برای استفاده داخلی ایمن هستند، اما برای استفاده صنعتی (انبوه) ناامن هستند.

ما در ZeroTech چگونه اپل سافاری و گواهی‌های مشتری را با سوکت‌های وب متصل کردیم

4. نشانه های جلسه موقت قبلاً به عنوان ماژول های آماده آپاچی پیاده سازی شده اند.

یک مشکل مهم از تکرار قبلی باقی ماند - ناتوانی در کنترل پیری نشانه.

ما به دنبال یک ماژول آماده هستیم که این کار را انجام دهد، با توجه به عبارت: apache token json two factor auth

بله، ماژول های آماده ای وجود دارد، اما همه آنها به اقدامات خاصی گره خورده اند و مصنوعاتی به شکل شروع یک جلسه و کوکی های اضافی دارند. یعنی برای مدتی نه.
جستجوی ما پنج ساعت طول کشید که نتیجه مشخصی نداشت.

5. نشانه های جلسه موقت را می توان با طراحی منطقی ساختار تعاملات پیاده سازی کرد.

ماژول های آماده بسیار پیچیده هستند، زیرا ما فقط به چند عملکرد نیاز داریم.

همانطور که گفته شد، مشکل تاریخ این است که توابع داخلی آپاچی اجازه تولید تاریخ از آینده را نمی‌دهند و هیچ جمع/ تفریق ریاضی در توابع داخلی هنگام بررسی منسوخ بودن وجود ندارد.

یعنی نمی توانید بنویسید:

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

شما فقط می توانید دو عدد را با هم مقایسه کنید.

در حین جستجوی راه حلی برای مشکل سافاری، مقاله جالبی پیدا کردم: ایمن سازی HomeAssistant با گواهی های مشتری (با Safari/iOS کار می کند)
نمونه‌ای از کد را در Lua برای Nginx توصیف می‌کند، و همانطور که مشخص شد، منطق آن قسمت از پیکربندی را که قبلاً پیاده‌سازی کرده‌ایم، به استثنای استفاده از روش hmac salting برای هش کردن، بسیار تکرار می‌کند. این در آپاچی یافت نشد).

مشخص شد که Lua یک زبان با منطق روشن است و می توان یک کار ساده برای آپاچی انجام داد:

پس از مطالعه تفاوت با Nginx و Apache:

و توابع موجود از سازنده زبان Lua:
22.1 - تاریخ و زمان

ما راهی برای تنظیم متغیرهای env در یک فایل کوچک 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

و در کل به این صورت کار می کند، با بهینه سازی تعداد کوکی ها و جایگزینی توکن زمانی که نیمی از زمان قبل از انقضای کوکی (توکن) قدیمی است:

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 فعال می شود.

ما در ZeroTech چگونه اپل سافاری و گواهی‌های مشتری را با سوکت‌های وب متصل کردیم

لینک به منبع تصویر.

نقطه دیگر است.

به طور کلی، مهم نیست که دستورالعمل ها به چه ترتیبی در پیکربندی Apache (احتمالاً Nginx) نوشته می شوند، زیرا در نهایت همه چیز بر اساس ترتیب درخواست کاربر، که مطابق با طرح پردازش است، مرتب می شود. اسکریپت های لوا

تکمیل:

حالت قابل مشاهده پس از اجرا (هدف):
مدیریت خدمات و زیرساخت ها از طریق تلفن همراه در IOS بدون برنامه های اضافی (VPN)، یکپارچه و امن در دسترس است.

هدف محقق شده است، سوکت های وب کار می کنند و سطح امنیتی کمتری از یک گواهی ندارند.

ما در ZeroTech چگونه اپل سافاری و گواهی‌های مشتری را با سوکت‌های وب متصل کردیم

منبع: www.habr.com

اضافه کردن نظر