it-swarm-vi.tech

Làm cách nào tôi có thể nhận được LWP để xác thực chứng chỉ máy chủ SSL?

Làm cách nào tôi có thể nhận LWP để xác minh rằng chứng chỉ của máy chủ tôi đang kết nối được ký bởi một cơ quan đáng tin cậy và được cấp cho đúng máy chủ lưu trữ? Theo như tôi có thể nói, thậm chí còn không kiểm tra xem chứng nhận có phải là tên máy chủ mà tôi đang kết nối hay không. Đó dường như là một lỗ hổng bảo mật lớn (đặc biệt là với các lỗ hổng DNS gần đây).

Cập nhật: Hóa ra thứ tôi thực sự muốn là HTTPS_CA_DIR, vì tôi không có ca-bundle.crt. Nhưng HTTPS_CA_DIR=/usr/share/ca-certificates/ đã lừa. Dù sao tôi cũng đánh dấu câu trả lời là được chấp nhận, vì nó đủ gần.

Cập nhật 2: Hóa ra HTTPS_CA_DIRHTTPS_CA_FILE chỉ áp dụng nếu bạn đang sử dụng Net :: SSL làm thư viện SSL cơ bản. Nhưng LWP cũng hoạt động với IO :: Socket :: SSL, sẽ bỏ qua các biến môi trường đó và vui vẻ nói chuyện với bất kỳ máy chủ nào, bất kể nó có chứng chỉ gì. Có một giải pháp tổng quát hơn?

Cập nhật 3: Thật không may, giải pháp vẫn chưa hoàn thành. Cả Net :: SSL và IO :: Socket :: SSL đều đang kiểm tra tên Máy chủ so với chứng chỉ. Điều này có nghĩa là ai đó có thể nhận được chứng chỉ hợp pháp cho một số tên miền và sau đó mạo danh bất kỳ tên miền nào khác mà không có khiếu nại của LWP.

Cập nhật 4:LWP 6,00 cuối cùng cũng giải quyết được vấn đề. Xem câu trả lời của tôi để biết chi tiết.

44
cjm

Lỗ hổng bảo mật lâu đời này cuối cùng đã được sửa trong phiên bản 6,00 của libwww-Perl . Bắt đầu với phiên bản đó, theo mặc định LWP :: UserAgent xác minh rằng các máy chủ HTTPS xuất trình chứng chỉ hợp lệ khớp với tên máy chủ dự kiến ​​(trừ khi $ENV{Perl_LWP_SSL_VERIFY_HOSTNAME} được đặt thành giá trị sai hoặc, để tương thích ngược nếu biến đó không được đặt hoặc $ENV{HTTPS_CA_FILE} hoặc $ENV{HTTPS_CA_DIR} được đặt).

Điều này có thể được kiểm soát bởi tùy chọn ssl_opts mới của LWP :: UserAgent. Xem liên kết đó để biết chi tiết về cách chứng chỉ của Cơ quan cấp chứng chỉ. Nhưng hãy cẩn thận, cách thức LWP :: UserAgent được sử dụng để làm việc, nếu bạn cung cấp hàm băm ssl_opts cho hàm tạo, thì verify_hostname được mặc định là 0 thay vì 1. ( Lỗi này đã được sửa trong LWP 6.03.) Để an toàn, luôn chỉ định verify_hostname => 1 trong ssl_opts của bạn.

Vì vậy, use LWP::UserAgent 6; phải đủ để chứng chỉ máy chủ được xác thực.

37
cjm

Có hai phương tiện để thực hiện việc này tùy thuộc vào mô-đun SSL nào bạn đã cài đặt. Các tài liệu LWP khuyên bạn nên cài đặt Crypt :: SSLeay . Nếu đó là những gì bạn đã làm, đặt biến môi trường HTTPS_CA_FILE để trỏ đến ca-bundle.crt của bạn sẽ thực hiện thủ thuật. (tài liệu Crypt :: SSLeay đề cập đến điều này nhưng hơi nhẹ về chi tiết). Ngoài ra, tùy thuộc vào thiết lập của bạn, bạn có thể cần phải đặt biến môi trường HTTPS_CA_DIR thay thế.

Ví dụ cho tiền điện tử :: 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

Lưu ý rằng get không die, nhưng nó sẽ trả về undef.

Ngoài ra, bạn có thể sử dụng mô-đun IO::Socket::SSL (cũng có sẵn từ CPAN). Để thực hiện xác minh chứng chỉ máy chủ này, bạn cần sửa đổi mặc định bối cảnh 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");

Phiên bản này cũng khiến get() trả về undef nhưng in cảnh báo cho STDERR khi bạn thực thi nó (cũng như một loạt các gỡ lỗi nếu bạn nhập các biểu tượng gỡ lỗi * từ 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) 
9
Brian Phillips

Tôi đã vào trang này để tìm cách vượt qua xác nhận SSL nhưng tất cả các câu trả lời vẫn rất hữu ích. Dưới đây là những phát hiện của tôi. Đối với những người đang tìm cách bỏ qua xác thực SSL (không được đề xuất nhưng có thể có trường hợp bạn hoàn toàn phải làm như vậy), tôi đang ở phiên bản 6.05 và điều này hiệu quả với tôi:

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";
}

Tôi cũng đã thử nghiệm trên một trang với POST và nó cũng hoạt động. Chìa khóa là sử dụng Net :: SSL cùng với verify_hostname = 0.

6
bshok

Tất cả các giải pháp được trình bày ở đây đều có một lỗ hổng bảo mật lớn ở chỗ chúng chỉ xác minh tính hợp lệ của chuỗi tin cậy của chứng chỉ, nhưng không so sánh Tên chung của chứng chỉ với tên máy chủ bạn đang kết nối. Do đó, một người đàn ông ở giữa có thể xuất trình chứng chỉ tùy ý cho bạn và LWP sẽ vui vẻ chấp nhận miễn là nó được ký bởi một CA mà bạn tin tưởng. Tên chung của chứng chỉ không có thật là không liên quan vì nó không bao giờ được kiểm tra bởi LWP.

Nếu bạn đang sử dụng IO::Socket::SSL làm phụ trợ của LWP, bạn có thể bật xác minh Tên chung bằng cách đặt tham số verifycn_scheme như thế này:

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"
    );
}
2
blumentopf

Nếu bạn sử dụng trực tiếp LWP :: UserAgent (không thông qua LWP :: Simple), bạn có thể xác thực tên máy chủ trong chứng chỉ bằng cách thêm tiêu đề "If-SSL-Cert-Subject" vào đối tượng HTTP :: Request của bạn. Giá trị của tiêu đề được coi là biểu thức chính quy được áp dụng cho chủ đề chứng chỉ và nếu nó không khớp, yêu cầu không thành công. Ví dụ:

#!/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"

sẽ in

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

Bạn có quyền được quan tâm về điều này. Thật không may, tôi không nghĩ có thể thực hiện an toàn 100% dưới bất kỳ ràng buộc SSL/TLS cấp thấp nào tôi đã xem xét cho Perl.

Về cơ bản, bạn cần phải nhập tên máy chủ của máy chủ mà bạn muốn kết nối với thư viện SSL trước khi quá trình bắt tay được tiến hành. Ngoài ra, bạn có thể sắp xếp cho một cuộc gọi lại xảy ra vào đúng thời điểm và hủy bỏ cái bắt tay từ bên trong cuộc gọi lại nếu nó không kiểm tra. Những người viết các ràng buộc Perl cho OpenSSL dường như có vấn đề khiến giao diện gọi lại luôn ổn định.

Phương pháp kiểm tra tên máy chủ đối với chứng chỉ của máy chủ cũng phụ thuộc vào giao thức. Vì vậy, đó sẽ phải là một tham số cho bất kỳ chức năng hoàn hảo.

Bạn có thể muốn xem liệu có bất kỳ ràng buộc nào với thư viện Netscape/Mozilla NSS không. Nó có vẻ khá tốt khi làm điều này khi tôi nhìn vào nó.

1
Marsh Ray

Bạn cũng có thể xem xét Net :: SSLGlue ( http://search.cpan.org/dist/Net-SSLGlue/lib/Net/SSLGlue.pm ) Nhưng, hãy cẩn thận, điều này phụ thuộc vào IO :: Socket gần đây :: SSL và Net :: Phiên bản SSLeay.

1
goneri

Chỉ cần thực hiện lệnh sau trong Terminal: Sudo cpan cài đặt Mozilla :: CA

Nó sẽ giải quyết nó.

0
Bojoer