Browse files

add verify_https (and https_ca_dir & https_host) options to check SSL…

… certificate (suggested by William Shallum, closes RT#61409); prepare v0.10
  • Loading branch information...
1 parent 4dcc491 commit 3e4b24aa38c47a5a01434b7b1985243a972c65b6 Steven Haryanto committed Sep 23, 2010
Showing with 138 additions and 6 deletions.
  1. +3 −0 Changes
  2. +2 −1 dist.ini
  3. +22 −3 lib/Finance/Bank/ID/BCA.pm
  4. +18 −2 lib/Finance/Bank/ID/Base.pm
  5. +60 −0 lib/Finance/BankUtils/ID/Mechanize.pm
  6. +33 −0 t/release-verify_https.t
View
3 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
View
3 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
@@ -32,4 +32,5 @@ Log::Any = 0
Any::Moose = 0
Mouse = 0
WWW::Mechanize = 0
+Crypt::SSLeay = 0
Data::Rmap = 0
View
25 lib/Finance/Bank/ID/BCA.pm
@@ -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 {
@@ -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
@@ -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()
View
20 lib/Finance/Bank/ID/Base.pm
@@ -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
@@ -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
@@ -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);
View
60 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;
View
33 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.