[ic] Proposed Patch for bcrypt Password Option
Mark Johnson
mark at endpoint.com
Thu Jun 12 18:40:10 UTC 2014
Attached is a proposed patch for introducing bcrypt as an encryption
option for passwords in Vend::UserDB. You can either skip right to
reviewing the patch or continue reading for a high-level synopsis.
Please provide feedback, regarding concerns or support. I'd like to get
this committed as soon as next week.
Thanks,
Mark
----------------------------------------------------
Synopsis:
* Full bcrypt support
+ Requires modules Digest::Bcrypt and Crypt::Random.
+ Pads out passwords to 72-character limit of bcrypt to increase
difficulty of brute-forcing weak passwords.
- Optional "pepper" (highly recommended) to make padding
pattern unique per catalog.
+ Defaults to cost of 13.
* Storage follows general guidelines of modular crypt format (MCF),
both weaning it from the length-based cipher identification, but also
allowing it to identify a "pre digest" against the password
(discussed below).
+ Example storage structure:
$2y$14$F4PQQ6QTuRFo0FBAYP1rhQIqJSTg7iHSS619fmiAOhvk5b5Ui8o6o
* Uses a "more complex than usual" approach to manage the identifier
than the standard MCF. This complexity is used to specify which
algorithm "pre digested" the raw password. They are as follows:
+ $2y$ - standard, default identifier. Means bcrypt processed the raw
password directly.
+ $2s$ - s => SHA1. Indicates bcrypt process first runs the raw
password through the SHA1 algorithm before encrypting. If you
update passwords originally stored as SHA1 as a background process,
the resulting bcrypt structures should all have this identifier.
- Example storage structure:
$2s$14$F4PQQ6QTuRFo0FBAYP1rhQIqJSTg7iHSS619fmiAOhvk5b5Ui8o6o
+ $2m$ - m => MD5. Same as $2s$ but for passwords that are originally
stored MD5.
- Example storage structure:
$2m$14$iJ7kMcGiNXRvBTRBIHVrmw1Rfq224SXd0QzSsKOupop4nZTVhEotA
+ $2n$..$ - n => md5_salted encryption algorithm. '..' are the 2 salt
characters in the original stored password, made available so that
the "pre digest" step can accurately reproduce the salted MD5
structure before bcrypting and comparing.
- Example storage structure:
$2n$jQ$14$MZjidwOjuROki9TXdJofsgp2ne2Vrm6JJtLcF+0f51mE1ncee0XZk
+ $2c$..$ - c => crypt(). Same as md5_salted, but with crypt()
instead.
- Example storage structure:
$2c$m4$14$QeCj3irfIJOWoWKHUtNpUQVxwXl8Sl4zRo79d7BRPQpDTSlaCTJv0
The "pre digested" feature allows a site developer to create a
background process for updating an existing user table with bcrypted
passwords even if the table is already encrypted by one of the
previously supported ciphers. Thus, in a matter of minutes to weeks
(depending on the size of your user table and chosen bcrypt cost)
your passwords can be fully upgraded to bcrypt without having to wait
on the organic process "promote" allows, or having to know any of your
users' original passwords.
* New routine construct_bcrypt() in Vend::UserDB. Takes a single hash
ref argument with keys "password", "type" (optional), and "profile"
(optional). Returns a properly-formatted bcrypt structure suitable
for being stored in the password field of the user table of interest.
Anticipated usage scenario would be for a developer with an already
encrypted user table (sha1, md5, md5_salted, or crypt) to create an
Interchange job that slurps in all the encrypted passwords, passes
them along with the type of encryption that created them (described
below), and gets in return the appropriate bcrypt structure
reflecting that original encryption type to write back to the user
table's password field.
+ If "type" is left off, assumes code is encrypting against the raw
password. Returns structure with identifier $2y$. Otherwise, "type"
is any of the supported Interchange encryption options:
- sha1 (identifier returned is $2s$)
- md5 (identifier returned is $2m$)
- md5_salted (identifier returned is $2n$..$)
- crypt (identifier returned is $2c$..$)
+ If "profile" is left off, uses "default" profile, which is
typically the definition for the userdb table. Common other profile
is "ui", which defines the access table for the admin.
Whatever profile is being used, it must be set to use bcrypt. If it's
set to anything other than bcrypt, the routine dies with an error.
+ Example usage: if my "ui" profile is configured with "crypt" (as it
is by default), I have crypt() passwords in the access table:
UserDB ui crypt 1
I first change and promote to bcrypt by replacing the above with:
UserDB ui promote 1
UserDB ui bcrypt 1
UserDB ui bcrypt_pepper {some reasonably long random string}
(remember to restart)
Then, rather than wait for every user to eventually log in, I run
all my crypt passwords through construct_bcrypt(). If I have
cWNLm21WqgOKU, e.g.:
my $bcrypt_password = Vend::UserDB::construct_bcrypt(
{
password => 'cWNLm21WqgOKU',
type => 'crypt',
profile => 'ui',
}
)
and $bcrypt_password now holds something like:
$2c$cW$14$QeCj3irfIJOWoWKHUtNpUQVxwXl8Sl4zRo79d7BRPQpDTSlaCTJv0
which can directly overwrite cWNLm21WqgOKU in the password field.
* "promote" flag has been expanded to recognize intra-bcrypt config
changes between the cost of a stored password and the current cost
being used for encryption. E.g., if the current cost setting for
bcrypt is 14, but the storage structure indicates $2y$13$..., promote
catches that and updates the password in the database to the
calculated structure for cost 14.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: bcrypt.diff
Type: text/x-patch
Size: 6814 bytes
Desc: not available
URL: <http://www.icdevgroup.org/pipermail/interchange-users/attachments/20140612/aa16936e/attachment.bin>
More information about the interchange-users
mailing list