File Coverage

blib/lib/Dancer2/Plugin/Passphrase.pm
Criterion Covered Total %
statement 18 18 100.0
branch n/a
condition n/a
subroutine 6 6 100.0
pod 1 1 100.0
total 25 25 100.0


line stmt bran cond sub pod time code
1              
2             use strict;
3 10     10   6059039 use warnings;
  10         82  
  10         235  
4 10     10   88 use Dancer2::Plugin::Passphrase::Core;
  10         16  
  10         278  
5 10     10   2985 use Dancer2::Plugin::Passphrase::Hashed;
  10         29  
  10         298  
6 10     10   3640 use Dancer2::Plugin;
  10         23  
  10         228  
7 10     10   3709  
  10         289500  
  10         79  
8             our $VERSION = '3.4.1';
9              
10             plugin_keywords 'passphrase';
11              
12             # ABSTRACT: Passphrases and Passwords as objects for Dancer2
13              
14             =head1 NAME
15              
16             Dancer2::Plugin::Passphrase - Passphrases and Passwords as objects for Dancer2
17              
18             =head1 DEPRECATION WARNING
19              
20             B<NOTE:> This module is DEPRECATED, and it is advised that you switch
21             to L<Dancer2::Plugin::CryptPassphrase> if possible.
22              
23             =head1 SYNOPSIS
24              
25             This plugin manages the hashing of passwords for Dancer2 apps, allowing
26             developers to follow cryptography best practices without having to
27             become a cryptography expert.
28              
29             It uses the bcrypt algorithm as the default, while also supporting any
30             hashing function provided by L<Digest>.
31              
32             =head1 USAGE
33              
34             package MyWebService;
35             use Dancer2;
36             use Dancer2::Plugin::Passphrase;
37              
38             post '/login' => sub {
39             my $phrase = passphrase( param('my password') )->generate;
40              
41             # $phrase is now an object that contains RFC 2307 representation
42             # of the hashed passphrase, along with the salt, and other metadata
43            
44             # You should store $phrase->rfc2307() for use later
45             };
46              
47             get '/protected' => sub {
48             # Retrieve $stored_rfc_2307_string, like we created above.
49             # IT MUST be a valid RFC 2307 string
50              
51             if ( passphrase( param('my password') )->matches( $stored_rfc_2307 ) ) {
52             # Passphrase matches!
53             }
54             };
55              
56             get '/generate_new_password' => sub {
57             return passphrase->generate_random;
58             };
59              
60             =head1 NOTE
61              
62             This package does no checking about how secure the password is,
63             minimum length or anything, including a length of 0 being valid.
64             You can add extra checks in your "MyWebService".
65              
66             =head1 AUTO STRINGIFICATION IS REMOVED
67              
68             You must use $phrase->rfc2307() to get a text string.
69              
70             =head1 KEYWORDS
71              
72             =head2 passphrase
73              
74             Given a plaintext password, it returns a Dancer2::Plugin::Passphrase::Core
75             object that you can generate a new hash from, or match against a stored hash.
76              
77             =cut
78              
79             has algorithm => (
80             is => 'ro',
81             from_config => sub { 'Bcrypt' },
82             );
83              
84             my ($plugin, $plaintext) = @_;
85              
86 98     98 1 29105 return Dancer2::Plugin::Passphrase::Core->new(
87             %{$plugin->config},
88             algorithm => $plugin->algorithm,
89 98         178 plaintext => $plaintext,
  98         2274  
90             );
91             }
92              
93             1;
94              
95              
96             =head1 MAIN METHODS
97              
98             =head2 generate
99              
100             Generates an RFC 2307 representation of the hashed passphrase
101             that is suitable for storage in a database.
102              
103             my $phrase = passphrase('my passphrase')->generate;
104              
105             It returns a Dancer2::Plugin::Passphrase::Hashed object.
106              
107             You should store C<< $phrase->rfc_2307() >> in your database.
108              
109             Accepts a hashref of options to specify what kind of hash should be
110             generated. All options settable in the config file are valid.
111              
112             If you specify only the algorithm, the default settings for that algorithm will be used.
113              
114             A cryptographically random salt is used if salt is not defined.
115             Only if you specify the empty string will an empty salt be used.
116             This is not recommended, and should only be used to upgrade old insecure hashes.
117              
118             my $phrase = passphrase('my password')->generate(
119             {
120             algorithm => $algo_name, # override algo from config
121             $algo_name => {
122             # override options for this algorithm
123             $opt1 => $value1,
124             },
125             }
126             );
127              
128             So for Bcrypt this might be:
129              
130              
131             my $phrase = passphrase('my password')->generate(
132             {
133             algorithm => 'Bcrypt',
134             Bcrypt => {
135             cost => 14,
136             }
137             }
138             );
139              
140             =head2 matches
141              
142             Matches a plaintext password against a stored hash.
143             Returns 1 if the hash of the password matches the stored hash.
144             Returns undef if they don't match or if there was an error
145             Fail-Secure, rather than Fail-Safe.
146              
147             passphrase('my password')->matches($stored_rfc_2307_string);
148              
149             $stored_rfc_2307_string B<MUST> be a valid RFC 2307 string,
150             as created by L<generate()|/"passphrase__generate">
151              
152             An RFC 2307 string is made up of a scheme identifier, followed by a
153             base64 encoded string. The base64 encoded string should contain
154             the password hash and the salt concatenated together - in that order.
155              
156             '{'.$scheme.'}'.encode_base64($hash . $salt, '');
157              
158             Where C<$scheme> can be any of the following and their unsalted variants,
159             which have the leading S removed. CRYPT will be Bcrypt.
160              
161             SMD5 SSHA SSHA224 SSHA256 SSHA384 SSHA512 CRYPT
162              
163             A complete RFC2307 string looks like this:
164              
165             {SSHA}K3LAbIjRL5CpLzOlm3/HzS3qt/hUaGVTYWx0
166              
167             This is the format created by L<generate()|/"passphrase__generate">
168              
169             =head2 generate_random
170              
171             Generates and returns any number of cryptographically random
172             characters from the url-safe base64 character set.
173              
174             my $rand_pass = passphrase->generate_random;
175              
176             The passwords generated are suitable for use as
177             temporary passwords or one-time authentication tokens.
178              
179             You can configure the length and the character set
180             used by passing a hashref of options.
181              
182             my $rand_pass = passphrase->generate_random({
183             length => 32,
184             charset => ['a'..'z', 'A'..'Z'],
185             });
186              
187             =head1 ADDITIONAL METHODS
188              
189             The methods are only applicable once you have called C<generate>
190              
191             passphrase( 'my password' )->generate->rfc2307; # CORRECT
192              
193             passphrase( 'my password' )->rfc2307; # INCORRECT, Returns undef
194              
195              
196             =head2 rfc2307
197              
198             Returns the rfc2307 representation from a C<Dancer2::Plugin::Passphrase> object.
199              
200             passphrase('my password')->generate->rfc2307;
201              
202             =head2 scheme
203              
204             Returns the scheme name from a C<Dancer2::Plugin::Passphrase> object.
205              
206             This is the scheme name as used in the RFC 2307 representation
207              
208             passphrase('my password')->generate->scheme;
209              
210             The scheme name can be any of the following, and will always be capitalized
211              
212             SMD5 SSHA SSHA224 SSHA256 SSHA384 SSHA512 CRYPT
213             MD5 SHA SHA224 SHA256 SHA384 SHA512
214              
215             =head2 algorithm
216              
217             Returns the algorithm name from a C<Dancer2::Plugin::Passphrase> object.
218              
219             The algorithm name can be anything that is accepted by C<< Digest->new($alg) >>
220             This includes any modules in the C<Digest::> Namespace
221              
222             passphrase('my password')->generate->algorithm;
223              
224             =head2 cost
225              
226             Returns the bcrypt cost from a C<Dancer2::Plugin::Passphrase> object.
227             Only works when using the bcrypt algorithm, returns undef for other algorithms
228              
229             passphrase('my password')->generate->cost;
230              
231             =head2 salt_raw
232              
233             Returns the raw salt from a C<Dancer2::Plugin::Passphrase> object.
234              
235             passphrase('my password')->generate->salt_raw;
236              
237             Can be defined, but false - The empty string is technically a valid salt.
238              
239             Returns C<undef> if there is no salt.
240              
241             =head2 hash_raw
242              
243             Returns the raw hash from a C<Dancer2::Plugin::Passphrase> object.
244              
245             passphrase('my password')->generate->hash_raw;
246              
247             =head2 salt_hex
248              
249             Returns the hex-encoded salt from a C<Dancer2::Plugin::Passphrase> object.
250              
251             Can be defined, but false - The empty string is technically a valid salt.
252             Returns C<undef> if there is no salt.
253              
254             passphrase('my password')->generate->salt_hex;
255              
256             =head2 hash_hex
257              
258             Returns the hex-encoded hash from a C<Dancer2::Plugin::Passphrase> object.
259              
260             passphrase('my password')->generate->hash_hex;
261              
262             =head2 salt_base64
263              
264             Returns the base64 encoded salt from a C<Dancer2::Plugin::Passphrase> object.
265              
266             Can be defined, but false - The empty string is technically a valid salt.
267             Returns C<undef> if there is no salt.
268              
269             passphrase('my password')->generate->salt_base64;
270              
271             =head2 hash_base64
272              
273             Returns the base64 encoded hash from a C<Dancer2::Plugin::Passphrase> object.
274              
275             passphrase('my password')->generate->hash_base64;
276              
277             =head2 plaintext
278              
279             Returns the plaintext password as originally supplied to the L<passphrase> keyword.
280              
281             passphrase('my password')->generate->plaintext;
282              
283              
284             =head1 MORE INFORMATION
285              
286             =head2 Purpose
287              
288             The aim of this module is to help you store new passwords in a secure manner,
289             whilst still being able to verify and upgrade older passwords.
290              
291             Cryptography is a vast and complex field. Many people try to roll their own
292             methods for securing user data, but succeed only in coming up with
293             a system that has little real security.
294              
295             This plugin provides a simple way of managing that complexity, allowing
296             developers to follow crypto best practice without having to become an expert.
297              
298              
299             =head2 Rationale
300              
301             The module defaults to hashing passwords using the bcrypt algorithm, returning them
302             in RFC 2307 format.
303              
304             RFC 2307 describes an encoding system for passphrase hashes, as used in the "userPassword"
305             attribute in LDAP databases. It encodes hashes as ASCII text, and supports several
306             passphrase schemes by starting the encoding with an alphanumeric scheme identifier enclosed
307             in braces.
308              
309             RFC 2307 only specifies the C<MD5>, and C<SHA> schemes - however in real-world usage,
310             schemes that are salted are widely supported, and are thus provided by this module.
311              
312             Bcrypt is an adaptive hashing algorithm that is designed to resist brute
313             force attacks by including a cost (aka work factor). This cost increases
314             the computational effort it takes to compute the hash.
315              
316             SHA and MD5 are designed to be fast, and modern machines compute a billion
317             hashes a second. With computers getting faster every day, brute forcing
318             SHA hashes is a very real problem that cannot be easily solved.
319              
320             Increasing the cost of generating a bcrypt hash is a trivial way to make
321             brute forcing ineffective. With a low cost setting, bcrypt is just as secure
322             as a more traditional SHA+salt scheme, and just as fast. Increasing the cost
323             as computers become more powerful keeps you one step ahead
324              
325             For a more detailed description of why bcrypt is preferred, see this article:
326             L<http://codahale.com/how-to-safely-store-a-password/>
327              
328              
329             =head2 Configuration
330              
331             In your applications config file, you can set the default hashing algorithm,
332             and the default settings for every supported algorithm. Calls to
333             L<generate()|/"passphrase__generate"> will use the default settings
334             for that algorithm specified in here.
335              
336             You can override these defaults when you call L<generate()|/"passphrase__generate">.
337              
338             If you do no configuration at all, the default is to bcrypt with a cost of 4, and
339             a strong psuedo-random salt.
340              
341             plugins:
342             Passphrase:
343             algorithm: Bcrypt
344             cost: 8
345              
346              
347             =head2 Storage in a database
348              
349             You should be storing the RFC 2307 string in your database, it's the easiest way
350             to use this module. You could store the C<raw_salt>, C<raw_hash>, and C<scheme>
351             separately, but this strongly discouraged. RFC 2307 strings are specifically
352             designed for storing hashed passwords, and should be used wherever possible.
353              
354             The length of the string produced by L<generate()|/"passphrase__generate"> can
355             vary dependent on your settings. Below is a table of the lengths generated
356             using default settings.
357              
358             You will need to make sure your database columns are at least this long.
359             If the string gets truncated, the password can I<never> be validated.
360              
361             ALGORITHM LENGTH EXAMPLE RFC 2307 STRING
362            
363             Bcrypt 67 {CRYPT}$2a$04$MjkMhQxasFQod1qq56DXCOvWu6YTWk9X.EZGnmSSIbbtyEBIAixbS
364             SHA-512 117 {SSHA512}lZG4dZ5EU6dPEbJ1kBPPzEcupFloFSIJjiXCwMVxJXOy/x5qhBA5XH8FiUWj7u59onQxa97xYdqje/fwY5TDUcW1Urplf3KHMo9NO8KO47o=
365             SHA-384 97 {SSHA384}SqZF5YYyk4NdjIM8YgQVfRieXDxNG0dKH4XBcM40Eblm+ribCzdyf0JV7i2xJvVHZsFSQNcuZPKtiTMzDyOU+w==
366             SHA-256 73 {SSHA256}xsJHNzPlNCpOZ41OkTfQOU35ZY+nRyZFaM8lHg5U2pc0xT3DKNlGW2UTY0NPYsxU
367             SHA-224 69 {SSHA224}FTHNkvKOdyX1d6f45iKLVxpaXZiHel8pfilUT1dIZ5u+WIUyhDGxLnx72X0=
368             SHA-1 54 {SSHA}Qsaao/Xi/bYTRMQnpHuD3y5nj02wbdcw5Cek2y2nLs3pIlPh
369             MD5 50 {SMD5}bgfLiUQWgzUm36+nBhFx62bi0xdwTp+UpEeNKDxSLfM=
370              
371             =head2 Common Mistakes
372              
373             Common mistakes people make when creating their own solution. If any of these
374             seem familiar, you should probably be using this module
375              
376             =over
377              
378             =item Passwords are stored as plain text for a reason
379              
380             There is never a valid reason to store a password as plain text.
381             Passwords should be reset and not emailed to customers when they forget.
382             Support people should be able to login as a user without knowing the users password.
383             No-one except the user should know the password - that is the point of authentication.
384              
385             =item No-one will ever guess our super secret algorithm!
386              
387             Unless you're a cryptography expert with many years spent studying
388             super-complex maths, your algorithm is almost certainly not as secure
389             as you think. Just because it's hard for you to break doesn't mean
390             it's difficult for a computer.
391              
392             =item Our application-wide salt is "Sup3r_S3cret_L0ng_Word" - No-one will ever guess that.
393              
394             This is common misunderstanding of what a salt is meant to do. The purpose of a
395             salt is to make sure the same password doesn't always generate the same hash.
396             A fresh salt needs to be created each time you hash a password. It isn't meant
397             to be a secret key.
398              
399             =item We generate our random salt using C<rand>.
400              
401             C<rand> isn't actually random, it's a non-unform pseudo-random number generator,
402             and not suitable for cryptographic applications. Whilst this module also defaults to
403             a PRNG, it is better than the one provided by C<rand>. Using a true RNG is a config
404             option away, but is not the default as it it could potentially block output if the
405             system does not have enough entropy to generate a truly random number
406              
407             =item We use C<md5(pass.salt)>, and the salt is from C</dev/random>
408              
409             MD5 has been broken for many years. Commodity hardware can find a
410             hash collision in seconds, meaning an attacker can easily generate
411             the correct MD5 hash without using the correct password.
412              
413             =item We use C<sha(pass.salt)>, and the salt is from C</dev/random>
414              
415             SHA isn't quite as broken as MD5, but it shares the same theoretical
416             weaknesses. Even without hash collisions, it is vulnerable to brute forcing.
417             Modern hardware is so powerful it can try around a billion hashes a second.
418             That means every 7 character password in the range [A-Za-z0-9] can be cracked
419             in one hour on your average desktop computer.
420              
421             =item If the only way to break the hash is to brute-force it, it's secure enough
422              
423             It is unlikely that your database will be hacked and your hashes brute forced.
424             However, in the event that it does happen, or SHA512 is broken, using this module
425             gives you an easy way to change to a different algorithm, while still allowing
426             you to validate old passphrases
427              
428             =back
429              
430              
431             =head1 KNOWN ISSUES
432              
433             If you see errors like this
434              
435             Wide character in subroutine entry
436              
437             or
438              
439             Input must contain only octets
440              
441             The C<MD5>, C<bcrypt>, and C<SHA> algorithms can't handle characters with an ordinal
442             value above 255, producing errors like this if they encounter them.
443             It is not possible for this plugin to automagically work out the correct
444             encoding for a given string.
445              
446             If you see errors like this, then you probably need to use the L<Encode> module
447             to encode your text as UTF-8 (or whatever encoding it is) before giving it
448             to C<passphrase>.
449              
450             Text encoding is a bag of hurt, and errors like this are probably indicitive
451             of deeper problems within your app's code.
452              
453             You will save yourself a lot of trouble if you read up on the
454             L<Encode> module sooner rather than later.
455              
456             For further reading on UTF-8, unicode, and text encoding in perl,
457             see L<http://training.perl.com/OSCON2011/index.html>
458              
459              
460             =head1 SEE ALSO
461              
462             L<Dancer2>, L<Digest>, L<Crypt::Eksblowfish::Bcrypt>
463              
464              
465             =head1 ACKNOWLEDGMENTS
466              
467             =over
468              
469             =item James Aitken for his D1 version.
470              
471             =item Sawyer X for his D2 magic.
472              
473             =item Mohammad S Anwar (GH#4, typo fixes)
474              
475             =item Jim Davis (GH#5)
476              
477             =item Peter Mottram (GH#11)
478              
479             =item Nuno Carvalho (GH#12)
480              
481             =item Tom Adams (fix generate docs)
482              
483             =item Jeremi M. Gosney (GH #2)
484              
485             =item Sergiy Borodych (GH #3)
486              
487             =back
488              
489             =head1 COPYRIGHT AND LICENSE
490              
491             Copyright (c) 2016-2018 Peter Mottram <peter@sysnix.com>.
492             Copyright (c) 2016 Henk van Oers <hvo.pm@xs4all.nl>.
493             Copyright (c) 2012-2016 James Aitken.
494              
495             This is free software; you can redistribute it and/or modify it under
496             the same terms as the Perl 5 programming language system itself.
497              
498             =cut