Passport.js digest-strategy で パスワードを安全に管理する

泣きながらDigest認証を行うnode.jsサーバを構築していて詰まったところがあったのでメモ。

passport.jsのdigest認証は、request時に受け付けたユーザIDを引数にして関数を組み、 DBなりと照合して最後のコールバックでパスワードをDigest Strategyコンストラクターに返す必要があるんだけれども、 コールバックで受け付けるパスワードがみた感じ平文でしか受け付けない。

passport.use(new DigestStrategy({ qop: 'auth' },
  function(username, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }
      return done(null, user, user.password);   //Wow, we must use plaintext...
    });
  },
  function(params, done) {
    // validate nonces as necessary
    done(null, true)
  }
));

は?これくそすぎないっすか・・・

ソースコード書き換えるしかないかと思って元のコード読んでたら、こんな事が書いてあった。

this._secret(creds.username, function(err, user, password) {
    if (err) { return self.error(err); }
    if (!user) { return self.fail(self._challenge()); }

    var ha1;
    if (!creds.algorithm || creds.algorithm === 'MD5') {
      if (typeof password === 'object' && password.ha1) {
        ha1 = password.ha1;
      } else  {
        ha1 = md5(creds.username + ":" + creds.realm + ":" + password);
      }
    }else

137行目の部分、コールバックで返す際に ha1 というデータを持つObjectを返すと、 すでに{ユーザ名}:{realm}:{パスワード} をMD5ハッシュ化したものとして扱ってくれるみたいだ。 逆に単純なString形式の値を渡された場合、そのStringを使ってha1を作り出している。

(Digest認証で生成するハッシュのうちの一つですね)

料理できていればそのまま出すし、材料だけ渡されたら足りない部分を使って料理する感じ。 exampleにも書いておいて欲しかった・・・

ということで、 あらかじめデータベースには  上記のようなMD5ハッシュ値を保管しておき、Digest認証時に

    let md5HashedString = "a867af741df2e7004b62c10f38063db8";  //hash of "hoge:Users:hoge"; foo.ha1 = md5HashedString; return done(null, userId, foo.ha1); //callback to digestStrategy constructor.

で返してあげれば良さそうだ。

/以上