diff --git a/README.md b/README.md
index 6a4d5d34985171a3e4995de6718d2c3d4ff525f2..78616d91bf808211c18b08f23f717826068e39d7 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ All of the servers here are based on node.js, and some number of 3rd party node
 * cookie-sessions (>= 0.0.2)
 * nodemailer (>= 0.1.18)
 * emavows (>= 0.5.8)
+* bcrypt (>= 0.2.3)
 
 ## Getting started:
 
diff --git a/browserid/lib/db.js b/browserid/lib/db.js
index c300d40fad83be3875be78c344102322380a6e02..84bf966592eebf29b9367c634779b57930a83432 100644
--- a/browserid/lib/db.js
+++ b/browserid/lib/db.js
@@ -1,5 +1,6 @@
 const sqlite = require('sqlite'),
-        path = require('path');
+      path = require('path'),
+      bcrypt = require('bcrypt');
 
 var VAR_DIR = path.join(path.dirname(__dirname), "var");
 
@@ -106,7 +107,7 @@ exports.isStaged = function(email) {
 function generateSecret() {
   var str = "";
   const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-  for (var i=0; i < 32; i++) {
+  for (var i=0; i < 48; i++) {
     str += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
   }
   return str;
@@ -168,16 +169,18 @@ exports.addKeyToEmail = function(existing_email, email, pubkey, cb) {
   });
 }
 
-/* takes an argument object including email, pass, and pubkey. */
+/* takes an argument object including email, password hash, and pubkey. */
 exports.stageUser = function(obj) {
   var secret = generateSecret();
+
   // overwrite previously staged users
   g_staged[secret] = {
     type: "add_account",
     email: obj.email,
     pubkey: obj.pubkey,
-    pass: obj.pass
+    pass: obj.hash
   };
+
   g_stagedEmails[obj.email] = secret;
   return secret;
 };
@@ -241,15 +244,23 @@ exports.gotVerificationSecret = function(secret, cb) {
   }
 };
 
-/* takes an argument object including email, pass, and pubkey. */
 exports.checkAuth = function(email, pass, cb) {
-  db.execute("SELECT users.id FROM emails, users WHERE users.id = emails.user AND emails.address = ? AND users.password = ?",
-             [ email, pass ],
+  db.execute("SELECT users.password FROM emails, users WHERE users.id = emails.user AND emails.address = ?",
+             [ email ],
+             function (error, rows) {
+               cb(rows.length === 1 && bcrypt.compare_sync(pass, rows[0].password));
+             });
+};
+
+exports.checkAuthHash = function(email, hash, cb) {
+  db.execute("SELECT users.password FROM emails, users WHERE users.id = emails.user AND emails.address = ? AND users.password = ?",
+             [ email, hash ],
              function (error, rows) {
                cb(rows.length === 1);
              });
 };
 
+
 /* a high level operation that attempts to sync a client's view with that of the
  * server.  email is the identity of the authenticated channel with the user,
  * identities is a map of email -> pubkey.
diff --git a/browserid/lib/wsapi.js b/browserid/lib/wsapi.js
index b6c42e47ba0ba7fde12e57308b616966745902bd..f76acfdf374275bbfb7c030442e5de113011c767 100644
--- a/browserid/lib/wsapi.js
+++ b/browserid/lib/wsapi.js
@@ -5,15 +5,7 @@ const db = require('./db.js'),
       url = require('url'),
       httputils = require('./httputils.js');
       email = require('./email.js'),
-      crypto = require('crypto');
-
-// md5 is used to obfuscate passwords simply so we don't store
-// users passwords in plaintext anywhere
-function obfuscatePassword(pass) {
-  var hash = crypto.createHash('sha256');
-  hash.update(pass);
-  return hash.digest('base64');
-}
+      bcrypt = require('bcrypt');
 
 function checkParams(getArgs, resp, params) {
   try {
@@ -63,7 +55,8 @@ exports.stage_user = function(req, resp) {
     return;
   }
 
-  getArgs.pass = obfuscatePassword(getArgs.pass);
+  // bcrypt the password
+  getArgs.hash = bcrypt.encrypt_sync(getArgs.pass, bcrypt.gen_salt_sync(4))
 
   try {
     // upon success, stage_user returns a secret (that'll get baked into a url
@@ -76,9 +69,12 @@ exports.stage_user = function(req, resp) {
     // store inside the session the details of this pending verification
     req.session.pendingVerification = {
       email: getArgs.email,
-      pass: getArgs.pass // that's an obfuscated password now stored in encrypted session data.
-                         // we must store both email and password to handle the case where
-                         // a user re-creates an account
+      hash: getArgs.hash // we must store both email and password to handle the case where
+                         // a user re-creates an account - specifically, registration status
+                         // must ensure the new credentials work to properly verify that
+                         // the user has clicked throught the email link. note, this salted, bcrypted
+                         // representation of a user's password will get thrust into an encrypted cookie
+                         // served over an encrypted (SSL) session.  guten, yah.
     };
 
     httputils.jsonResponse(resp, true);
@@ -128,7 +124,7 @@ exports.registration_status = function(req, resp) {
     // session are good yet.
 
     var v = req.session.pendingVerification;
-    db.checkAuth(v.email, v.pass, function(authed) {
+    db.checkAuthHash(v.email, v.hash, function(authed) {
       if (authed) {
         delete req.session.pendingVerification;
         req.session.authenticatedUser = v.email;
@@ -146,8 +142,6 @@ exports.authenticate_user = function(req, resp) {
 
   if (!checkParams(getArgs, resp, [ "email", "pass" ])) return;
 
-  getArgs.pass = obfuscatePassword(getArgs.pass);
-
   db.checkAuth(getArgs.email, getArgs.pass, function(rv) {
     if (rv) {
       if (!req.session) req.session = {};