view release on metacpan or search on metacpan
Connect to adam.acme.com, and import the Signing certificate from menu
bar.
7. Try the demos.
All relevant user login info is:
user: bob
password: sekret
pin code: 0918
As you go from top to bottom in the menu, there are increasing
authentication requirements, and increasingly strong credentials are
issued. When you get to milt.sec.acme.com, watch carefully, as you will
be redirected to stu.transacme.com for login. And, when stu issues
URL credentials you will be transparently redirected across domains to get
cookies for milt.sec.acme.com and noam.acme.org, before being redirected
back to the original milt request. Then you may hop to noam and stu
without further interruption.
->renew() Will re-issue credentials if proper conditions are satisfied
->delete() Will delete credentials where relevant (i.e. deletes cookies).
At one level beneath SecSess (SecSess::Cookie.pm and SecSess::URL.pm),
are the methods for interpreting and manipulating credentials.
At the lowest level, are subclasses which "know" how to interpret the
*initial* identifying information during the issuance of credentials.
So, *::Cookie::LoginForm presents the client with a user/password
login form for identification. And thus the difference between
*::Cookie::URL and *::URL::Cookie is that the former will issue cookies
after validating an URL credential, and the latter will "issue" an URL
credential (typically it will redirect to a resource with realm=cred in
the URL) after validating a cookie.
=head1 CREDENTIAL FORMAT
Credentials in Apache::SecSess have a similar format:
protection or strength roughly measured in bits. They are the signed
values appearing *inside* the credential hash described above. In the
case of cookies, where multiple credentials with different security
levels are may be present at request time, only the strongest credentials
are used to make this determination. (The cleartext pairs (qop,authqop)
are sorted lexicographically to determine which cookies to use. The
cleartext values are not trusted.)
The QOP parameters are separated to allow flexibility in the threat model.
In the simplest paradigm (and first demo examples), qop=0 and authqop=40,
which merely indicates that the user ID's and passwords are protected with
SSL but the web docs acquired with them are not. This is somewhat common
over intranets. Under the stronger threat model of an active adversary
who controls the untrusted network, true end-to-end security is
required, but we may still wish to separate session and authentication
qualities of protection. For example, if all SSL sessions never drop
below 128-bits, we may still choose to allow weaker strength during user
authentication, say with a 20-bit PIN or one-time password. Scientific
cryptography cannot always afford to distinguish between an attack which
costs 2^20 computations and one which succeeds with probability 1/2^20,
because with 1 million users, the two situations are identical. But, for
practical risk assessment, it may be perfectly acceptable to trade strong
session credentials for weak login credentials.
The values of qop and authqop issued are determined by the
Apache::SecSess object in all cases. For URL credentials they come
directly from arguments sessQOP and authQOP, respectively. For cookie
credentials, they come from the hash keys of the argument cookieDomain
Here are some examples taken from the demo.
cookieDomain => {0 => '.acme.com', 40 => '.acme.com'}
will set two cookies for the .acme.com domain: a non-secure one of
form 'realm:0,0=value', and a secure one of form 'realm:40,40=value'.
cookieDomain => {'0,40' => 'lysander.acme.com'}
will set a single non-secure cookie for the given host. This is a
common paradigm for protecting passwords over an intranet.
cookieDomain => {
0 => '.acme.com',
40 => '.acme.com', # weak wildcard domain
'64,128' => '.sec.acme.com', # stronger wildcard domain
128 => 'milt.sec.acme.com'
}
will set 4 cookies, all but the first being secure. In addition to
an explicit hostname at the end, this declaration defines two
See the URL-Chaining example and the demo for more details.
=head2 Database Object Argument
dbo => Apache::SecSess::DBI->new(
dbifile => '/usr/local/apache/conf/private/dbilogin.txt'
),
UNFINISHED. Apache::SecSess was designed to abstractly handle user
information. All user ID, password, X.509 DN queries are handled through
an opaque object of class Apache::SecSess::DBI.
Since there is no documentation for this version, you must follow the
instructions in INSTALL to get it to work. Read db/* for more
info.
=head1 EXAMPLES
=head2 A Simple Cookie Example
SecSess/Cookie.pm view on Meta::CPAN
## check if *new* user is valid
$status = $self->{dbo}->get_user_status($newuid);
unless ($status eq 'enabled') {
return {
message => "User '$newuid' unavailable: '$status'.",
uri => "$form?msg=$status"
};
}
## validate super user's password
$msg = $self->{dbo}->validate_user_pass($uid, $pw);
unless ($msg eq 'OK') {
return {
message => "Incorrect superuser password for '$uid'.",
uri => "$form?msg=$msg"
};
}
## everything looks good, allow the change of identity
return {
message => "Superuser '$uid' changing to user '$newuid'",
newuid => $newuid
};
}
SecSess/Cookie/BasicAuth.pm view on Meta::CPAN
## validate (usually non-cookie) credentials used to authenicate user
sub verifyIdentity {
my $self = shift;
my($r) = @_;
my $log = $r->log;
my($uid, $res, $pw, $msg);
$log->debug(ref($self), "->verifyIdentity():");
## read password and user id if present, bail otherwise
($res, $pw) = $r->get_basic_auth_pw;
unless ($res eq OK) { # I hate this
return {
message => "Basic auth required.",
auth_required => 'true'
};
}
$uid = $r->user;
unless ($uid && $pw) {
$r->note_basic_auth_failure;
return {
message => "Basic auth failed, uid: '$uid', pw: '$pw'.",
auth_required => 'true'
};
}
## validate user and password
$msg = $self->{dbo}->validate_user_pass($uid, $pw);
unless ($msg eq 'OK') {
return {
message => $msg,
auth_required => 'true',
};
}
return undef;
}
SecSess/Cookie/LoginForm.pm view on Meta::CPAN
$log->debug(ref($self), "->verifyIdentity():");
## is this the initial visit to the form?
unless ($r->method eq 'POST') { # allow no GET for now ...
return {
message => 'Initial visit to login form.',
fill_form => 'true'
}
}
## extract user ID, password and other data
%params = $r->content;
$uid = $params{uid};
$pw = $params{pw};
%args = $r->args;
$url = $args{url};
## validate user and password
$msg = $self->{dbo}->validate_user_pass($uid, $pw);
unless ($msg eq 'OK') {
$form = $self->authenURL;
$form .= "?msg=$msg";
$form .= "&url=$url" if $url;
return {
message => "Auth error code: '$msg'",
uri => $form
};
}
SecSess/Cookie/X509PIN.pm view on Meta::CPAN
}
## is this the initial visit to the form?
unless ($r->method eq 'POST') { # allow no GET for now ...
return {
message => 'Initial visit to login form.',
fill_form => 'true'
}
}
## extract user ID, password and other data from POST
%params = $r->content;
$pin = $params{pin};
%args = $r->args;
$url = $args{url};
## validate PIN code
$msg = $self->{dbo}->validate_stored_token($uid, $pin, 'pin');
unless ($msg eq 'OK') {
$form = $self->authenURL;
$form .= "?msg=$msg";
SecSess/DBI.pm view on Meta::CPAN
## init object only means acquiring DBI login info
sub _init {
my $self = shift;
my ($usage, $file, $fh, $dbistr, $dbiuser, $dbipw);
## define usage
$usage = sprintf("usage: %s->new([%s] || [%s]);\n", ref($self),
"dbifile => 'filename'",
"dbistr => 'DBI string', dbiuser => 'DBI user', "
. "dbipw => 'DBI password'"
);
## must provide some way to connect
if ($self->{dbistr} && defined($self->{dbiuser}) &&
defined($self->{dbipw})) {
return;
}
unless ($file = $self->{dbifile}) { die $usage; }
## read login info from file
SecSess/DBI.pm view on Meta::CPAN
## get the full name
sub get_full_name {
my $self = shift;
my($uid) = @_;
my $rec = $self->get_user_record($uid);
unless ($rec) { return undef; }
return $rec->{name};
}
## get UNIX-style password hash
sub get_pwhash {
my $self = shift;
my($uid) = @_;
return $self->get_stored_token($uid, 'unixpw');
}
## get stored token
sub get_stored_token {
my $self = shift;
my($uid, $authid) = @_;
SecSess/DBI.pm view on Meta::CPAN
ENDSQL
$uasth->execute($uid, $authid);
# process query output
($token) = $uasth->fetchrow_array;
$uasth->finish;
return $self->dbunquote($token);
}
## valid a user/password against database
sub validate_user_pass {
my $self = shift;
my($uid, $pw) = @_;
## this little extra step is necessary for crypt() to work
unless ($uid && $pw) { return 'empty'; }
my $pwhash = $self->get_pwhash($uid);
return $self->validate_stored_token($uid, crypt($pw, $pwhash), 'unixpw');
}
## validate a general stored token (eg, password, PIN, etc)
sub validate_stored_token {
my $self = shift;
my($uid, $token, $authid) = @_;
my($status);
unless ($uid) { return 'empty'; } # empty uid argument
$status = $self->get_user_status($uid);
unless ($status eq 'enabled') { return $status; } # disabled or unknown
unless ($token) { return 'empty'; } # empty token argument
unless ($token eq $self->get_stored_token($uid, $authid)) {
SecSess/DBI.pm view on Meta::CPAN
## reset *all* failure counts at once
$uasth = $self->{dbh}->prepare(<<'ENDSQL');
UPDATE userauthen
SET failcount = ?
WHERE usrid = ?
ENDSQL
$uasth->execute(0, $uid);
$uasth->finish;
}
## change user's password
sub change_password {
my $self = shift;
my($uid, $pw) = @_;
my(@salt, $hash, $sth);
# prepare new hash
@salt = ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand(64), rand(64)];
$hash = crypt($pw, join('', @salt));
# prepare set statement
$self->refresh_dbh;
%groups = (
admin => 'Administrative privileges',
transact => 'Transaction privileges',
confidential => 'Sensitivity privilege: confidential data',
secret => 'Sensitivity privilege: secret data',
topsecret => 'Sensitivity privilege: top secret data',
super => 'Sensitivity privilege: all data',
);
%authens = ( # authid => [<description>, <maximum failure count>]
unixpw => ['Unix password crypt', 100],
x509email => ['X.509 certificate (signed by us)', 0],
pin => ['Personal Identity Number (PIN)', 10]
);
%users = (
bob => { name => 'Col. Robert Bobtight', group => 'bob',
groups => [qw(super admin transact confidential secret topsecret)],
unixpw => crypt('sekret', 'Mq'),
x509email => 'bob@acme.com',
pin => '0918'
db/schema.sql view on Meta::CPAN
\!#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
\!# authentication methods
\!# maxfail = # of consecutive failures before status 'disabled'
create table Authens (
authid char(16) primary key,
descr varchar,
maxfail integer
);
\!# user authentication data (e.g. passwords, certs, etc)
create table UserAuthen (
usrid char(16),
authid char(16),
token varchar,
failcount integer,
unique (usrid, authid)
);
\!# Note: This data model, with a single string 'token', is overly simplistic
\!# for things like challenge/response, multiple CA chaining, etc.
demo/ht/john.sec.acme.com/authen/index.html view on Meta::CPAN
my $sorry = 'Please contact the system administrator.';
my $err = '<font color="red" size=5>An Error Occurred.</font><p>';
my $noerr = '<font color="#000000" size=5>Logging in.</font><p>';
my %message = (
empty => "$err One or more required fields contained no data. $pta",
unknown => "$err Unknown user. $pta",
again => "$err The supplied PIN code was incorrect. $pta",
disabled => "$err Your account has been temporarily disabled. $sorry"
);
my $inputs = [
{name => 'pin', prompt => 'Your PIN Code:', type => 'password'}
];
my $action = "https://john.sec.acme.com/authen?url=$url";
</%init>
<%args>
$uid => undef
$msg => undef
$url => undef
</%args>
demo/ht/john.sec.acme.com/changeid/index.html view on Meta::CPAN
&>
<%init>
my $pta = 'Please try again.';
my $sorry = 'Please contact the system administrator.';
my $err = '<font color="red" size=5>An Error Occurred.</font><p>';
my $noerr = '<font color="#000000" size=5>Logging in.</font><p>';
my %message = (
empty => "$err One or more required fields contained no data. $pta",
unknown => "$err Unknown user. $pta",
again => "$err The supplied password was incorrect. $pta",
disabled => "$err The account has been temporarily disabled. $sorry"
);
my $action = 'https://john.sec.acme.com/changeid';
my $inputs = [
{name => 'newuid', prompt => 'Desired User ID:', type => 'text'},
{name => 'pw', prompt => 'Your Password:', type => 'password'}
];
</%init>
<%args>
$msg => undef
$url => undef
</%args>
demo/ht/lysander.acme.com/authen/index.html view on Meta::CPAN
&>
<%init>
my $pta = 'Please try again.';
my $sorry = 'Please contact the system administrator.';
my $err = '<font color="red" size=5>An Error Occurred.</font><p>';
my $noerr = '<font color="#000000" size=5>Logging in.</font><p>';
my %message = (
empty => "$err One or more required fields contained no data. $pta",
unknown => "$err Unknown user. $pta",
again => "$err The supplied password was incorrect. $pta",
disabled => "$err Your account has been temporarily disabled. $sorry"
);
my $inputs = [
{name => 'uid', prompt => 'Your Login ID:', type => 'text'},
{name => 'pw', prompt => 'Your Password:', type => 'password'}
];
my $action = "https://lysander.acme.com/authen?url=$url";
</%init>
<%args>
$msg => undef
$url => undef
</%args>
demo/ht/milt.sec.acme.com/changeid/index.html view on Meta::CPAN
&>
<%init>
my $pta = 'Please try again.';
my $sorry = 'Please contact the system administrator.';
my $err = '<font color="red" size=5>An Error Occurred.</font><p>';
my $noerr = '<font color="#000000" size=5>Logging in.</font><p>';
my %message = (
empty => "$err One or more required fields contained no data. $pta",
unknown => "$err Unknown user. $pta",
again => "$err The supplied password was incorrect. $pta",
disabled => "$err The account has been temporarily disabled. $sorry"
);
my $action = 'https://john.sec.acme.com/changeid';
my $inputs = [
{name => 'newuid', prompt => 'Desired User ID:', type => 'text'},
{name => 'pw', prompt => 'Your Password:', type => 'password'}
];
</%init>
<%args>
$msg => undef
$url => undef
</%args>
demo/ht/noam.acme.org/changeid/index.html view on Meta::CPAN
&>
<%init>
my $pta = 'Please try again.';
my $sorry = 'Please contact the system administrator.';
my $err = '<font color="red" size=5>An Error Occurred.</font><p>';
my $noerr = '<font color="#000000" size=5>Logging in.</font><p>';
my %message = (
empty => "$err One or more required fields contained no data. $pta",
unknown => "$err Unknown user. $pta",
again => "$err The supplied password was incorrect. $pta",
disabled => "$err The account has been temporarily disabled. $sorry"
);
my $action = 'https://john.sec.acme.com/changeid';
my $inputs = [
{name => 'newuid', prompt => 'Desired User ID:', type => 'text'},
{name => 'pw', prompt => 'Your Password:', type => 'password'}
];
</%init>
<%args>
$msg => undef
$url => undef
</%args>
demo/ht/stu.transacme.com/authen/index.html view on Meta::CPAN
my $sorry = 'Please contact the system administrator.';
my $err = '<font color="red" size=5>An Error Occurred.</font><p>';
my $noerr = '<font color="#000000" size=5>Logging in.</font><p>';
my %message = (
empty => "$err One or more required fields contained no data. $pta",
unknown => "$err Unknown user. $pta",
again => "$err The supplied PIN code was incorrect. $pta",
disabled => "$err Your account has been temporarily disabled. $sorry"
);
my $inputs = [
{name => 'pin', prompt => 'Your PIN Code:', type => 'password'}
];
my $action = "https://stu.transacme.com/authen?url=$url";
</%init>
<%args>
$uid => undef
$msg => undef
$url => undef
</%args>
demo/ht/stu.transacme.com/changeid/index.html view on Meta::CPAN
&>
<%init>
my $pta = 'Please try again.';
my $sorry = 'Please contact the system administrator.';
my $err = '<font color="red" size=5>An Error Occurred.</font><p>';
my $noerr = '<font color="#000000" size=5>Logging in.</font><p>';
my %message = (
empty => "$err One or more required fields contained no data. $pta",
unknown => "$err Unknown user. $pta",
again => "$err The supplied password was incorrect. $pta",
disabled => "$err The account has been temporarily disabled. $sorry"
);
my $action = 'https://john.sec.acme.com/changeid';
my $inputs = [
{name => 'newuid', prompt => 'Desired User ID:', type => 'text'},
{name => 'pw', prompt => 'Your Password:', type => 'password'}
];
</%init>
<%args>
$msg => undef
$url => undef
</%args>
demo/ht/tom.acme.com/changeid/index.html view on Meta::CPAN
&>
<%init>
my $pta = 'Please try again.';
my $sorry = 'Please contact the system administrator.';
my $err = '<font color="red" size=5>An Error Occurred.</font><p>';
my $noerr = '<font color="#000000" size=5>Logging in.</font><p>';
my %message = (
empty => "$err One or more required fields contained no data. $pta",
unknown => "$err Unknown user. $pta",
again => "$err The supplied password was incorrect. $pta",
disabled => "$err The account has been temporarily disabled. $sorry"
);
my $action = 'https://tom.acme.com/changeid';
my $inputs = [
{name => 'newuid', prompt => 'Desired User ID:', type => 'text'},
{name => 'pw', prompt => 'Your Password:', type => 'password'}
];
</%init>
<%args>
$msg => undef
$url => undef
</%args>
demo/httpdconf/httpd.sec2.conf view on Meta::CPAN
AccessFileName .htaccess
#
# The following lines prevent .htaccess files from being viewed by
# Web clients. Since .htaccess files often contain authorization
# information, access is disallowed for security reasons. Comment
# these lines out if you want Web visitors to see the contents of
# .htaccess files. If you change the AccessFileName directive above,
# be sure to make the corresponding changes here.
#
# Also, folks tend to use names such as .htpasswd for password
# files, so this will protect those as well.
#
<Files ~ "^\.ht">
Order allow,deny
Deny from all
</Files>
#
# CacheNegotiatedDocs: By default, Apache sends "Pragma: no-cache" with each
# document that was negotiated on the basis of content. This asks proxy
rfc/rfc2965.txt view on Meta::CPAN
problems if they can be used to share cookies between disparate
hosts. For example, a malicious server could embed cookie
information for host a.com in a URI for a CGI on host b.com. User
agent implementors are strongly encouraged to prevent this sort of
exchange whenever possible.
7.4 Cookies For Account Information
While it is common practice to use them this way, cookies are not
designed or intended to be used to hold authentication information,
such as account names and passwords. Unless such cookies are
exchanged over an encrypted path, the account information they
contain is highly vulnerable to perusal and theft.
8. OTHER, SIMILAR, PROPOSALS
Apart from RFC 2109, three other proposals have been made to
accomplish similar goals. This specification began as an amalgam of
Kristol's State-Info proposal [DMK95] and Netscape's Cookie proposal
[Netscape].
utils/mkcerts view on Meta::CPAN
# security
$rsabits = 2048; # number of RSA modulus bits
$digest = 'sha1'; # or 'md5'
$cadays = 4*365; # days of validity for root CA
$days = 2*365; # days of validity for signed certs
# cert files
$cacert = 'acme-ca.crt';
$cakey = 'acme-ca.key';
$capasswd = 'certd@ddy'; # password for root CA (really do change me)
$spasswd = 'serverpw'; # server passwords (not used, unless -e opt)
$cpasswd = 'certb@by'; # PKCS12 client passwords (always used)
# DN info
$dnc = 'US'; # country
$dnst = 'NJ'; # state, province, canton etc
$dnl = 'Basispoint Springs'; # city
$company = 'Acme Industries Inc'; # company name
$dns = 'acme.com'; # DNS domain
#
# things to do when creating new CA (-n option)
utils/mkcerts view on Meta::CPAN
#
# create root request config file
#
$configinfo = <<"END_CONFILE";
[ req ]
default_bits = $rsabits
default_md = $digest
default_keyfile = $cakey
distinguished_name = req_distinguished_name
prompt = no
output_password = $capasswd
[ req_distinguished_name ]
C = $dnc
ST = $dnst
L = $dnl
O = $company
OU = www.$dns
CN = $company Root CA
emailAddress = trustmaster\@$dns
END_CONFILE
utils/mkcerts view on Meta::CPAN
printf("\n:\n: Creating and Signing Cert for '%s' ...\n:\n", $name);
if ($opt_d) {
printf("%s: creating request/key (%s,%s)\n", $name, $req, $key);
}
#
# make configuation file for openssl req
#
$enccmd = ($opt_e) ?
"output_password = $spasswd" :
"encrypt_key = no";
$configinfo = <<"END_CONFILE";
[ req ]
default_bits = $rsabits
default_md = $digest
default_keyfile = $key
distinguished_name = req_distinguished_name
prompt = no
$enccmd