【Ruby】セキュアなパスワードポリシー設定のための正規表現


1.はじめに

 Webアプリケーション作成の際に特に意識をしないとならない箇所はセキュリティ面です。パスワードに関しては慎重に取り扱わなければいけません。
 この記事ではパスワードポリシー設定の際に正規表現を使うことが有用という話を聞きましたので、パスワードと正規表現について考察してみました。

2.正規表現とはなにか

 初心者歓迎!手と目で覚える正規表現入門・その1「さまざまな形式の電話番号を検索しよう」によると

「パターンを指定して、文字列を効率よく検索・置換するためのミニ言語」

ということみたいです。
 実際にRuby用の正規表現エディタのRubularというWebサイトを使用して、正規表現がどういったものか見ていきます。
例えば下記のようなテキストがあります。

8月30日学習時間:1時間
8月31日学習時間:3時間
9月1日学習時間:2時間
9月2日学習時間:4時間
9月3日学習時間:6時間
9月4日学習時間:10時間

この中で9月の学習時間の数字だけ正規表現を使って抽出します。
正規表現を使うと下記のような文字列になります。

  ^9.*:(\d*)時間$

^.*などの正規表現で使用する特別な文字を「メタ文字」と呼びます。
ここでのメタ文字の意味は後述するとしてRubularで検証していきます。
Rubularの「Your test string」に検証したいテキストを、「Your regular expression」に正規表現を入力していきます。
そうすると下記のような結果になります。

「Match result」の水色の部分が検索して見つかった部分です。
「Match groups」がキャプチャされた部分です。ここで学習時間を抽出します。
こうやって正規表現を使用して文字列を検索していきます。

3.この記事で使用する正規表現のメタ文字

参考にしました→基本的な正規表現一覧

メタ文字 意味
^ 文頭を表す
$ 文末を表す
[] いずれか1文字を表す文字クラスを作る
- []内で使われる文字の範囲を示す
. 任意の1文字を表す
? 直前の文字やパターンが1回、もしくは0回現れる
* 直前の文字やパターンが0回以上連続する。条件に合う最長の部分に一致
*? 直前の文字やパターンが0回以上連続する。条件に合う最短の部分に一致
+ 直前の文字やパターンが1回以上連続する。条件に合う最長の部分に一致
() 内部でマッチした文字列をキャプチャもしくはグループ化する
{n,m} 直前の文字やパターンがn回以上、m回以下連続する
\ メタ文字をエスケープしたり、\dや\wといった他のメタ文字の一部になったりする
\d 1個の半角数字(0123456789)
\w 半角英数字とアンダースコア'_'
(?=x) xの「直前の位置」を表す(肯定先読み)

 

4.どういったパスワードポリシーを設定すればよいのか

 コンピュータの性能は日進月歩で向上しているため、パスワードの暗号解読(例えば総当り攻撃)のスピードは年々上がっていっています。それに伴いパスワードの複雑化が求められています。
 2020年3月31日発行の内閣サイバーセキュリティーセンター「インターネットの安全・安心ハンドブック Ver.4.10」のP30によると、パスワードの安全性を高めるためのポリシーとして「英大文字+英小文字+数字+記号混じりで10桁以上」を推奨しています。
 ここではハンドブックに則ったパスワードポリシーを正規表現にて作成していきます。

5.パスワードポリシー設定のための正規表現

 ここでは段階的にそれぞれの正規表現を考えていきたいと思います。

 (1) 10桁以上40桁以下の半角英数記号
 (2) 英大文字1桁以上+英小文字1桁以上の条件追加
 (3) 数字1桁以上の条件追加
 (4) 記号1桁以上の条件追加

すべてのパターンを網羅できているわけではありませんが、上から

 ・9桁の文字
 ・10桁の英小文字
 ・10桁の英大文字+英小文字
 ・10桁の英大文字+英小文字+数字
 ・10桁の英大文字+英小文字+数字+記号
 ・41桁の文字

で下記のテキストで検証をしていきます。

Aaaaaaa1?
aaaaaaaaaa
Aaaaaaaaaa
Aaaaaaaaa1
Aaaaaaaa1?
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1?

 (1) 10桁以上40桁以下の半角英数記号

  正規表現で使用する文字についてはASCIIコードを使用して指定ができます。ここでの半角英数字記号はASCIIコード表の!から~までですので、下記のようになります。

Ruby
^[!-~]{10,40}$

予想通りの結果となりました。

 (2) 英大文字1桁以上+英小文字1桁以上の条件追加

  理解するのに時間がかかりましたが、段階を踏んで説明します

   a.肯定的先読み(?= )を使用して、条件の順序の依存をなくす
   b.条件の前の文字は、任意の1文字.を最短一致で0回以上連続*?する
   c.条件を設定する[A-Z][a-z]

  参考にしました→パスワード向け正規表現 /^(?=.?[a-z])(?=.?\d)[a-z\d]{8,100}$/i を解読する

Ruby
^(?=.*?[A-Z])(?=.*?[a-z])[!-~]{10,40}$

成功しました。

 (3) 数字1桁以上の条件追加

  (2)に数字0-9の条件\dを追加しました。

Ruby
^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?\d)[!-~]{10,40}$

成功しました。

 (4) 記号1桁以上の条件追加

  ASCIIコードを確認して記号を指定しました。
  記号とメタ文字がかぶる箇所については、エスケープ\をしました。

Ruby
^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?\d)(?=.*?[!-\/:-@\[-`{-~])[!-~]{10,40}$

成功しました。
記号確認用にテキストを追加しました。

6.まとめ

 正規表現は理解するのに骨が折れる部分がありました。パスワードポリシー設定以外にも色々使わせていただきます。