Apache-AppSamurai

 view release on metacpan or  search on metacpan

lib/Apache/AppSamurai/Util.pm  view on Meta::CPAN

    # "+1d"  -- in 1 day
    # "+3M"  -- in 3 months
    # "+2y"  -- in 2 years
    # "-3m"  -- 3 minutes ago(!)
    # If you don't supply one of these forms, we assume you are
    # specifying the date yourself
    my($offset);
    if (!$time || (lc($time) eq 'now')) {
        $offset = 0;
    } elsif ($time=~/^\d+/) {
        return $time;
    } elsif ($time=~/^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)/) {
        $offset = ($mult{$2} || 1)*$1;
    } else {
        return $time;
    }
    return (time+$offset);
}


# Create a session authentication key to send back to the user's browser.
# This is the "session key", not the local "session ID".  It will be used
# with the server's ServerKey value to create the local session ID, and 
# to look up a user's session going forward.  This session key is also used
# to encrypt the user's session data.  Do not log the session authentication
# key!  All logging should reference the server side session key/ID.
#
# If no arguments are passed the key is chosen randomly, else it is a digest of
# the concatenated args
sub CreateSessionAuthKey {
    my $key = '';
    my $cycles = 5;
    my $text = '';

    # Pull in and concatenate custom key text
    if (scalar @_) {
	$text = join("", @_);
	($text =~ /^\s*$/) && ($text = '');
    }

    if ($text) {
	$key = sha256_hex($text);
    } else {
	# You only make a new session once in a while, so take the time to pick
	# something hard. (Though, Bruce Schneier might very well laugh at it.)
	for (my $i=0; $i < $cycles; $i++) {
	    $key = sha256_hex(sprintf("%0.6f", Time::HiRes::time()) . $key . $$);
	}
    }

    # One time I put a VERY stupid bug in this code.  End result: It returned
    # the SHA256 digest of '' for everything.  Stupid.  NEVER AGAIN!!!!
    # (FYI: Yes, this method is unit tested now, too, but still...)
    if ($key =~ /^e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855$/i) {
	die "OH MY GOD!!!! That is the SHA256 of nothing, bozo!";
    }

    return $key;
}

# Hash plaintext password/passphrase
sub HashPass {
    my $plain = shift;
 
    # Check for basic decency.  (This is checked when configuring.  This is just a failsafe.)
    ($plain =~ /^[[:print:]]+$/s) or die "HashPass(): Invalid characters in plaintext passphrase";

    return sha256_hex($plain);
}

# Just hash whatever is passed in, joining as needed
sub HashAny {
    my $plain = join('', @_);
    return sha256_hex($plain);
}
       
# Given session authentication key from browser and ServerKey from the config.
# use a HMAC to compute the real session ID.
sub ComputeSessionId {
    my ($authkey, $serverkey) = @_;

    # This is checked before this point.  This is just a failsafe
    (CheckSidFormat($authkey) && CheckSidFormat($serverkey)) or return undef;
    
    return hmac_sha256_hex($authkey, $serverkey);
}

# Check the composition of the Session ID.  This does not check if the ID 
# exists and that it is well formed
sub CheckSidFormat {
    my $sid = shift;
    (defined($sid)) || (return undef);
    
    my $tlen = $IDLEN * 2;

    # Check that the ID is a hex string of length $IDLEN bytes
    ($sid =~ /^([a-f0-9]{$tlen})$/i) ? (return $1) : (return undef);
}

# Check full URL (host + args).  Untaints as it cleans.  Returns undef if it
# ain't clean.  
sub CheckUrlFormat {
    my $url = shift;
    # Following check pulled out of OWASP FAQ, and converted for Perl
    ($url =~ /((((https?|ftps?|gopher|telnet|nntp):\/\/)|(mailto:|news:))(%[0-9A-Fa-f]{2}|[\-\(\)_\.!\~\*\';\/\?:\@\&=\+\$,A-Za-z0-9])+)([\)\.!\';\/\?:,][[:blank:]])?$/) ? (return $1) : (return undef);
}

# Check host address or DNS name.  NOT A STRICT TEST!  This will allow in
# IPv4 and v6 and most DNS names.  Use CheckHostIP for a strict IPv4 check.
sub CheckHostName {
    my $hostname = shift;
    ($hostname =~ /^\s*([\w\d\-\_\.\:]+)\s*$/) ? (return $1) : (return undef);
}

# Check IPv4 or IPv6 IP for valid format, using a nice little regex
# for the IPv4 check, and a hellaciously long but (as far as I can tell,
# good) regex from http://www.regexlib.com/REDetails.aspx?regexp_id=1000 by
# Jeff Johnston for IPv6 checks.  
sub CheckHostIP {
    my $ip = shift;
    my @t;

lib/Apache/AppSamurai/Util.pm  view on Meta::CPAN

This method of looking up the real (local) session ID allows for keeping the
session authentication key a secret to the web server while it is not
being actively used.  This is important because the session authentication key
is used (in part) to encrypt the user's session data.  Without the session
authentication key, a hacker can not steal information from a stale session
file, remnant data on a hard drive, or from a hacked database.

=head2 CheckSidFormat()

Check input scalar for proper ID format.  (Characters and length.)  Returns
the untainted input, or undef on failure.

Apache::AppSamurai currently uses SHA256 for all digest and ID functions.
All are represented as hex strings with a length of 32 characters.  (256 bits
divided by 4 characters per nibble.)  This magic number is set in the C<$IDLEN>
global in the Util.pm file.  Future versions may be more flexible and allow
alternate digest algorithms.

=head2 CheckUrlFormat()

Check the scalar for proper URL formatting.  Returns the untainted URL or undef
on failure.

This is just a basic check, and allows through ftp:, gopher:, etc in addition
to http: and https:.  It is just a sanity check.  Apply more extensive
filtering using mod_rewrite or other means, as needed.

=head2 CheckHostName()

Check scalar for basic hostname/domain name syntax.  Returns an untainted
version of the input, or undef on failure.

=head2 CheckHostIP()

Check input scalar for proper text IP format. Returns the untainted input
on success, or undef on failure.

IPv4 dotted quads are only supported at this time.  IPv6 support will be
added, but considering the ungodly tangled mess that can represent an
IPv6 address, the motivation to tackle it is not currently present.

=head2 XHalf()

Check that input scalar is text, then convert the second half of the string
to a string of 'X's and return the new string.

This is used for debug logging of potentially sensitive information, where
some context text is required, but where a full disclosure would be dangerous.
Only use this method when the latter half of the text contains all or most
of the sensitive data.  It is a convenience function to avoid needing to
write custom data sanitization into each logging event.

For instance, for a session ID of
"628b49d96dcde97a430dd4f597705899e09a968f793491e4b704cae33a40dc02"
the output would be:
"628b49d96dcde97a430dd4f597705899XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
which would be fairly safe since only half the data would be revealed in the
log. This is still 128 bits of digest, and in most cases would be enough not
to seriously endanger the data.
 
On the other hand, if you allow long passwords and you log a basic
authentication "Authorization:" header of:
"cm9nZXJ0aGVtYW46VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2cu"
the output would be:
"cm9nZXJ0aGVtYW46VGhlIHF1aWNrIGJyb3duIGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".
This is not very safe.  Here is what decoding produces:
"rogertheman:The quick brown e×]u×]u×]u×]u×]u×]u×]u×]u×]u×"
So, the user's name is "rogertheman".  More importantly, we can guess what
the rest of the password is, and we know the length of the password.

Apache::AppSamurai does log the Authorization: header using XHalf when
Debug is enabled.  Be very careful when running production servers!  Only
use Debug when absolutely needed, monitor the logs for sensitive information
leak, and remove debug log data when possible.

That said, leave Debug set to 0 and do not use XHalf in any modules you
code if you find it too risky.

=head1 SEE ALSO

L<Apache::AppSamurai>, L<Digest::SHA>

=head1 AUTHOR

Paul M. Hirsch, C<< <paul at voltagenoir.org> >>

=head1 BUGS

See L<Apache::AppSamurai> for information on bug submission and tracking.

=head1 SUPPORT

See L<Apache::AppSamurai> for support information.

=head1 ACKNOWLEDGEMENTS

This module includes date calculation code from
L<CGI::Util>.

=head1 COPYRIGHT & LICENSE

Copyright 2007 Paul M. Hirsch, all rights reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut



( run in 0.492 second using v1.01-cache-2.11-cpan-39bf76dae61 )