كيف قمنا في ZeroTech بتوصيل Apple Safari وشهادات العميل بمقابس الويب

ستكون المقالة مفيدة لأولئك الذين:

  • يعرف ماهية Client Cert ويفهم سبب حاجته إلى websockets على Safari للجوال؛
  • أرغب في نشر خدمات الويب لدائرة محدودة من الأشخاص أو لنفسي فقط؛
  • يعتقد أن كل شيء قد تم بالفعل بواسطة شخص ما، ويرغب في جعل العالم أكثر راحة وأمانًا.

بدأ تاريخ مآخذ الويب منذ حوالي 8 سنوات. في السابق، كانت الأساليب تُستخدم في شكل طلبات http طويلة (في الواقع استجابات): أرسل متصفح المستخدم طلبًا إلى الخادم وانتظر حتى يجيب على شيء ما، وبعد الاستجابة اتصل مرة أخرى وانتظر. ولكن بعد ذلك ظهرت مآخذ الويب.

كيف قمنا في ZeroTech بتوصيل Apple Safari وشهادات العميل بمقابس الويب

منذ بضع سنوات، قمنا بتطوير تطبيقنا الخاص بلغة PHP خالصة، والذي لا يمكنه استخدام طلبات https، نظرًا لأن هذه هي طبقة الارتباط. منذ وقت ليس ببعيد، تعلمت جميع خوادم الويب تقريبًا التعامل مع طلبات الوكيل عبر https ودعم الاتصال: الترقية.

عندما حدث ذلك، أصبحت Websockets هي الخدمة الافتراضية تقريبًا لتطبيقات SPA، نظرًا لمدى ملاءمة توفير المحتوى للمستخدم بمبادرة من الخادم (إرسال رسالة من مستخدم آخر أو تنزيل نسخة جديدة من صورة أو مستند أو عرض تقديمي) الذي يقوم شخص آخر بتحريره حاليًا).

على الرغم من أن شهادة العميل موجودة منذ بعض الوقت، إلا أنها لا تزال غير مدعومة بشكل جيد، لأنها تخلق الكثير من المشكلات عند محاولة تجاوزها. و (ربما :slightly_smiling_face: ) لهذا السبب لا ترغب متصفحات IOS (جميعها باستثناء Safari) في استخدامه وتطلبه من مخزن الشهادات المحلي. تتمتع الشهادات بالعديد من المزايا مقارنة بمفاتيح تسجيل الدخول/المرور أو مفاتيح ssh أو إغلاق المنافذ الضرورية من خلال جدار الحماية. ولكن هذا ليس ما يدور حوله هذا الأمر.

على نظام التشغيل iOS، يكون إجراء تثبيت الشهادة بسيطًا جدًا (ليس بدون تفاصيل)، ولكن بشكل عام يتم ذلك وفقًا للتعليمات، والتي يوجد الكثير منها على الإنترنت والتي لا تتوفر إلا لمتصفح Safari. لسوء الحظ، لا يعرف Safari كيفية استخدام Client Сert لمقابس الويب، ولكن هناك العديد من الإرشادات على الإنترنت حول كيفية إنشاء مثل هذه الشهادة، ولكن في الممارسة العملية لا يمكن تحقيق ذلك.

كيف قمنا في ZeroTech بتوصيل Apple Safari وشهادات العميل بمقابس الويب

لفهم مقابس الويب، استخدمنا الخطة التالية: المشكلة / الفرضية / الحل.

المشكلة: لا يوجد دعم لمآخذ توصيل الويب عند إرسال طلبات الوكيل إلى الموارد المحمية بواسطة شهادة عميل على متصفح Safari للجوال لنظام IOS والتطبيقات الأخرى التي مكنت دعم الشهادة.

الفرضيات:

  1. من الممكن تكوين مثل هذا الاستثناء لاستخدام الشهادات (مع العلم أنه لن يكون هناك أي شهادات) لمقابس الويب الخاصة بموارد الوكيل الداخلية/الخارجية.
  2. بالنسبة إلى websockets، يمكنك إجراء اتصال فريد وآمن ويمكن الدفاع عنه باستخدام الجلسات المؤقتة التي يتم إنشاؤها أثناء طلب متصفح عادي (غير websocket).
  3. يمكن تنفيذ الجلسات المؤقتة باستخدام خادم ويب وكيل واحد (الوحدات والوظائف المضمنة فقط).
  4. تم بالفعل تنفيذ الرموز المميزة للجلسة المؤقتة كوحدات Apache جاهزة.
  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 بتوصيل Apple Safari وشهادات العميل بمقابس الويب

اختبار الفرضيات:

1. من الممكن تكوين مثل هذا الاستثناء لاستخدام الشهادات (مع العلم أنه لن يكون هناك أي منها) لمآخذ الويب الخاصة بموارد الوكيل الداخلية/الخارجية.

تم العثور على حلين هنا:

أ) على المستوى

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

تغيير مستوى الوصول.

تحتوي هذه الطريقة على الفروق الدقيقة التالية:

  • يتم التحقق من الشهادة بعد تقديم طلب إلى المورد الوكيل، أي تأكيد الاتصال بعد الطلب. وهذا يعني أنه سيتم تحميل الوكيل أولاً ثم قطع الطلب إلى الخدمة المحمية. وهذا أمر سيء، ولكنه ليس بالغ الأهمية؛
  • في بروتوكول http2. لا يزال في المسودة، ولا يعرف مصنعو المتصفح كيفية تنفيذه #info about 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: مصادقة شهادة عميل خادم أباتشي

تم اختبار كلا الخيارين، وتم اختيار الخيار "ب" لتعدد استخداماته وتوافقه مع بروتوكول 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 بتوصيل Apple Safari وشهادات العميل بمقابس الويب

2. بالنسبة إلى websockets، يمكنك إجراء اتصال فريد وآمن ومحمي باستخدام الجلسات المؤقتة التي يتم إنشاؤها أثناء طلب متصفح عادي (غير websocket).

استنادًا إلى الخبرة السابقة، تحتاج إلى إضافة قسم إضافي إلى التكوين لإعداد الرموز المؤقتة لاتصالات مقبس الويب أثناء الطلب العادي (غير مقبس الويب).

#подготовка передача себе С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. يمكن تنفيذ الجلسات المؤقتة باستخدام خادم ويب وكيل واحد (فقط الوحدات والوظائف المضمنة).

كما اكتشفنا سابقًا، يحتوي Apache على الكثير من الوظائف الأساسية التي تسمح لك بإنشاء بنيات شرطية. ومع ذلك، نحتاج إلى وسائل لحماية معلوماتنا أثناء وجودها في متصفح المستخدم، لذلك نقوم بتحديد ما يجب تخزينه ولماذا، وما هي الوظائف المضمنة التي سنستخدمها:

  • نحن بحاجة إلى رمز مميز لا يمكن فك شفرته بسهولة.
  • نحن بحاجة إلى رمز مميز يحتوي على التقادم والقدرة على التحقق من التقادم على الخادم.
  • نحتاج إلى رمز مميز يرتبط بمالك الشهادة.

يتطلب ذلك وظيفة تجزئة وملحًا وتاريخًا لعمر الرمز المميز. بناء على الوثائق التعبيرات في خادم Apache 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 بتوصيل Apple Safari وشهادات العميل بمقابس الويب

4. تم بالفعل تنفيذ الرموز المميزة للجلسة المؤقتة كوحدات Apache جاهزة.

بقيت مشكلة واحدة مهمة من التكرار السابق - وهي عدم القدرة على التحكم في تقادم الرمز المميز.

نبحث عن وحدة جاهزة تقوم بذلك، حسب الكلمات: apache token json two Factor auth

نعم، هناك وحدات جاهزة، ولكنها جميعها مرتبطة بإجراءات محددة ولها عناصر في شكل بدء جلسة وملفات تعريف ارتباط إضافية. وهذا هو، ليس لفترة من الوقت.
استغرق البحث خمس ساعات، ولم يعط نتيجة ملموسة.

5. يمكن تنفيذ الرموز المميزة للجلسة المؤقتة من خلال التصميم المنطقي لبنية التفاعلات.

الوحدات الجاهزة معقدة للغاية، لأننا نحتاج فقط إلى وظيفتين.

ومع ذلك، فإن مشكلة التاريخ هي أن وظائف Apache المضمنة لا تسمح بإنشاء تاريخ من المستقبل، ولا توجد إضافة/طرح رياضي في الوظائف المضمنة عند التحقق من التقادم.

أي أنه لا يمكنك الكتابة:

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

يمكنك مقارنة رقمين فقط.

أثناء البحث عن حل بديل لمشكلة Safari، وجدت مقالة مثيرة للاهتمام: تأمين HomeAssistant بشهادات العميل (يعمل مع Safari/iOS)
إنه يصف مثالًا للتعليمات البرمجية في Lua لـ Nginx، والذي، كما اتضح فيما بعد، يكرر كثيرًا منطق ذلك الجزء من التكوين الذي قمنا بتنفيذه بالفعل، باستثناء استخدام طريقة hmac للتمليح للتجزئة ( لم يتم العثور على هذا في أباتشي).

أصبح من الواضح أن لغة Lua هي لغة ذات منطق واضح، ومن الممكن القيام بشيء بسيط لـ Apache:

بعد دراسة الفرق بين 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 بتوصيل Apple Safari وشهادات العميل بمقابس الويب

رابط للمصدر صورة.

نقطة أخرى.

بشكل عام، لا يهم ترتيب كتابة التوجيهات في تكوين Apache (وربما Nginx أيضًا)، لأنه في النهاية سيتم فرز كل شيء بناءً على ترتيب الطلب المقدم من المستخدم، والذي يتوافق مع مخطط المعالجة مخطوطات لوا.

انتهاء:

الحالة المرئية بعد التنفيذ (الهدف):
إدارة الخدمات والبنية التحتية متاحة من الهاتف المحمول على نظام IOS بدون برامج إضافية (VPN)، موحدة وآمنة.

تم تحقيق الهدف، ومآخذ الويب تعمل وتتمتع بمستوى أمان لا يقل عن الشهادة.

كيف قمنا في ZeroTech بتوصيل Apple Safari وشهادات العميل بمقابس الويب

المصدر: www.habr.com

إضافة تعليق