كيف يمكنني الحصول على LWP للتحقق من صحة شهادات خادم SSL؟

StackOverflow https://stackoverflow.com/questions/74358

  •  09-06-2019
  •  | 
  •  

سؤال

كيف يمكنني الحصول على LWP للتحقق من أن شهادة الخادم الذي أتصل به موقعة من قبل سلطة موثوقة وصادرة إلى المضيف الصحيح؟بقدر ما أستطيع أن أقول، فإنه لا يتحقق حتى من أن الشهادة تدعي أنها تخص اسم المضيف الذي أتصل به.يبدو هذا بمثابة ثغرة أمنية كبيرة (خاصة مع ثغرات DNS الأخيرة).

تحديث: اتضح أن ما أردته حقًا هو HTTPS_CA_DIR, ، لأنني لا أملك ca-bundle.crt.لكن HTTPS_CA_DIR=/usr/share/ca-certificates/ فعلت الحيلة.أضع علامة على الإجابة على أنها مقبولة على أي حال، لأنها كانت قريبة بما فيه الكفاية.

التحديث 2: لقد أتضح أن HTTPS_CA_DIR و HTTPS_CA_FILE لا ينطبق إلا إذا كنت تستخدم Net::SSL كمكتبة SSL الأساسية.لكن LWP يعمل أيضًا مع IO::Socket::SSL، الذي سيتجاهل متغيرات البيئة هذه ويتحدث بسعادة مع أي خادم، بغض النظر عن الشهادة التي يقدمها.هل هناك حل أكثر عمومية؟

التحديث 3: ولسوء الحظ، فإن الحل لا يزال غير كامل.لا يقوم Net::SSL ولا IO::Socket::SSL بالتحقق من اسم المضيف مقابل الشهادة.وهذا يعني أنه يمكن لأي شخص الحصول على شهادة شرعية لبعض النطاقات، ثم انتحال شخصية أي نطاق آخر دون شكوى LWP.

التحديث 4: لو بي 6.00 أخيرا يحل المشكلة.يرى إجابتي للتفاصيل.

هل كانت مفيدة؟

المحلول

تم أخيرًا إصلاح هذا الثغرة الأمنية الطويلة الأمد في الإصدار 6.00 من libwww-بيرل.بدءًا من هذا الإصدار، بشكل افتراضي LWP::UserAgent يتحقق من أن خوادم HTTPS تقدم شهادة صالحة تطابق اسم المضيف المتوقع (ما لم $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} تم تعيينه على قيمة خاطئة أو للتوافق مع الإصدارات السابقة إذا لم يتم تعيين هذا المتغير على الإطلاق $ENV{HTTPS_CA_FILE} أو $ENV{HTTPS_CA_DIR} تم ضبطه).

هذا يمكن السيطرة عليه من خلال الجديد ssl_opts خيار LWP::UserAgent.راجع هذا الرابط للحصول على تفاصيل حول كيفية العثور على شهادات المرجع المصدق.لكن احرص, ، الطريقة التي كان يعمل بها LWP::UserAgent، إذا قمت بتوفير ملف ssl_opts التجزئة إلى المنشئ، ثم verify_hostname الافتراضي إلى 0 بدلا من 1.(هذا الخطأ تم إصلاحه في LWP 6.03.) لكي تكون آمنًا، حدد دائمًا verify_hostname => 1 في الخاص بك ssl_opts.

لذا use LWP::UserAgent 6; يجب أن يكون كافيًا للتحقق من صحة شهادات الخادم.

نصائح أخرى

هناك طريقتان للقيام بذلك اعتمادًا على وحدة SSL التي قمت بتثبيتها.ال توصي مستندات LWP بتثبيت Crypt::SSLeay.إذا كان هذا ما قمت به، فقم بتعيين HTTPS_CA_FILE يجب أن يقوم متغير البيئة للإشارة إلى ca-bundle.crt الخاص بك بالمهمة.(ال التشفير::SSLeay مستندات يذكر هذا ولكنه خفيف بعض الشيء على التفاصيل).أيضًا، اعتمادًا على الإعداد الخاص بك، قد تحتاج إلى ضبط HTTPS_CA_DIR متغير البيئة بدلا من ذلك.

مثال على Crypt::SSLeay:


use LWP::Simple qw(get);
$ENV{HTTPS_CA_FILE} = "/path/to/your/ca/file/ca-bundle";
$ENV{HTTPS_DEBUG} = 1;

print get("https://some-server-with-bad-certificate.com");

__END__
SSL_connect:before/connect initialization
SSL_connect:SSLv2/v3 write client hello A
SSL_connect:SSLv3 read server hello A
SSL3 alert write:fatal:unknown CA
SSL_connect:error in SSLv3 read server certificate B
SSL_connect:error in SSLv3 read server certificate B
SSL_connect:before/connect initialization
SSL_connect:SSLv3 write client hello A
SSL_connect:SSLv3 read server hello A
SSL3 alert write:fatal:bad certificate
SSL_connect:error in SSLv3 read server certificate B
SSL_connect:before/connect initialization
SSL_connect:SSLv2 write client hello A
SSL_connect:error in SSLv2 read server hello B

لاحظ أن الحصول على لا die, ، لكنه يُرجع ملفًا undef.

وبدلاً من ذلك، يمكنك استخدام IO::Socket::SSL الوحدة (متوفرة أيضًا من CPAN).ولجعل هذا التحقق من شهادة الخادم تحتاج إلى تعديل الإعدادات الافتراضية لسياق SSL:


use IO::Socket::SSL qw(debug3);
use Net::SSLeay;
BEGIN {
    IO::Socket::SSL::set_ctx_defaults(
        verify_mode => Net::SSLeay->VERIFY_PEER(),
        ca_file => "/path/to/ca-bundle.crt",
      # ca_path => "/alternate/path/to/cert/authority/directory"
    );
}
use LWP::Simple qw(get);

warn get("https:://some-server-with-bad-certificate.com");

هذا الإصدار يسبب أيضا get() للعودة undef ولكن طباعة تحذير ل STDERR عند تنفيذه (بالإضافة إلى مجموعة من عمليات التصحيح إذا قمت باستيراد رموز التصحيح* من IO::Socket::SSL):


% perl ssl_test.pl
DEBUG: .../IO/Socket/SSL.pm:1387: new ctx 139403496
DEBUG: .../IO/Socket/SSL.pm:269: socket not yet connected
DEBUG: .../IO/Socket/SSL.pm:271: socket connected
DEBUG: .../IO/Socket/SSL.pm:284: ssl handshake not started
DEBUG: .../IO/Socket/SSL.pm:327: Net::SSLeay::connect -> -1
DEBUG: .../IO/Socket/SSL.pm:1135: SSL connect attempt failed with unknown errorerror:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

DEBUG: .../IO/Socket/SSL.pm:333: fatal SSL error: SSL connect attempt failed with unknown errorerror:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
DEBUG: .../IO/Socket/SSL.pm:1422: free ctx 139403496 open=139403496
DEBUG: .../IO/Socket/SSL.pm:1425: OK free ctx 139403496
DEBUG: .../IO/Socket/SSL.pm:1135: IO::Socket::INET configuration failederror:00000000:lib(0):func(0):reason(0)
500 Can't connect to some-server-with-bad-certificate.com:443 (SSL connect attempt failed with unknown errorerror:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed) 

لقد وصلت إلى هذه الصفحة بحثًا عن طريقة لتجاوز التحقق من صحة طبقة المقابس الآمنة (SSL) ولكن جميع الإجابات كانت لا تزال مفيدة للغاية.وهنا النتائج التي توصلت إليها.بالنسبة لأولئك الذين يتطلعون إلى تجاوز التحقق من صحة طبقة المقابس الآمنة (غير مستحسن ولكن قد تكون هناك حالات يتعين عليك فيها ذلك تمامًا)، فأنا أستخدم lwp 6.05 وقد نجح هذا معي:

use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common qw(GET);
use Net::SSL;

my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 }, );
my $req = GET 'https://github.com';
my $res = $ua->request($req);
if ($res->is_success) {
    print $res->content;
} else {
    print $res->status_line . "\n";
}

لقد اختبرت أيضًا على صفحة بها POST وقد نجحت أيضًا.المفتاح هو استخدام Net::SSL مع Vere_hostname = 0.

إذا كنت تستخدم LWP::UserAgent مباشرة (وليس عبر LWP::Simple)، فيمكنك التحقق من صحة اسم المضيف في الشهادة عن طريق إضافة رأس "If-SSL-Cert-Subject" إلى كائن HTTP::Request الخاص بك.يتم التعامل مع قيمة الرأس كتعبير عادي ليتم تطبيقه على موضوع الشهادة، وإذا لم يتطابق، يفشل الطلب.على سبيل المثال:

#!/usr/bin/perl 
use LWP::UserAgent;
my $ua = LWP::UserAgent->new();
my $req = HTTP::Request->new(GET => 'https://yourdomain.tld/whatever');
$req->header('If-SSL-Cert-Subject' => '/CN=make-it-fail.tld');

my $res = $ua->request( $req );

print "Status: " . $res->status_line . "\n"

سوف طباعة

Status: 500 Bad SSL certificate subject: '/C=CA/ST=Ontario/L=Ottawa/O=Your Org/CN=yourdomain.tld' !~ //CN=make-it-fail.tld/

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

إذا كنت تستخدم IO::Socket::SSL باعتبارك الواجهة الخلفية لـ LWP، يمكنك تمكين التحقق من الاسم الشائع عن طريق تعيين verifycn_scheme المعلمة مثل هذا:

use IO::Socket::SSL;
use Net::SSLeay;
BEGIN {
    IO::Socket::SSL::set_ctx_defaults(
        verify_mode => Net::SSLeay->VERIFY_PEER(),
        verifycn_scheme => 'http',
        ca_path => "/etc/ssl/certs"
    );
}

قد تفكر أيضًا في Net::SSLGlue ( http://search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm ) لكن انتبه، فالأمر يعتمد على إصدارات IO::Socket::SSL وNet::SSLeay الحديثة.

من حقك أن تقلق بشأن هذا.لسوء الحظ، لا أعتقد أنه من الممكن القيام بذلك بشكل آمن بنسبة 100% تحت أي من روابط SSL/TLS منخفضة المستوى التي بحثت عنها في Perl.

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

تعتمد طريقة التحقق من اسم المضيف مقابل شهادة الخادم على البروتوكول أيضًا.لذلك يجب أن يكون ذلك معلمة لأي وظيفة مثالية.

قد ترغب في معرفة ما إذا كان هناك أي ارتباطات لمكتبة Netscape/Mozilla NSS.بدا الأمر جيدًا جدًا في القيام بذلك عندما نظرت إليه.

فقط قم بتنفيذ الأمر التالي في المحطة الطرفية:Sudo cpan تثبيت Mozilla::CA

يجب أن يحلها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top