Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision c157c911

Von Moritz Bunkus vor fast 9 Jahren hinzugefügt

  • ID c157c911ac9175e4838990d051c594dd5eec7e21
  • Vorgänger 92deb643
  • Nachfolger 69af5044

Passwörter: Hash-Verfahren PBKDF2 unterstützen und als Standard nutzen

Der aktuelle Stand der Technik sind die SHA-*-Varianten schon lange
nicht mehr. In der Zwischenzeit wurden der PBKDF2-Mechanismus
entwickelt, um schnelles Berechnen zu erschweren. Noch neuer und in
ASICs noch schwerer umsetzbar sind BCrypt und SCrypt, für die es aber
noch keine weit verbreiteten Perl-Module gibt.

Unterschiede anzeigen:

SL/Auth.pm
168 168
    return ERR_PASSWORD;
169 169
  }
170 170

  
171
  $password             = SL::Auth::Password->hash(login => 'root', password => $password);
172 171
  my $admin_password    = SL::Auth::Password->hash_if_unhashed(login => 'root', password => $self->{admin_password}->());
172
  $password             = SL::Auth::Password->hash(login => 'root', password => $password, stored_password => $admin_password);
173 173

  
174 174
  my $result = $password eq $admin_password ? OK : ERR_PASSWORD;
175 175
  $self->set_session_value(SESSION_KEY_ROOT_AUTH() => $result);
SL/Auth/DB.pm
38 38

  
39 39
  my $stored_password = $self->{auth}->get_stored_password($login);
40 40

  
41
  my ($algorithm, $algorithm2);
42

  
43 41
  # Empty password hashes in the database mean just that -- empty
44 42
  # passwords. Hash it for easier comparison.
45
  $stored_password               = SL::Auth::Password->hash(password => $stored_password) unless $stored_password;
46
  ($algorithm, $stored_password) = SL::Auth::Password->parse($stored_password);
47
  ($algorithm2, $password)       = SL::Auth::Password->parse(SL::Auth::Password->hash(password => $password, algorithm => $algorithm, login => $login));
43
  $stored_password    = SL::Auth::Password->hash(password => $stored_password) unless $stored_password;
44
  my ($algorithm)     = SL::Auth::Password->parse($stored_password);
45
  my $hashed_password = SL::Auth::Password->hash(password => $password, algorithm => $algorithm, login => $login, stored_password => $stored_password);
48 46

  
49 47
  $main::lxdebug->leave_sub();
50 48

  
51
  return $password eq $stored_password ? OK : ERR_PASSWORD;
49
  return $hashed_password eq $stored_password ? OK : ERR_PASSWORD;
52 50
}
53 51

  
54 52
sub can_change_password {
SL/Auth/Password.pm
5 5
use Carp;
6 6
use Digest::MD5 ();
7 7
use Digest::SHA ();
8
use Encode ();
9
use PBKDF2::Tiny ();
10

  
11
sub hash_pkkdf2 {
12
  my ($class, %params) = @_;
13

  
14
  # PBKDF2::Tiny expects data to be in octets. Therefore we must
15
  # encode everything we hand over (login, password) to UTF-8.
16

  
17
  # This hash method uses a random hash and not just the user's login
18
  # for its salt. This is due to the official recommendation that at
19
  # least eight octets of random data should be used. Therefore we
20
  # must store the salt together with the hashed password. The format
21
  # in the database is:
22

  
23
  # {PBKDF2}salt-in-hex:hash-in-hex
24

  
25
  my $salt;
26

  
27
  if ((defined $params{stored_password}) && ($params{stored_password} =~ m/^\{PBKDF2\} ([0-9a-f]+) :/x)) {
28
    $salt = (split m{:}, Encode::encode('utf-8', $1), 2)[0];
29

  
30
  } else {
31
    my @login  = map { ord } split m{}, Encode::encode('utf-8', $params{login});
32
    my @random = map { int(rand(256)) } (0..16);
33

  
34
    $salt      = join '', map { sprintf '%02x', $_ } @login, @random;
35
  }
36

  
37
  my $hashed = "{PBKDF2}${salt}:" . join('', map { sprintf '%02x', ord } split m{}, PBKDF2::Tiny::derive('SHA-256', $salt, Encode::encode('utf-8', $params{password})));
38

  
39
  return $hashed;
40
}
8 41

  
9 42
sub hash {
10 43
  my ($class, %params) = @_;
11 44

  
12
  $params{algorithm} ||= 'SHA256S';
45
  $params{algorithm} ||= 'PBKDF2';
13 46

  
14 47
  my $salt = $params{algorithm} =~ m/S$/ ? $params{login} : '';
15 48

  
......
25 58
  } elsif ($params{algorithm} eq 'CRYPT') {
26 59
    return '{CRYPT}' . crypt($params{password}, substr($params{login}, 0, 2));
27 60

  
61
  } elsif ($params{algorithm} =~ m/^PBKDF2/) {
62
    return $class->hash_pkkdf2(password => $params{password}, stored_password => $params{stored_password});
63

  
28 64
  } else {
29 65
    croak 'Unsupported hash algorithm ' . $params{algorithm};
30 66
  }
SL/InstallationCheck.pm
25 25
  { name => "DateTime::Format::Strptime",          url => "http://search.cpan.org/~drolsky/",   debian => 'libdatetime-format-strptime-perl' },
26 26
  { name => "DBI",             version => '1.50',  url => "http://search.cpan.org/~timb/",      debian => 'libdbi-perl' },
27 27
  { name => "DBD::Pg",         version => '1.49',  url => "http://search.cpan.org/~dbdpg/",     debian => 'libdbd-pg-perl' },
28
  { name => "Digest::SHA",                         url => "http://search.cpan.org/~mshelor/",   debian => 'libdigest-sha-perl' },
28 29
  { name => "Email::Address",                      url => "http://search.cpan.org/~rjbs/",      debian => 'libemail-address-perl' },
29 30
  { name => "Email::MIME",                         url => "http://search.cpan.org/~rjbs/",      debian => 'libemail-mime-perl' },
30 31
  { name => "FCGI",            version => '0.72',  url => "http://search.cpan.org/~mstrout/",   debian => 'libfcgi-perl' },
......
37 38
  { name => "List::MoreUtils", version => '0.21',  url => "http://search.cpan.org/~vparseval/", debian => 'liblist-moreutils-perl' },
38 39
  { name => "List::UtilsBy",                       url => "http://search.cpan.org/~pevans/",    debian => 'liblist-utilsby-perl' },
39 40
  { name => "Params::Validate",                    url => "http://search.cpan.org/~drolsky/",   debian => 'libparams-validate-perl' },
41
  { name => "PBKDF2::Tiny",    version => '0.005', url => "http://search.cpan.org/~arodland/", },
40 42
  { name => "PDF::API2",       version => '2.000', url => "http://search.cpan.org/~areibens/",  debian => 'libpdf-api2-perl' },
41 43
  { name => "Rose::Object",                        url => "http://search.cpan.org/~jsiracusa/", debian => 'librose-object-perl' },
42 44
  { name => "Rose::DB",                            url => "http://search.cpan.org/~jsiracusa/", debian => 'librose-db-perl' },
......
54 56
);
55 57

  
56 58
@optional_modules = (
57
  { name => "Digest::SHA",                         url => "http://search.cpan.org/~mshelor/",   debian => 'libdigest-sha-perl' },
58 59
  { name => "IO::Socket::SSL",                     url => "http://search.cpan.org/~sullr/",     debian => 'libio-socket-ssl-perl' },
59 60
  { name => "Net::LDAP",                           url => "http://search.cpan.org/~gbarr/",     debian => 'libnet-ldap-perl' },
60 61
  # Net::SMTP is core since 5.7.3
doc/UPGRADE
7 7
Upgrade auf v?????
8 8
==================
9 9

  
10
* Neue Perl-Modul-Abhängigkeiten:
11

  
12
  * PBKDF2::Tiny
13

  
14
  Wie immer bitte vor dem ersten Aufrufen einmal die Pakete überprüfen:
15

  
16
  $ scripts/installation_check.pl -ro
17

  
10 18
* Der in der Dokumentation beschriebene Mechanismus für die CGI-Anbindung
11 19
  (2.6.1 Grundkonfiguration mittels CGI) wurde geändert. Ein einfacher Alias
12 20
  auf das Programmverzeichnis funktioniert nicht mehr, und es muss immer ein
doc/changelog
55 55

  
56 56
  - Besseren kivi-Adventssupport
57 57

  
58
Sicherheit:
59

  
60
  - Das sichere Passwort-Hash-Verfahren PBKDF2 wird nun unterstützt
61
    und standardmäßig bei allen zukünftigen Passwortänderungen
62
    benutzt.
63

  
58 64
2015-08-20 - Release 3.3
59 65

  
60 66
Größere neue Features:
modules/fallback/PBKDF2/Tiny.pm
1
use strict;
2
use warnings;
3

  
4
package PBKDF2::Tiny;
5
# ABSTRACT: Minimalist PBKDF2 (RFC 2898) with HMAC-SHA1 or HMAC-SHA2
6

  
7
our $VERSION = '0.005';
8

  
9
use Carp ();
10
use Exporter 5.57 qw/import/;
11

  
12
our @EXPORT_OK = qw/derive derive_hex verify verify_hex hmac digest_fcn/;
13

  
14
my ( $BACKEND, $LOAD_ERR );
15
for my $mod (qw/Digest::SHA Digest::SHA::PurePerl/) {
16
    $BACKEND = $mod, last if eval "require $mod; 1";
17
    $LOAD_ERR ||= $@;
18
}
19
die $LOAD_ERR if !$BACKEND;
20

  
21
#--------------------------------------------------------------------------#
22
# constants and lookup tables
23
#--------------------------------------------------------------------------#
24

  
25
# function coderef placeholder, block size in bytes, digest size in bytes
26
my %DIGEST_TYPES = (
27
    'SHA-1'   => [ undef, 64,  20 ],
28
    'SHA-224' => [ undef, 64,  28 ],
29
    'SHA-256' => [ undef, 64,  32 ],
30
    'SHA-384' => [ undef, 128, 48 ],
31
    'SHA-512' => [ undef, 128, 64 ],
32
);
33

  
34
for my $type ( keys %DIGEST_TYPES ) {
35
    no strict 'refs';
36
    ( my $name = lc $type ) =~ s{-}{};
37
    $DIGEST_TYPES{$type}[0] = \&{"$BACKEND\::$name"};
38
}
39

  
40
my %INT = map { $_ => pack( "N", $_ ) } 1 .. 16;
41

  
42
#--------------------------------------------------------------------------#
43
# public functions
44
#--------------------------------------------------------------------------#
45

  
46
#pod =func derive
47
#pod
48
#pod     $dk = derive( $type, $password, $salt, $iterations, $dk_length )
49
#pod
50
#pod The C<derive> function outputs a binary string with the derived key.
51
#pod The first argument indicates the digest function to use.  It must be one
52
#pod of: SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512.
53
#pod
54
#pod If a password or salt are not provided, they default to the empty string, so
55
#pod don't do that!  L<RFC 2898
56
#pod recommends|https://tools.ietf.org/html/rfc2898#section-4.1> a random salt of at
57
#pod least 8 octets.  If you need a cryptographically strong salt, consider
58
#pod L<Crypt::URandom>.
59
#pod
60
#pod The password and salt should encoded as octet strings. If not (i.e. if
61
#pod Perl's internal 'UTF8' flag is on), then an exception will be thrown.
62
#pod
63
#pod The number of iterations defaults to 1000 if not provided.  If the derived
64
#pod key length is not provided, it defaults to the output size of the digest
65
#pod function.
66
#pod
67
#pod =cut
68

  
69
sub derive {
70
    my ( $type, $passwd, $salt, $iterations, $dk_length ) = @_;
71

  
72
    my ( $digester, $block_size, $digest_length ) = digest_fcn($type);
73

  
74
    $passwd = '' unless defined $passwd;
75
    $salt   = '' unless defined $salt;
76
    $iterations ||= 1000;
77
    $dk_length  ||= $digest_length;
78

  
79
    # we insist on octet strings for password and salt
80
    Carp::croak("password must be an octet string, not a character string")
81
      if utf8::is_utf8($passwd);
82
    Carp::croak("salt must be an octet string, not a character string")
83
      if utf8::is_utf8($salt);
84

  
85
    my $key = ( length($passwd) > $block_size ) ? $digester->($passwd) : $passwd;
86
    my $passes = int( $dk_length / $digest_length );
87
    $passes++ if $dk_length % $digest_length; # need part of an extra pass
88

  
89
    my $dk = "";
90
    for my $i ( 1 .. $passes ) {
91
        $INT{$i} ||= pack( "N", $i );
92
        my $digest = my $result =
93
          "" . hmac( $salt . $INT{$i}, $key, $digester, $block_size );
94
        for my $iter ( 2 .. $iterations ) {
95
            $digest = hmac( $digest, $key, $digester, $block_size );
96
            $result ^= $digest;
97
        }
98
        $dk .= $result;
99
    }
100

  
101
    return substr( $dk, 0, $dk_length );
102
}
103

  
104
#pod =func derive_hex
105
#pod
106
#pod Works just like L</derive> but outputs a hex string.
107
#pod
108
#pod =cut
109

  
110
sub derive_hex { unpack( "H*", &derive ) }
111

  
112
#pod =func verify
113
#pod
114
#pod     $bool = verify( $dk, $type, $password, $salt, $iterations, $dk_length );
115
#pod
116
#pod The C<verify> function checks that a given derived key (in binary form) matches
117
#pod the password and other parameters provided using a constant-time comparison
118
#pod function.
119
#pod
120
#pod The first parameter is the derived key to check.  The remaining parameters
121
#pod are the same as for L</derive>.
122
#pod
123
#pod =cut
124

  
125
sub verify {
126
    my ( $dk1, @derive_args ) = @_;
127

  
128
    my $dk2 = derive(@derive_args);
129

  
130
    # shortcut if input dk is the wrong length entirely; this is not
131
    # constant time, but this doesn't really give much away as
132
    # the keys are of different types anyway
133

  
134
    return unless length($dk1) == length($dk2);
135

  
136
    # if lengths match, do constant time comparison to avoid timing attacks
137
    my $match = 1;
138
    for my $i ( 0 .. length($dk1) - 1 ) {
139
        $match &= ( substr( $dk1, $i, 1 ) eq substr( $dk2, $i, 1 ) ) ? 1 : 0;
140
    }
141

  
142
    return $match;
143
}
144

  
145
#pod =func verify_hex
146
#pod
147
#pod Works just like L</verify> but the derived key must be a hex string (without a
148
#pod leading "0x").
149
#pod
150
#pod =cut
151

  
152
sub verify_hex {
153
    my $dk = pack( "H*", shift );
154
    return verify( $dk, @_ );
155
}
156

  
157
#pod =func digest_fcn
158
#pod
159
#pod     ($fcn, $block_size, $digest_length) = digest_fcn('SHA-1');
160
#pod     $digest = $fcn->($data);
161
#pod
162
#pod This function is used internally by PBKDF2::Tiny, but made available in case
163
#pod it's useful to someone.
164
#pod
165
#pod Given one of the valid digest types, it returns a function reference that
166
#pod digests a string of data. It also returns block size and digest length for that
167
#pod digest type.
168
#pod
169
#pod =cut
170

  
171
sub digest_fcn {
172
    my ($type) = @_;
173

  
174
    Carp::croak("Digest function '$type' not supported")
175
      unless exists $DIGEST_TYPES{$type};
176

  
177
    return @{ $DIGEST_TYPES{$type} };
178
}
179

  
180
#pod =func hmac
181
#pod
182
#pod     $key = $digest_fcn->($key) if length($key) > $block_size;
183
#pod     $hmac = hmac( $data, $key, $digest_fcn, $block_size );
184
#pod
185
#pod This function is used internally by PBKDF2::Tiny, but made available in case
186
#pod it's useful to someone.
187
#pod
188
#pod The first two arguments are the data and key inputs to the HMAC function.  Both
189
#pod should be encoded as octet strings, as underlying HMAC/digest functions may
190
#pod croak or may give unexpected results if Perl's internal UTF-8 flag is on.
191
#pod
192
#pod B<Note>: if the key is longer than the digest block size, it must be
193
#pod preprocessed using the digesting function.
194
#pod
195
#pod The third and fourth arguments must be a digesting code reference (from
196
#pod L</digest_fcn>) and block size.
197
#pod
198
#pod =cut
199

  
200
# hmac function adapted from Digest::HMAC by Graham Barr and Gisle Aas.
201
# Compared to that implementation, this *requires* a preprocessed
202
# key and block size, which makes iterative hmac slightly more efficient.
203
sub hmac {
204
    my ( $data, $key, $digest_func, $block_size ) = @_;
205

  
206
    my $k_ipad = $key ^ ( chr(0x36) x $block_size );
207
    my $k_opad = $key ^ ( chr(0x5c) x $block_size );
208

  
209
    &$digest_func( $k_opad, &$digest_func( $k_ipad, $data ) );
210
}
211

  
212
1;
213

  
214

  
215
# vim: ts=4 sts=4 sw=4 et:
216

  
217
__END__
218

  
219
=pod
220

  
221
=encoding UTF-8
222

  
223
=head1 NAME
224

  
225
PBKDF2::Tiny - Minimalist PBKDF2 (RFC 2898) with HMAC-SHA1 or HMAC-SHA2
226

  
227
=head1 VERSION
228

  
229
version 0.005
230

  
231
=head1 SYNOPSIS
232

  
233
    use PBKDF2::Tiny qw/derive verify/;
234

  
235
    my $dk = derive( 'SHA-1', $pass, $salt, $iters );
236

  
237
    if ( verify( $dk, 'SHA-1', $pass, $salt, $iters ) ) {
238
        # password is correct
239
    }
240

  
241
=head1 DESCRIPTION
242

  
243
This module provides an L<RFC 2898|https://tools.ietf.org/html/rfc2898>
244
compliant PBKDF2 implementation using HMAC-SHA1 or HMAC-SHA2 in under 100 lines
245
of code.  If you are using Perl 5.10 or later, it uses only core Perl modules.
246
If you are on an earlier version of Perl, you need L<Digest::SHA> or
247
L<Digest::SHA::PurePerl>.
248

  
249
All documented functions are optionally exported.  No functions are exported by default.
250

  
251
=head1 FUNCTIONS
252

  
253
=head2 derive
254

  
255
    $dk = derive( $type, $password, $salt, $iterations, $dk_length )
256

  
257
The C<derive> function outputs a binary string with the derived key.
258
The first argument indicates the digest function to use.  It must be one
259
of: SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512.
260

  
261
If a password or salt are not provided, they default to the empty string, so
262
don't do that!  L<RFC 2898
263
recommends|https://tools.ietf.org/html/rfc2898#section-4.1> a random salt of at
264
least 8 octets.  If you need a cryptographically strong salt, consider
265
L<Crypt::URandom>.
266

  
267
The password and salt should encoded as octet strings. If not (i.e. if
268
Perl's internal 'UTF8' flag is on), then an exception will be thrown.
269

  
270
The number of iterations defaults to 1000 if not provided.  If the derived
271
key length is not provided, it defaults to the output size of the digest
272
function.
273

  
274
=head2 derive_hex
275

  
276
Works just like L</derive> but outputs a hex string.
277

  
278
=head2 verify
279

  
280
    $bool = verify( $dk, $type, $password, $salt, $iterations, $dk_length );
281

  
282
The C<verify> function checks that a given derived key (in binary form) matches
283
the password and other parameters provided using a constant-time comparison
284
function.
285

  
286
The first parameter is the derived key to check.  The remaining parameters
287
are the same as for L</derive>.
288

  
289
=head2 verify_hex
290

  
291
Works just like L</verify> but the derived key must be a hex string (without a
292
leading "0x").
293

  
294
=head2 digest_fcn
295

  
296
    ($fcn, $block_size, $digest_length) = digest_fcn('SHA-1');
297
    $digest = $fcn->($data);
298

  
299
This function is used internally by PBKDF2::Tiny, but made available in case
300
it's useful to someone.
301

  
302
Given one of the valid digest types, it returns a function reference that
303
digests a string of data. It also returns block size and digest length for that
304
digest type.
305

  
306
=head2 hmac
307

  
308
    $key = $digest_fcn->($key) if length($key) > $block_size;
309
    $hmac = hmac( $data, $key, $digest_fcn, $block_size );
310

  
311
This function is used internally by PBKDF2::Tiny, but made available in case
312
it's useful to someone.
313

  
314
The first two arguments are the data and key inputs to the HMAC function.  Both
315
should be encoded as octet strings, as underlying HMAC/digest functions may
316
croak or may give unexpected results if Perl's internal UTF-8 flag is on.
317

  
318
B<Note>: if the key is longer than the digest block size, it must be
319
preprocessed using the digesting function.
320

  
321
The third and fourth arguments must be a digesting code reference (from
322
L</digest_fcn>) and block size.
323

  
324
=begin Pod::Coverage
325

  
326

  
327

  
328

  
329
=end Pod::Coverage
330

  
331
=head1 SEE ALSO
332

  
333
=over 4
334

  
335
=item *
336

  
337
L<Crypt::PBKDF2>
338

  
339
=item *
340

  
341
L<Digest::PBDKF2>
342

  
343
=back
344

  
345
=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
346

  
347
=head1 SUPPORT
348

  
349
=head2 Bugs / Feature Requests
350

  
351
Please report any bugs or feature requests through the issue tracker
352
at L<https://github.com/dagolden/PBKDF2-Tiny/issues>.
353
You will be notified automatically of any progress on your issue.
354

  
355
=head2 Source Code
356

  
357
This is open source software.  The code repository is available for
358
public review and contribution under the terms of the license.
359

  
360
L<https://github.com/dagolden/PBKDF2-Tiny>
361

  
362
  git clone https://github.com/dagolden/PBKDF2-Tiny.git
363

  
364
=head1 AUTHOR
365

  
366
David Golden <dagolden@cpan.org>
367

  
368
=head1 COPYRIGHT AND LICENSE
369

  
370
This software is Copyright (c) 2014 by David Golden.
371

  
372
This is free software, licensed under:
373

  
374
  The Apache License, Version 2.0, January 2004
375

  
376
=cut

Auch abrufbar als: Unified diff