view release on metacpan or search on metacpan
3.22 2014-05-07
3.21 2014-05-07
- Bad release - deleted
3.20 2013-12-09
- login_form: return OK for mobile IE 10, which also ignores content for
FORBIDDEN response.
- test .pl registry scripts: do not try to load mod_perl.pm
- escape html tags in destination.
- fix abstract in FAQ pod.
3.19 2012-12-28
- split out CGI data handling into ::AuthCookie::Params modules
- use Apache::Request/Apache2::Request from libapreq if available. Otherwise,
fall back to CGI.pm for handling CGI data.
- improve "removed cookie" debug log message
- add dependencies: autobox, Class::Load
- allow username to be '0'
- login_form: return OK for SymbianOS, which ignores content for FORBIDDEN responses.
Version: 3.09
- POD doc fixes.
- MP2: remove _check_request_req() - this was only necessary when
running under both MP1 and MP2. Package name change eliminates the
need for this.
- test suite converted to Test::More style test suites.
- descriptive test descriptions added
- make login() stash credentials in $r->pnotes("${AuthName}Creds") so
that the login form can access the user-supplied credentials if the
login fails.
- bug fix: use of Apache2::URI::unescape_url() does not handle
'+' to ' ' conversion. This caused problems for credentials
that contain spaces.
- MP2: remove mod_perl features from "use mod_perl2" line. This is
no longer supported by mod_perl2.
- MP2: _get_form_data() - switch to CGI.pm to handle form data (fixes
several form data handling bugs)
- In a subrequest, copy $r->prev->user to $r->user (or r->connection->user
for MP1).
- remove Apache2::AuthCookie::Util - no longer necessary
- multi-valued form fields are now handled properly in POST -> GET conversion
Version: 3.06
** BUG FIX: AuthNameSatisfy (Any|All) directives were broken. AuthCookie
was using AuthCookieSatisfy rather than ${auth_name}Satisfy. If you
used this feature and had an "AuthCookieSatisfy" directive in your
config file, you MUST change this to ${auth_name}Satisfy.
E.g.: "WhateverSatisfy All"
- created better test cases for AuthNameSatisfy directives.
- when redirecting, set Location with headers_out() not err_headers_out().
apache prefers Location in headers_out, even if the status code is not
200.
- MP2: Apache::unescape_url() -> Apache::URI::unescape_url()
- check for mod_perl 1.9913 or later for Apache::URI (Frederick Moyer)
- Remove set status in login.pl which caused malformed custom error
document (Frederick Moyer)
- Add support for ${auth_name}CookieName to change the name of the cookie
used for each auth name. Default remains ${auth_name}_${auth_type} if
not set.
- make some debug log_error() calls conditional on $debug
Version: 3.05
- Fix POD documentation bug (thanks Steve van der Burg)
lib/Apache/AuthCookie.pm view on Meta::CPAN
$Apache::AuthCookie::VERSION = '3.32';
# ABSTRACT: Perl Authentication and Authorization via cookies
use strict;
use Carp;
use mod_perl qw(1.07 StackedHandlers MethodHandlers Authen Authz);
use Apache::Constants qw(:common M_GET FORBIDDEN OK REDIRECT);
use Apache::AuthCookie::Params;
use Apache::AuthCookie::Util qw(is_blank is_local_destination);
use Apache::Util qw(escape_uri);
use Apache::URI;
use Encode ();
sub recognize_user ($$) {
my ($self, $r) = @_;
# only check if user is not already set
return DECLINED unless is_blank($r->connection->user);
lib/Apache/AuthCookie.pm view on Meta::CPAN
my $args = $self->params($r);
my @pairs = ();
for my $name ($args->param) {
# we dont want to copy login data, only extra data
next if $name eq 'destination'
or $name =~ /^credential_\d+$/;
for my $v ($args->param($name)) {
push @pairs, escape_uri($name) . '=' . escape_uri($v);
}
}
$r->args(join '&', @pairs) if scalar(@pairs) > 0;
$r->method('GET');
$r->method_number(M_GET);
$r->headers_in->unset('Content-Length');
}
lib/Apache/AuthCookie.pm view on Meta::CPAN
$r->header_out(Location => $self->untaint_destination($destination));
return REDIRECT;
}
sub untaint_destination {
my ($self, $dest) = @_;
return Apache::AuthCookie::Util::escape_destination($dest);
}
sub logout($$) {
my ($self, $r) = @_;
my $debug = $r->dir_config("AuthCookieDebug") || 0;
$self->remove_cookie;
$self->handle_cache;
lib/Apache/AuthCookie.pm view on Meta::CPAN
the C<authen_cred()> method, passing it C<$r> and all the submitted
data with names like C<"credential_#">, where # is a number. These will
be passed in a simple array, so the prototype is
C<$self-E<gt>authen_cred($r, @credentials)>. After calling
C<authen_cred()>, we set the user's cookie and redirect to the
URL contained in the C<"destination"> submitted form field.
=head2 untaint_destination($uri)
This method returns a modified version of the destination parameter
before embedding it into the response header. Per default it escapes
CR, LF and TAB characters of the uri to avoid certain types of
security attacks. You can override it to more limit the allowed
destinations, e.g., only allow relative uris, only special hosts or
only limited set of characters.
=head2 logout($r)
This is simply a convenience method that unsets the session key for
you. You can call it in your logout scripts. Usually this looks like
C<$r-E<gt>auth_type-E<gt>logout($r);>.
lib/Apache/AuthCookie.pm view on Meta::CPAN
=item *
your L</authen_cred()> and L</authen_ses_key()> function is expected to return
a decoded username, either by passing it through L<Encode/decode()>, or, by
turning on the UTF8 flag if appropriate.
=item *
Due to the way HTTP works, cookies cannot contain non-ASCII characters.
Because of this, if you are including the username in your generated session
key, you will need to escape any non-ascii characters in the session key
returned by L</authen_cred()>.
=item *
Similarly, you must reverse this escaping process in L</authen_ses_key()> and
return a L<Encode/decode()> decoded username. If your L</authen_cred()>
function already only generates ASCII-only session keys then you do not need to
worry about any of this.
=item *
lib/Apache/AuthCookie/Util.pm view on Meta::CPAN
} elsif ($time=~/^\d+/) {
return $time;
} elsif ($time=~/^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)/) {
$offset = ($mult{$2} || 1)*$1;
} else {
return $time;
}
return (time+$offset);
}
# escape embedded CR, LF, TAB's to prevent possible XSS attacks.
# see http://www.securiteam.com/securityreviews/5WP0E2KFGK.html
sub escape_destination {
my $text = shift;
$text =~ s/([\r\n\t\>\<"])/sprintf("%%%02X", ord $1)/ge;
return $text;
}
# return true if the given user agent understands a HTTP_FORBIDDEN response
# with custom content. Some agents (e.g.: Symbian OS browser), use their own
# HTML and completely ignore the HTTP content.
lib/Apache2/AuthCookie.pm view on Meta::CPAN
=item *
your L<authen_cred()> and L<authen_ses_key()> function is expected to return
a decoded username, either by passing it through L<Encode/decode()>, or, by
turning on the UTF8 flag if appropriate.
=item *
Due to the way HTTP works, cookies cannot contain non-ASCII characters.
Because of this, if you are including the username in your generated session
key, you will need to escape any non-ascii characters in the session key
returned by L<authen_cred()>.
=item *
Similarly, you must reverse this escaping process in L<authen_ses_key()> and
return a L<Encode/decode()> decoded username. If your L<authen_cred()>
function already only generates ASCII-only session keys then you do not need to
worry about any of this.
=item *
lib/Apache2/AuthCookie/Base.pm view on Meta::CPAN
sub encoding {
my ($self, $r) = @_;
my $auth_name = $r->auth_name;
return $r->dir_config("${auth_name}Encoding");
}
sub escape_uri {
my ($r, $string) = @_;
return Apache2::Util::escape_path($string, $r->pool);
}
sub get_cookie_path {
my ($self, $r) = @_;
my $auth_name = $r->auth_name;
return $r->dir_config("${auth_name}Path");
}
lib/Apache2/AuthCookie/Base.pm view on Meta::CPAN
if (my $p3p = $r->dir_config("${auth_name}P3P")) {
$r->err_headers_out->set(P3P => $p3p);
}
}
sub untaint_destination {
my ($self, $dest) = @_;
return Apache::AuthCookie::Util::escape_destination($dest);
}
# convert current request to GET
sub _convert_to_get {
my ($self, $r) = @_;
return unless $r->method eq 'POST';
my $debug = $r->dir_config("AuthCookieDebug") || 0;
lib/Apache2/AuthCookie/Base.pm view on Meta::CPAN
my $args = $self->params($r);
my @pairs = ();
for my $name ($args->param) {
# we dont want to copy login data, only extra data
next if $name eq 'destination'
or $name =~ /^credential_\d+$/;
for my $v ($args->param($name)) {
push @pairs, escape_uri($r, $name) . '=' . escape_uri($r, $v);
}
}
$r->args(join '&', @pairs) if scalar(@pairs) > 0;
$r->method('GET');
$r->method_number(M_GET);
$r->headers_in->unset('Content-Length');
}
lib/Apache2/AuthCookie/Base.pm view on Meta::CPAN
=head2 decoded_user($r): string
If you have set ${auth_name}Encoding, then this will return the decoded value of
C<< $r-E<gt>user >>.
=head2 encoding($r): string
Return the ${auth_name}Encoding setting that is in effect for this request.
=head2 escape_uri($r, $value): string
Escape the given string so it is suitable to be used in a URL.
=head2 get_cookie_path($r): string
Returns the value of C<PerlSetVar ${auth_name}Path>.
=head2 handle_cache($r): void
If C<${auth_name}Cache> is defined, this sets up the response so that the
lib/Apache2/AuthCookie/Base.pm view on Meta::CPAN
you can override this method.
=head2 send_p3p($r): void
Set a P3P response header if C<${auth_name}P3P> is configured. The value of
the header is whatever is in the C<${auth_name}P3P> setting.
=head2 untaint_destination($destination): string
This method returns a modified version of the destination parameter before
embedding it into the response header. Per default it escapes CR, LF and TAB
characters of the uri to avoid certain types of security attacks. You can
override it to more limit the allowed destinations, e.g., only allow relative
uris, only special hosts or only limited set of characters.
=for Pod::Coverage OK
DECLINED
SERVER_ERROR
M_GET
HTTP_FORBIDDEN
HTTP_MOVED_TEMPORARILY
lib/Apache2_4/AuthCookie.pm view on Meta::CPAN
=item *
your L<authen_cred()> and L<authen_ses_key()> function is expected to return
a decoded username, either by passing it through L<Encode/decode()>, or, by
turning on the UTF8 flag if appropriate.
=item *
Due to the way HTTP works, cookies cannot contain non-ASCII characters.
Because of this, if you are including the username in your generated session
key, you will need to escape any non-ascii characters in the session key
returned by L<authen_cred()>.
=item *
Similarly, you must reverse this escaping process in L<authen_ses_key()> and
return a L<Encode/decode()> decoded username. If your L<authen_cred()>
function already only generates ASCII-only session keys then you do not need to
worry about any of this.
=item *
t/lib/Sample/Apache/AuthCookieHandler.pm view on Meta::CPAN
package Sample::Apache::AuthCookieHandler;
use strict;
use utf8;
use base 'Apache::AuthCookie';
use Apache;
use Apache::Constants qw(:common);
use Apache::AuthCookie;
use Apache::Util;
use URI::Escape qw(uri_escape_utf8 uri_unescape);
use Encode;
sub authen_cred ($$\@) {
my $self = shift;
my $r = shift;
my @creds = @_;
return if $creds[0] eq 'fail'; # simulate bad_credentials
# This would really authenticate the credentials
# and return the session key.
# Here I'm just using setting the session
# key to the escaped credentials and delaying authentication.
return join ':', map { uri_escape_utf8($_) } @creds;
}
sub authen_ses_key ($$$) {
my ($self, $r, $ses_key) = @_;
# NOTE: uri_escape_utf8() was used to encode this so we have to decode
# using UTF-8. We don't rely on $self->encoding($r) here because if an
# encoding other than UTF-8 is configured in t/conf/extra.conf.in, then the
# wrong encoding gets used here.
my($user, $password) =
map { decode('UTF-8', uri_unescape($_)) }
split /:/, $ses_key, 2;
if ($user eq 'programmer' && $password eq 'Hero') {
return $user;
}
elsif ($user eq 'some-user') {
return $user;
}
elsif ($user eq '0') {
return $user;
t/lib/Sample/Apache2/AuthCookieHandler.pm view on Meta::CPAN
package Sample::Apache2::AuthCookieHandler;
use strict;
use utf8;
use Class::Load 'load_class';
use Apache2::Const qw(:common HTTP_FORBIDDEN);
use Apache2::AuthCookie;
use Apache2::RequestRec;
use Apache2::RequestIO;
use Apache2::Util;
use URI::Escape qw(uri_escape_utf8 uri_unescape);
use Encode qw(decode);
use vars qw(@ISA);
use Apache::Test;
use Apache::TestUtil;
if (have_min_apache_version('2.4.0')) {
load_class('Apache2_4::AuthCookie');
@ISA = qw(Apache2_4::AuthCookie);
}
t/lib/Sample/Apache2/AuthCookieHandler.pm view on Meta::CPAN
my $r = shift;
my @creds = @_;
$r->server->log_error("authen_cred entry");
return if $creds[0] eq 'fail'; # simulate bad_credentials
# This would really authenticate the credentials
# and return the session key.
# Here I'm just using setting the session
# key to the escaped credentials and delaying authentication.
return join ':', map { uri_escape_utf8($_) } @creds;
}
sub authen_ses_key ($$$) {
my ($self, $r, $cookie) = @_;
my ($user, $password) =
map { decode('UTF-8', uri_unescape($_)) }
split /:/, $cookie, 2;
$r->server->log_error("authen_ses_key entry");
$r->server->log_error("user=$user pass=$password cookie=$cookie");
if ($user eq 'programmer' && $password eq 'Hero') {
return $user;
}
elsif ($user eq 'some-user') {
my $r = POST('/LOGIN', [
destination => '/docs/protected/get_me.html',
credential_0 => 'fail',
credential_1 => 'Hero'
]);
like($r->content, qr/creds: fail Hero/s, 'WhatEverCreds pnotes works');
};
# regression - Apache2::URI::unescape_url() does not handle '+' to ' '
# conversion.
subtest 'unescape URL with spaces' => sub {
plan tests => 1;
my $r = POST('/LOGIN', [
destination => '/docs/protected/get_me.html',
credential_0 => 'fail',
credential_1 => 'one two'
]);
like($r->content, qr/creds: fail one two/,
'read form data handles "+" conversion');
my $r = POST('/LOGIN', [
destination => '/docs/protected/get_me.html',
credential_0 => 'fail',
credential_1 => 'one+two'
]);
like($r->content, qr/creds: fail one\+two/,
'read form data handles "+" conversion with encoded +');
};
# XSS attack prevention. make sure embedded \r, \n, \t is escaped in the destination.
subtest 'XSS: no newlines in destination' => sub {
plan tests => 4;
my $r = POST('/LOGIN', [
destination => "/docs/protected/get_me.html\r\nX-Test-Bar: True\r\nX-Test-Foo: True\r\n",
credential_0 => 'programmer',
credential_1 => 'Hero'
]);
ok(!defined $r->header('X-Test-Foo'), 'anti XSS injection');
ok(!defined $r->header('X-Test-Bar'), 'anti XSS injection');
# try with escaped CRLF also.
$r = POST('/LOGIN', [
destination => "/docs/protected/get_me.html%0d%0aX-Test-Foo: True%0d%0aX-Test-Bar: True\r\n",
credential_0 => 'programmer',
credential_1 => 'Hero'
]);
ok(!defined $r->header('X-Test-Foo'), 'anti XSS injection with escaped CRLF');
ok(!defined $r->header('X-Test-Bar'), 'anti XSS injection with escaped CRLF');
};
# embedded html tags in destination
subtest 'XSS: no embedded HTML in destination' => sub {
plan tests => 1;
my $r = POST('/LOGIN', [
destination => '/"><form method="post">Embedded Form</form>'
]);