webシステムで認証するときのパスワードの扱い方


事の発端

webシステム初心者として認証するときにパスワードをどのように扱えばいいかよくわからない。
とりあえず以下のことに気をつけたい。

  • frontend → backend へusername、passwordをPOSTする際に、平文は避けたい
  • DBに登録しておくパスワードは平文で保存したくない
  • DBに登録したパスワードは、簡単に復号化させたくない

構成

  • frontendはjavascript/typescript でcrypto が使えれば何でも良いはず
  • backendも同様にjavascript/typescript でcrypto、bcrypt が使えること。expressを想定しています
  • 認証するのは独自のユーザ情報が格納されたDB内のデータで認証

どうやって暗号化・復号化するかしてDBに格納するか

こちらの記事を参考にさせていただきました。
bcryptに関してはこちらの記事を参考にされていただきました。

流れはこんな感じ。

とりあえず、frontend、backendの両方で以下のような関数とパスフレーズを保持。

import crypto from 'crypto';

const passphrase = 'hogehoge';
const algorithm = 'aes-256-ctr';

function encrypt(pass: string): string {
  const cipher = crypto.createCipher(algorithm, passphrase);
  let crypted = cipher.update(pass, 'utf8', 'base64');
  crypted += cipher.final('base64');
  return crypted;
}

function decrypt(decryped_pass: string): string {
  const decipher = crypto.createDecipher(algorithm, passphrase);
  let dec = decipher.update(decryped_pass, 'base64', 'utf8');
  dec += decipher.final('utf8');
return dec;

hash化は以下の処理で生成。

import bcrypt from 'bcrypt';

let hashed_password = bcrypt.hashSync(password, 10);

どうやって認証させるか

流れはこんな感じ。

まずはDBに保存するためのhash化。ユーザ登録時にはこのhash化した値をパスワードとして保存。

entity はちょっと省きますが、こんな感じでusernameに紐づくデータを
検索して、見つかったらパスワードを復号化し、hash値と比較。

import bcrypt from 'bcrypt';
import { createConnection, getRepository } from "typeorm";
import { User } from "../entity/User";

const user = getRepository(User).findOne({username: username});
if (user) {
  const dec = decrypt(req.body.password);
  if (bcrypt.compareSync(dec, hash_password)) {
    res.sendStatus(200);
  } 
}

res.sendStatus(401);

とりあえず、こんな感じでやれば目的は達成できてるんじゃないかな。。。
ここまでお付き合いしていただきありがとうございます。