Login機能の実装 Express-Session と パスワードのhash化

アプリケーションといえば、ログインをしなければ情報が見れなかったり、そもそも利用ができなくなっていたりします

また、ユーザーが切り替わることによって表示される情報を変更されるなど、アプリケーションで実現できる幅が広がります

ログイン機能がなければだれでも情報が見れるサイトとなるため使い道が限られてしまいます

プライベートな内容となるとやはり認証機能があることが前提となります

そこで今回はLoginとLogoutの機能を実装してみました

また、Loginの際に必要になるパスワードは実際のアプリケーションに実装する際には取り扱いを注意しなければいけません

あくまでその一例としてパスワードのhash化も併せて説明したいと思います

ということで今回の記事では

  • Express-Session  認証機能の実装に使用します
  • bcrypt 文字列(パスワード)のhash化に使用します

この二つは標準で実装されていないので事前にインストールが必要です

npm install –save express-session bcrypt

セッションとは何か  認証機能の実装

まずはログインと認証機能についてです

ログインの仕組み自体はそれほど難しくありません

あらかじめ登録しておいたアカウント名とパスワードが一致すればログインできるという機能を実装すればいいだけです

ただ、その場合、何かページ遷移を行うたびにログインをして認証をしなければなりません

要するに、ログインしたらログアウトするまでユーザー情報を持ち続けてアプリケーションを継続的に使用する(サーバーとやりとりをする)ことが必要になります

簡単にいうとこのログインからログアウトまでの一連のながれがセッションとなります

ユーザーはログインをするとセッションIDというのものを取得してcookieに保持します

そしてそれを使って認証機能を実装します

Expressでサーバーを構築した場合は

express-sessionというモジュールを使うことでセッション管理が可能です

ログイン画面のhtmlを付け加え、ログイン画面へのリンクとログアウトのボタンを付け加えるためにindex.htmlもかきかえました

Express-Sessionの設定について

app.jsのexpress-sessionの設定について簡単に説明します

この部分が設定になります

secret:デフォルトから書き換えをしておきましょう。 ここではmy secretとしていますが、実際にはわかりにくいキーにしましょう。

resave:false セッションストアに強制的に値を保存するためのものです。使用しないのでfalseを設定します。

saveUninitialized:false 初期化されていない値を強制的に保存します。こちらも使用しないのでfalseを設定します。

cookie:{maxage: クッキーの保管期間(ミリ秒)}ここでは1時間としています

cookieにもいくつかオプションがあります。

本番ではセキュリティを考えて設定を検討しましょう

パスワードのHash化

今回ログイン認証は、POSTメソッドでbodyの中に入れて送ってきたアカウントとパスワードについて、DBのアカウントの検索を検索し、そのパスワードが正しければログインできるようにします

ただし、ここで一つ確認しておきたいことがあります

それはパスワードのHash化についてです

Hash化というのは、ある文字列を、アルゴリズムにしたがって一方通行で変換をすることです

一方通行というのがポイントになります

Hash化を行うと、同じ文字列は必ず同じ文字列に変換されます

変化後の文字列をHash値といいます

同じ文字列からは同じHash値となるわけですが、先ほど述べた通り、この値は逆算して元の文字列にすることができません

例えば、割り算のあまりを考えるとわかりやすいと思います

ある数字を3で割った時の値がHash値だとかんがえてください

7÷3のあまりは1です

もとの値が7の場合は必ず1になります

しかし、あまりが1というだけでは元の数字には戻せないことはわかると思います

※もちろんこの場合、同じHash値になる元の数字がたくさんあるため実用的ではありません

もとには戻せませんが、同じ文字列からは必ず同じHash値になります

パスワードをDBに保存するときは、そのHash値を保存します

平文を保存しません

もし仮にDBからパスワードのHash値が他者に知られることがあったとしても、もとのパスワードへの変換は困難なため、それを利用して不正ログインをすることはできません

通常、このようにDBにはなるべく重要な情報を保存しないようにします

入門書だとなかなかそこまで書いてくれていなかったりします

ということで、ログインをする場合は、パスワードをHash化してDBのHash値と同じであればパスワードも同じだろうと考えてログインを許可するようにします

Hash化はbcryptをもちいます

56行目のlet hashedPassword =await bcrypt.hash(pass, 10)

の部分です

※Hash化には時間がかかるため、async awaitを用いています

また、Hash化だけでは総当たり攻撃をすれば時間の問題でHash値からパスワードにたどり着いてしまう可能性があります

そこで、さらにsaltと呼ばれるランダムな文字列をパスワードに加えてHash化し

Hash化の回数を重ねる(ストレッチング)ことで、もとの文字列にたどり着くことをさらに困難にするなど、総当たり攻撃に対する対処が必要になります

bcryptを用いることでそれらが実行されてHash値が出力されます

書き方 bcrypt.hash(元の文字列, ストレッチング回数) 

もちろんこれでOKというわけではありませんが、単純にHash化のみよりも元のパスワードを解読するのはより困難になります

当然、そもそもセキュリティの強化は必要になります

Hash値の検証について

ログインの際に元のパスワードから変換するHash値とDBの中のHash値が一致するかどうかを検証する必要があります

それを実施するのが

compareです

41行目のconst compared = await bcrypt.compare(pass, row.password);

の部分です

これによって送られてきたパスワードをHash化した値と、DBに保存されている値を検証します

その後、一致すればログインを実行し、一致しなければログインはできません

もちろんユーザーはそのことを意識する必要はありません

普通にアカウント名とパスワードをフォームに入力して送信するだけです

セッション管理について

ではログイン完了後の流れです

ログインができたら、セッションを開始します

express-sessionによって

セッションIDが発行されます

これは一意なIDです

これをcookieに入れてクライアントに戻します

クライアントはそれを受け取り、次サーバーとやり取りする際はそのcookieをヘッダーに入れてリクエストを送ることになります

また、セッションには変数を持たせることができますので

req.session.login = row.account; //アカウント名のみ保持

という形でloginにアカウント名だけ保持をさせています

なんらかのアカウント名を持っていればログインしているとみなしています

※req.session.loginがundefinedでなければログインしているとif文を用いて条件分岐をしています

※通常何も持たせないのですが、ログアウトを簡易にするため今回はこのようにしています

ログアウトはreq.session.login = undefinedと代入することで実装しています

難しい内容ですし、セキュリティのことはこれからもっと学んでいく必要があると感じています

正直このレベルでは本番には使えないレベルです

少しずつ勉強していきましょう

セキュリティについて考えるなら

ログインの機能についての実装は、ライブラリを用いるというのが現実的だと思います

ということで、今後Passport.jsを利用するログイン機能についても確認をしていきたいと思います

また、セキュリティについてどういった問題があるかということも勉強していきたいと思います

今回は以上です

YouTubeもよろしくお願いします!じゃっかんだらだらしゃべっているので必要なところだけ見てくださいね。

コメント

  1. マルコメ より:

    コメント失礼します
    プログラム参考にさせていただいたのですが、新規作成でデータベースにユーザーIDとパスワードを登録したいのですがどうすればいいかわかりません。もしよろしければ教えてください。お手数をおかけしますがよろしくお願いします。

    • Kikujiro より:

      53行目以降の
      //新規アカウント作成
      app.post(‘/users’,async (req, res) => {
      let pass = req.body.password;
      let hashedPassword =await bcrypt.hash(pass, 10)
      const db = new sqlite.Database(‘wishlist.db’);
      const account = req.body.account;
      db.serialize(() => {
      db.exec(`insert into users (account, password) values(“${account}”,”${hashedPassword}”)`, (stat, error) => {
      db.close();
      res.redirect(‘/’);
      })
      });
      })
      の内容がアカウント作成の部分になります。
      フロントエンドからPOSTメソッドでbodyにaccountとパスワードを送ってdbに対してinsertでアカウントの追加をしています。
      POSTメソッドはHTMLのinputフォームで実装できます。
      答えになっていないかったらごめんなさい。

    • Hiroyuki Naito より:

      私も最初意図したとおりに動かなかったのですが、下記の2点をおこなってログイン、アカウント作成、ログアウトの機能を確認することができました。

      ①本文で紹介されているファイルの他にadd.htmlを作成してpublicフォルダに格納する必要があります。add.htmlの内容は動画の中で見れます。長いファイルではないので手打ちしてください。

      ②DBブラウザを使って、wishlist.dbに新しくusersというテーブルを作成して、そこにaccountとpasswordというフィールド(TEXT型)を追加してください。

タイトルとURLをコピーしました