SQLとJavaScriptとRustでメールによる認証を自力で実装してみた ①
自由につかえるSQLを手に入れたので、ログイン機能を練習を兼ねて作ってみました。
最終的には認証機能を利用してチャットアプリを作成しようと思っています。
4部構成になっているので最後までお読みいただけると嬉しいです。
目次:
① 認証の流れを確認 ←いまココ!
② DB開発 →執筆中
③ バックエンド開発 →執筆中
④ フロントエンド開発 →執筆中
使用技術
- Solid.js (軽量WebUIフレームワーク・GitHub Pagesにホスティング)
- actix-web (Rust製Webフレームワーク・shuttleでデプロイ)
- MariaDB (データベース・Xserverでデプロイ)
どんな仕様か?
Webサイト丸ごとについて認証を必要とするのではなく、(ブラウザが最初に行うGETリクエスト自体には制限は設けない)APIへのアクセスに認証を必要とします。
Solid.jsによるフロントエンドアプリケーションのロジックは全て公開することになるので、もしかしたらNext.jsなどでページ丸ごと認証を必要にした方がセキュリティーは上がるかもしれません。
認証はメールアドレスを用いて行います。
データベースの構造
実際に使用したSQL文です。
/* 登録済みのユーザー */
CREATE TABLE users(
name CHAR(15) NOT NULL, /* 名前 */
id CHAR(10) NOT NULL, /* ユニークなID */
token CHAR(30) NOT NULL, /* 今ログインに使用できるトークン */
pass CHAR(30) NOT NULL, /* パスワードのSHA256ハッシュ */
last_login DATE NOT NULL, /* 最後にログインした日時 */
created_date DATE NOT NULL, /* アカウント作成日時 */
registered_date DATE NOT NULL, /* 登録日時 */
updated_date DATE NOT NULL, /* 更新日時 */
);
CREATE TABLE unregistered_users(
name CHAR(15) NOT NULL, /* 名前 */
id CHAR(10) NOT NULL, /* ユニークなID */
pass CHAR(30) NOT NULL, /* パスワードのSHA256ハッシュ */
temporary_token CHAR(30) NOT NULL, /* 一時トークン */
created_date DATE NOT NULL, /* アカウント作成日時 */
)
ここでは、
最初に情報を入力することをアカウント作成
メールアドレスの認証が済んだことを登録と呼んでいます。
作成・登録・ログイン・ログアウト・認証の流れ
1、作成
クライアントサイド
- ユーザーがメールアドレスと、自ら決めたパスワード・名前・IDを入力
- クライアントサイドでパスワードのSHA256ハッシュを生成する
- メールアドレス・パスワードのハッシュ・名前・IDをサーバーに送信
- サーバーから一時トークンを受け取り、cookieに保存しておく
- 指定のメールアドレスにワンタイムパスワードを含む認証メールを送ったこと、またはエラー情報を表示
サーバーサイド
- クライアントサイドから情報を受け取る・情報が正しいことを確認
- Gmail APIを用いてメールを送信
- unregistered_usersに情報を保存し待機
2、登録
クライアントサイド
- ワンタイムパスワードを入力させる
- cookieに保存されている一時トークンとワンタイムパスを合わせてサーバーサイドに送信する
- 登録に成功したことを表示するまたは、エラー情報を表示する
サーバーサイド
- 一時トークン・ワンタイムパスワードの一致・作成から24時間以内であることを確認する
- unregistered_usersから項目を消去し、usersに項目を追加する
3、ログイン
クライアントサイド
- パスワード・メールアドレス(またはID)を入力
- パスワードのSHA256ハッシュをとる
- ハッシュとメールアドレス(またはID)をサーバーサイドに送信
- トークンを受け取りcookieに保存
サーバーサイド
- ハッシュとメールアドレス(またはID)を取得し、整合性をチェック
- トークンを新しく生成し、DBに保存し・クライアントへ返す
- last_loginを更新
3、ログアウト
クライアントサイド
- cookieを削除
4、認証
クライアントサイド
- APIへのリクエストと共にトークン・IDを送信する
- APIのレスポンスを受け取る
サーバーサイド
- 整合性をチェックしてレスポンスを返す
クライアントサイドは、今自分がどのフェーズであるのかを認識するため、localStrageにそれを保存します。
レインボーリスト攻撃対策のために個別にソルトもしくはペッパーを用いるのが標準であると思いますが、そのように実装をすると複雑になるかと思ったので、実装していません。
SHA256のみの単純な認証の実装は、安全性が低く業務レベルでは用いるべきではありません。
cookieにはSecure属性・SameSite属性を付与し、各種攻撃を防ぎます。