Skip to content

Commit

Permalink
add verify_https (and https_ca_dir & https_host) options to check SSL…
Browse files Browse the repository at this point in the history
… certificate (suggested by William Shallum, closes RT#61409); prepare v0.10
  • Loading branch information
sharyanto committed Sep 23, 2010
1 parent 4dcc491 commit 3e4b24a
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 6 deletions.
3 changes: 3 additions & 0 deletions Changes
@@ -1,5 +1,8 @@
Revision history for Finance-Bank-ID-BCA

0.10 2010-09-23
- add verify_https option (suggested by William Shallum, RT#61409)

0.09 2010-06-17
- build fixes

Expand Down
3 changes: 2 additions & 1 deletion dist.ini
@@ -1,5 +1,5 @@
name = Finance-Bank-ID-BCA
version = 0.09
version = 0.10
author = Steven Haryanto <stevenharyanto@gmail.com>
license = Perl_5
copyright_holder = Steven Haryanto
Expand Down Expand Up @@ -32,4 +32,5 @@ Log::Any = 0
Any::Moose = 0
Mouse = 0
WWW::Mechanize = 0
Crypt::SSLeay = 0
Data::Rmap = 0
25 changes: 22 additions & 3 deletions lib/Finance/Bank/ID/BCA.pm
Expand Up @@ -14,6 +14,8 @@ package Finance::Bank::ID::BCA;
my $ibank = Finance::Bank::ID::BCA->new(
username => 'ABCDEFGH1234', # optional if you're only using parse_statement()
password => '123456', # idem
verify_https => 1, # default is 0
#https_ca_dir => '/etc/ssl/certs', # default is already /etc/ssl/certs
);
eval {
Expand Down Expand Up @@ -123,13 +125,29 @@ C<login()> etc.
=item * mech
Optional. A L<WWW::Mechanize>-like object. By default this module instantiate a
new WWW::Mechanize object to retrieve web pages, but if you want to use a
custom/different one, you are allowed to do so here. Use cases include: you want
to retry and increase timeout due to slow/unreliable network connection (using
new L<Finance::BankUtils::ID::Mechanize> (a WWW::Mechanize subclass) object to
retrieve web pages, but if you want to use a custom/different one, you are
allowed to do so here. Use cases include: you want to retry and increase timeout
due to slow/unreliable network connection (using
L<WWW::Mechanize::Plugin::Retry>), you want to slow things down using
L<WWW::Mechanize::Sleepy>, you want to use IE engine using
L<Win32::IE::Mechanize>, etc.
=item * verify_https
Optional. If you are using the default mech object (see previous option), you can
set this option to 1 to enable SSL certificate verification (recommended for
security). Default is 0.
SSL verification will require a CA bundle directory, default is /etc/ssl/certs.
Adjust B<https_ca_dir> option if your CA bundle is not located in that directory.
=item * https_ca_dir
Optional. Default is /etc/ssl/certs. Used to set HTTPS_CA_DIR environment
variable for enabling certificate checking in Crypt::SSLeay. Only used if
B<verify_https> is on.
=item * logger
Optional. You can supply a L<Log::Any>-like logger object here. If not
Expand All @@ -152,6 +170,7 @@ sub BUILD {
my ($self, $args) = @_;

$self->site("https://ibank.klikbca.com") unless $self->site;
$self->https_host("ibank.klikbca.com") unless $self->https_host;
}

=head2 login()
Expand Down
20 changes: 18 additions & 2 deletions lib/Finance/Bank/ID/Base.pm
Expand Up @@ -12,11 +12,12 @@ L<Finance::Bank::ID::Mandiri>.
=cut

use 5.010;
use Any::Moose;
use Data::Dumper;
use DateTime;
use Log::Any;
use WWW::Mechanize;
use Finance::BankUtils::ID::Mechanize;

=head1 ATTRIBUTES
Expand All @@ -36,6 +37,10 @@ has site => (is => 'rw');

has _req_counter => (is => 'rw', default => 0);

has verify_https => (is => 'rw', default => 0);
has https_ca_dir => (is => 'rw', default => '/etc/ssl/certs');
has https_host => (is => 'rw');

=head1 METHODS
=cut
Expand Down Expand Up @@ -72,13 +77,24 @@ sub BUILD {
$self->password($args->{pin}) if $args->{pin} && !$self->password;
}

sub _set_default_mech {
my ($self) = @_;
$self->mech(
Finance::BankUtils::ID::Mechanize->new(
verify_https => $self->verify_https,
https_ca_dir => $self->https_ca_dir,
https_host => $self->https_host,
)
);
}

# if check_sub is supplied, then after the request it will be passed the mech
# object and should return an error string. request is assumed to be failed if
# error string is not empty.

sub _req {
my ($self, $meth, $args, $check_sub) = @_;
$self->mech(new WWW::Mechanize) unless $self->mech;
$self->_set_default_mech unless $self->mech;
my $mech = $self->mech;
my $c = $self->_req_counter + 1;
$self->_req_counter($c);
Expand Down
60 changes: 60 additions & 0 deletions lib/Finance/BankUtils/ID/Mechanize.pm
@@ -0,0 +1,60 @@
package Finance::BankUtils::ID::Mechanize;
# ABSTRACT: A subclass of WWW::Mechanize that does HTTPS certificate verification

=head1 SYNOPSIS
my $mech = Finance::BankUtils::ID::Mechanize->new(
verify_https => 1,
#https_ca_dir => '/etc/ssl/certs',
https_host => 'example.com',
);
# use as you would WWW::Mechanize object ...
=head1 DESCRIPTION
This is a subclass of WWW::Mechanize that does (optional) HTTPS certificate verification.
=cut

use 5.010;
use Crypt::SSLeay;
use Log::Any qw($log);
use base qw(WWW::Mechanize);

=head1 METHODS
=cut

=head2 new()
=cut

sub new {
my ($class, %args) = @_;
my $mech = WWW::Mechanize->new;
$mech->{verify_https} = $args{verify_https} // 0;
$mech->{https_ca_dir} = $args{https_ca_dir} // "/etc/ssl/certs";
$mech->{https_host} = $args{https_host};
bless $mech, $class;
}

=head2 request()
=cut

sub request {
my ($self, $req) = @_;
local $ENV{HTTPS_CA_DIR} = $self->{verify_https} ?
$self->{https_ca_dir} : undef;
$log->trace("HTTPS_CA_DIR = $ENV{HTTPS_CA_DIR}");
if ($self->{verify_https} && $self->{https_host}) {
$req->header('If-SSL-Cert-Subject',
qr!\Q/CN=$self->{https_host}\E(/|$)!);
}
$log->trace('Mech request: ' . $req->headers_as_string);
my $resp = $self->SUPER::request($req);
$log->trace('Mech response: ' . $resp->headers_as_string);
$resp;
}

1;
33 changes: 33 additions & 0 deletions t/release-verify_https.t
@@ -0,0 +1,33 @@
#!perl -Tw

BEGIN {
unless ($ENV{RELEASE_TESTING}) {
require Test::More;
Test::More::plan(skip_all => 'these tests are for release candidate testing');
}
}

use strict;
use Test::More tests => 3-1;

use Finance::Bank::ID::BCA;

my $ibank = Finance::Bank::ID::BCA->new(verify_https => 1);
$ibank->_set_default_mech;

$ibank->mech->get($ibank->site);
ok($ibank->mech->success, 'normal request succeed');

# https_ca_dir always set by WWW::Mechanize?
#{
# local $ibank->mech->{https_ca_dir};
# my $req = HTTP::Request->new(GET => $ibank->site);
# my $resp = $ibank->mech->request($req);
# like($resp->headers_as_string,
# qr/^Client-SSL-Warning: Peer certificate not verified/m,
# 'request has SSL warning because https_ca_dir unset');
#}

$ibank->mech->{https_host} = "example.com";
eval { $ibank->mech->get($ibank->site) };
like($@, qr/Bad SSL certificate subject/, 'request failed because https_host doesnt match');

0 comments on commit 3e4b24a

Please sign in to comment.