diff options
author | Jonathan Prykop <jonathan@freeside.biz> | 2015-12-07 17:46:45 -0600 |
---|---|---|
committer | Jonathan Prykop <jonathan@freeside.biz> | 2015-12-14 20:22:22 -0600 |
commit | 5a813d91f3d4b1c6eff81dfa9c88e2b587442984 (patch) | |
tree | 69b4dd20858c739a07959c887c06bc9b36486f82 | |
parent | 8cd6e05d5d906da6b001b36bab5aa87ecdfca944 (diff) |
RT#29354: Password Security in Email [customer fields, images, js files]
-rw-r--r-- | FS/FS/Password_Mixin.pm | 34 | ||||
-rw-r--r-- | FS/FS/svc_acct.pm | 1 | ||||
-rw-r--r-- | fs_selfservice/FS-SelfService/cgi/add_password_validation.js (renamed from fs_selfservice/FS-SelfService/cgi/add_password_validation.html) | 16 | ||||
-rw-r--r-- | fs_selfservice/FS-SelfService/cgi/change_password.html | 10 | ||||
-rw-r--r-- | fs_selfservice/FS-SelfService/cgi/images/error.png | bin | 0 -> 666 bytes | |||
-rw-r--r-- | fs_selfservice/FS-SelfService/cgi/images/tick.png | bin | 0 -> 537 bytes | |||
-rw-r--r-- | fs_selfservice/FS-SelfService/cgi/process_forgot_password.html | 19 | ||||
-rwxr-xr-x | fs_selfservice/FS-SelfService/cgi/selfservice.cgi | 9 | ||||
-rw-r--r-- | fs_selfservice/FS-SelfService/cgi/send_xmlhttp.js (renamed from fs_selfservice/FS-SelfService/cgi/send_xmlhttp.html) | 2 | ||||
-rwxr-xr-x | fs_selfservice/FS-SelfService/cgi/signup.cgi | 28 | ||||
-rwxr-xr-x | fs_selfservice/FS-SelfService/cgi/signup.html | 12 | ||||
-rw-r--r-- | httemplate/elements/validate_password.html | 8 |
12 files changed, 85 insertions, 54 deletions
diff --git a/FS/FS/Password_Mixin.pm b/FS/FS/Password_Mixin.pm index 3129366..834fd6f 100644 --- a/FS/FS/Password_Mixin.pm +++ b/FS/FS/Password_Mixin.pm @@ -67,6 +67,40 @@ sub is_password_allowed { return '' unless $self->get($self->primary_key); # for validating new passwords pre-insert + #check against customer fields + my $cust_main = $self->cust_main; + if ($cust_main) { + my @words; + # words from cust_main + foreach my $field ( qw( last first daytime night fax mobile ) ) { + push @words, split(/\W/,$cust_main->get($field)); + } + # words from cust_location + foreach my $loc ($cust_main->cust_location) { + foreach my $field ( qw(address1 address2 city county state zip) ) { + push @words, split(/\W/,$loc->get($field)); + } + } + # words from cust_contact & contact_phone + foreach my $contact (map { $_->contact } $cust_main->cust_contact) { + foreach my $field ( qw(last first) ) { + push @words, split(/\W/,$contact->get($field)); + } + # not hugely useful right now, hyphenless stored values longer than password max, + # but max will probably be increased eventually... + foreach my $phone ( qsearch('contact_phone', {'contactnum' => $contact->contactnum}) ) { + push @words, split(/\W/,$phone->get('phonenum')); + } + } + # do the actual checking + foreach my $word (@words) { + next unless length($word) > 2; + if ($password =~ /$word/i) { + return qq(Password contains account information '$word'); + } + } + } + my $no_reuse = 3; # allow override here if we really must diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index 38cebc1..53b12f1 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -2686,6 +2686,7 @@ sub password_svc_check { my ($self, $password) = @_; foreach my $field ( qw(username finger) ) { foreach my $word (split(/\W+/,$self->get($field))) { + next unless length($word) > 2; if ($password =~ /$word/i) { return qq(Password contains account information '$word'); } diff --git a/fs_selfservice/FS-SelfService/cgi/add_password_validation.html b/fs_selfservice/FS-SelfService/cgi/add_password_validation.js index e349fd7..e2e3227 100644 --- a/fs_selfservice/FS-SelfService/cgi/add_password_validation.html +++ b/fs_selfservice/FS-SelfService/cgi/add_password_validation.js @@ -1,5 +1,4 @@ -<SCRIPT> -function add_password_validation (fieldid) { +function add_password_validation (fieldid,nologin) { var inputfield = document.getElementById(fieldid); inputfield.onchange = function () { var fieldid = this.id+'_result'; @@ -11,19 +10,22 @@ function add_password_validation (fieldid) { } if (this.value) { resultfield.innerHTML = '<SPAN STYLE="color: blue;">Validating password...</SPAN>'; + var action = nologin ? 'validate_password_nologin' : 'validate_password'; send_xmlhttp('selfservice.cgi', - ['action','validate_password','fieldid',fieldid,'svcnum',svcnum,'check_password',this.value], + ['action',action,'fieldid',fieldid,'svcnum',svcnum,'check_password',this.value], function (result) { result = JSON.parse(result); var resultfield = document.getElementById(result.fieldid); if (resultfield) { + var errorimg = '<IMG SRC="images/error.png" style="width: 1em; display: inline-block; padding-right: .5em">'; + var validimg = '<IMG SRC="images/tick.png" style="width: 1em; display: inline-block; padding-right: .5em">'; if (result.valid) { - resultfield.innerHTML = '<SPAN STYLE="color: green;">Password valid!</SPAN>'; + resultfield.innerHTML = validimg+'<SPAN STYLE="color: green;">Password valid!</SPAN>'; } else if (result.error) { - resultfield.innerHTML = '<SPAN STYLE="color: red;">'+result.error+'</SPAN>'; + resultfield.innerHTML = errorimg+'<SPAN STYLE="color: red;">'+result.error+'</SPAN>'; } else { result.syserror = result.syserror || 'Server error'; - resultfield.innerHTML = '<SPAN STYLE="color: red;">'+result.syserror+'</SPAN>'; + resultfield.innerHTML = errorimg+'<SPAN STYLE="color: red;">'+result.syserror+'</SPAN>'; } } } @@ -33,4 +35,4 @@ function add_password_validation (fieldid) { } }; } -</SCRIPT> + diff --git a/fs_selfservice/FS-SelfService/cgi/change_password.html b/fs_selfservice/FS-SelfService/cgi/change_password.html index ef66554..879faf2 100644 --- a/fs_selfservice/FS-SelfService/cgi/change_password.html +++ b/fs_selfservice/FS-SelfService/cgi/change_password.html @@ -28,11 +28,11 @@ <TD> <INPUT ID="new_password" TYPE="password" NAME="new_password" SIZE="18"> <DIV ID="new_password_result"></DIV> -<%= include('send_xmlhttp') %> -<%= include('add_password_validation') %> -<SCRIPT> -add_password_validation('new_password'); -</SCRIPT> + <SCRIPT SRC="send_xmlhttp.js"></SCRIPT> + <SCRIPT SRC="add_password_validation.js"></SCRIPT> + <SCRIPT> + add_password_validation('new_password'); + </SCRIPT> </TD> </TR> diff --git a/fs_selfservice/FS-SelfService/cgi/images/error.png b/fs_selfservice/FS-SelfService/cgi/images/error.png Binary files differnew file mode 100644 index 0000000..628cf2d --- /dev/null +++ b/fs_selfservice/FS-SelfService/cgi/images/error.png diff --git a/fs_selfservice/FS-SelfService/cgi/images/tick.png b/fs_selfservice/FS-SelfService/cgi/images/tick.png Binary files differnew file mode 100644 index 0000000..a9925a0 --- /dev/null +++ b/fs_selfservice/FS-SelfService/cgi/images/tick.png diff --git a/fs_selfservice/FS-SelfService/cgi/process_forgot_password.html b/fs_selfservice/FS-SelfService/cgi/process_forgot_password.html index ec672c8..da6fc8d 100644 --- a/fs_selfservice/FS-SelfService/cgi/process_forgot_password.html +++ b/fs_selfservice/FS-SelfService/cgi/process_forgot_password.html @@ -15,6 +15,7 @@ <INPUT TYPE="hidden" NAME="session_id" VALUE="<%= $session_id %>"> <INPUT TYPE="hidden" NAME="agentnum" VALUE="<%= $agentnum %>"> +<DIV STYLE="background: <%= $box_bgcolor || '#c0c0c0' %>"> <TABLE BGCOLOR="<%= $box_bgcolor || '#c0c0c0' %>" BORDER=0 CELLSPACING=2 CELLPADDING=0> <%= if (!$error) { @@ -23,16 +24,27 @@ <TR> <TH ALIGN="right">New password: </TH> - <TD><INPUT TYPE="password" NAME="new_password" SIZE="18"></TD> + <TD> + <INPUT ID="new_password" TYPE="password" NAME="new_password" SIZE="18"> + </TD> + <TD> + <SPAN ID="new_password_result"></SPAN> + <SCRIPT SRC="send_xmlhttp.js"></SCRIPT> + <SCRIPT SRC="add_password_validation.js"></SCRIPT> + <SCRIPT> + add_password_validation('new_password',true); + </SCRIPT> + </TD> </TR> <TR> <TH ALIGN="right">Re-enter new password: </TH> <TD><INPUT TYPE="password" NAME="new_password2" SIZE="18"></TD> + <TD></TD> </TR> - <TR> - <TD COLSPAN=2 ALIGN="center"><INPUT TYPE="submit" VALUE="Change password"></TD> + <TD COLSPAN="2" ALIGN="center"><INPUT TYPE="submit" VALUE="Change password"></TD> + <TD></TD> </TR> END @@ -40,6 +52,7 @@ END %> </TABLE> +</DIV> </FORM> <%= $body_footer %> diff --git a/fs_selfservice/FS-SelfService/cgi/selfservice.cgi b/fs_selfservice/FS-SelfService/cgi/selfservice.cgi index 5845122..aff9bca 100755 --- a/fs_selfservice/FS-SelfService/cgi/selfservice.cgi +++ b/fs_selfservice/FS-SelfService/cgi/selfservice.cgi @@ -95,6 +95,7 @@ my @nologin_actions = (qw( process_forgot_password do_process_forgot_password process_forgot_password_session + validate_password_nologin )); push @actions, @nologin_actions; my %nologin_actions = map { $_=>1 } @nologin_actions; @@ -1132,6 +1133,14 @@ sub validate_password { ) } +sub validate_password_nologin { + $action = 'validate_password'; #use same landing page + validate_passwd( + map { $_ => scalar($cgi->param($_)) } + qw( fieldid check_password ) + ) +} + #-- sub do_template { diff --git a/fs_selfservice/FS-SelfService/cgi/send_xmlhttp.html b/fs_selfservice/FS-SelfService/cgi/send_xmlhttp.js index ac85cb2..e299168 100644 --- a/fs_selfservice/FS-SelfService/cgi/send_xmlhttp.html +++ b/fs_selfservice/FS-SelfService/cgi/send_xmlhttp.js @@ -1,4 +1,3 @@ -<SCRIPT> function rs_init_object () { var A; try { @@ -41,5 +40,4 @@ function send_xmlhttp (url,args,callback) { xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xmlhttp.send(content); } -</SCRIPT> diff --git a/fs_selfservice/FS-SelfService/cgi/signup.cgi b/fs_selfservice/FS-SelfService/cgi/signup.cgi index 072ce96..817fdd3 100755 --- a/fs_selfservice/FS-SelfService/cgi/signup.cgi +++ b/fs_selfservice/FS-SelfService/cgi/signup.cgi @@ -508,31 +508,3 @@ use FS::SelfService qw( regionselector expselect popselector domainselector didselector ); -sub add_password_validation { - my $fieldid = shift; - my $out = ''; - if ((-e './send_xmlhttp.html') && (-e './add_password_validation.html')) { - my $template = new Text::Template( TYPE => 'FILE', - SOURCE => "./send_xmlhttp.html", - DELIMITERS => [ '<%=', '%>' ], - UNTAINT => 1, - ) - or die $Text::Template::ERROR; - $out .= $template->fill_in( PACKAGE => 'FS::SelfService::_signupcgi' ); - $template = new Text::Template( TYPE => 'FILE', - SOURCE => "./add_password_validation.html", - DELIMITERS => [ '<%=', '%>' ], - UNTAINT => 1, - ) - or die $Text::Template::ERROR; - $out .= $template->fill_in( PACKAGE => 'FS::SelfService::_signupcgi' ); - $out .= <<ENDOUT; -<SCRIPT> -add_password_validation('$fieldid'); -</SCRIPT> -ENDOUT - } - return $out; -} - - diff --git a/fs_selfservice/FS-SelfService/cgi/signup.html b/fs_selfservice/FS-SelfService/cgi/signup.html index 5900ba6..def5299 100755 --- a/fs_selfservice/FS-SelfService/cgi/signup.html +++ b/fs_selfservice/FS-SelfService/cgi/signup.html @@ -386,12 +386,12 @@ ENDOUT <TD ALIGN="right">Password</TD> <TD> <INPUT ID="new_password" TYPE="password" NAME="_password" VALUE="$_password"> - <DIV ID="new_password_result"></DIV> -ENDOUT - - $OUT .= add_password_validation('new_password'); - - $OUT .= <<ENDOUT; + <SPAN ID="new_password_result"></SPAN> + <SCRIPT SRC="send_xmlhttp.js"></SCRIPT> + <SCRIPT SRC="add_password_validation.js"></SCRIPT> + <SCRIPT> + add_password_validation('new_password',true); + </SCRIPT> </TD> </TR> <TR> diff --git a/httemplate/elements/validate_password.html b/httemplate/elements/validate_password.html index fd2cb6c..a488c4f 100644 --- a/httemplate/elements/validate_password.html +++ b/httemplate/elements/validate_password.html @@ -32,13 +32,15 @@ function add_password_validation (fieldid) { result = JSON.parse(result); var resultfield = document.getElementById(result.fieldid); if (resultfield) { + var errorimg = '<IMG SRC="<% $p %>images/error.png" style="width: 1em; display: inline-block; padding-right: .5em">'; + var validimg = '<IMG SRC="<% $p %>images/tick.png" style="width: 1em; display: inline-block; padding-right: .5em">'; if (result.valid) { - resultfield.innerHTML = '<SPAN STYLE="color: green;">Password valid!</SPAN>'; + resultfield.innerHTML = validimg+'<SPAN STYLE="color: green;">Password valid!</SPAN>'; } else if (result.error) { - resultfield.innerHTML = '<SPAN STYLE="color: red;">'+result.error+'</SPAN>'; + resultfield.innerHTML = errorimg+'<SPAN STYLE="color: red;">'+result.error+'</SPAN>'; } else { result.syserror = result.syserror || 'Server error'; - resultfield.innerHTML = '<SPAN STYLE="color: red;">'+result.syserror+'</SPAN>'; + resultfield.innerHTML = errorimg+'<SPAN STYLE="color: red;">'+result.syserror+'</SPAN>'; } } } |