【FileMaker】パスワードの保存について考える

######注意事項
このページ内でご紹介する方法は、非常に単純なパスワード暗号化の方法です。
サンプルファイル内のスクリプトをそのまま利用されますと、容易に復号化出来ますので、最低限ご自身でキーを変更して頂くか、CryptAuthCode関数の利用などを検討されて下さい。

#パスワードを保存するシーンについて…

パスワードを FileMaker カスタムApp 内に保存するシーンとしては、次のような場合が考えられます。

- カスタムAppを開く時は予め設定しているアカウントで自動ログインさせ、あらかじめ準備するログイン画面で認証を行う。
    - ユーザーマスタのようなものを作成しておき、そこでログイン時のアカウントとパスワードを保存しておく。
        - 氏名、部署などのほか、権限なども設定出来るようにしておき、ログイン時にマスタからグローバルフィールドに設置値を保持させ、後々の処理に使う。
        - 1つのシステムが複数のファイルから構成されていて、FileMaker 標準の認証方法ではファイルごとにアカウントの設定を行うのが面倒な場合。

など…

#マスク付き編集ボックスを設定しても…

FileMaker Pro 15 から利用出来るようになった、マスク付き編集ボックスは非常に便利です。
しかし、ただマスク付き編集ボックスを設定しただけだと、容易に値を閲覧出来てしまいます。
<img width="575" alt="SS_ 2018-11-23 22.56.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/122690/f3fe9af2-de44-ceae-4f01-fba73c132ba0.png">

#パスワードを暗号化する…

そこでフィールドの値を暗号化することを考えてみます。

##CryptEncryptBase64関数とHexDecode関数

CryptEncryptBase64関数は、データとキーを指定することで、Base64フォーマットでデータを暗号化したテキストを返してくれます。

> 参考：
CryptEncryptBase64関数
https://fmhelp.filemaker.com/help/17/fmp/ja/index.html#page/FMP_Help%2Fcryptencryptbase64.html%23

この関数を使うと、評価される度に返ってくるテキストは変わりますので、これだけでも何もしていないよりはマシになります。
ただ、引数として設定するキーを平文で設定していると、計算式が分かると簡単に復号出来てしまいます。

そこで、HexDecode関数を併用してみます。
この関数は16進フォーマットでエンコードされたテキストから、オブジェクトまたはテキスト内容を返してくれるため、予め16進フォーマットでエンコードされたテキストを、CryptEncryptBase64関数のキーとして設定することで、若干ですがレベルは上がります。
16進フォーマットでエンコードする関数はHexEncodeが用意されていますので、データビューアなどで予めエンコードしておけばいいでしょう。

> 参考：
HexDecode関数
https://fmhelp.filemaker.com/help/17/fmp/ja/index.html#page/FMP_Help%2Fhexdecode.html%23
HexEncode関数
https://fmhelp.filemaker.com/help/17/fmp/ja/index.html#page/FMP_Help%2Fhexencode.html%23

###実装してみる…

####スクリプトの準備…
以下のようなものを準備します。

<pre>
フィールド設定 [ テーブル名::フィールド名 ; CryptEncryptBase64 ( テーブル名::フィールド名 ; HexDecode ( "16進フォーマットでエンコードされたテキスト" ) ) ] 
レコード/検索条件確定 [ ダイアログあり: オフ ] 
</pre>

####スクリプトトリガの設定…

上記で作ったスクリプトを、パスワードを入力するフィールドのスクリプトトリガ「OnObjectExit」などに設定します。

####結果…

パスワードを入力するフィールドに入り、値を入力します。
<img width="564" alt="SS_ 2018-11-23 23.02.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/122690/b24361e8-f26d-ece2-8a76-b5d35d10edfe.png">

上図のように先ほどは平文で保存されていた値が、よく分からない値になっていれば、正常に動作しています。

この方法でパスワードを暗号化することは出来ました。
ただ、このままではログイン時などにパスワードが合っているかの判断をすることは出来ません。

#パスワードを復号化する…

次に、パスワードが合っているかの判断をするために、CryptEncryptBase64関数で暗号化した文字列を、暗号化する前の状態に戻す必要があります。

##CryptDecryptBase64関数

CryptDecryptBase64関数は、データとキーを指定することで、Base64フォーマットで暗号化されたデータを復号した結果を返してくれます。

> 参考：
CryptDecryptBase64関数
https://fmhelp.filemaker.com/help/17/fmp/ja/index.html#page/FMP_Help%2Fcryptdecryptbase64.html%23

###実装してみる…

####認証する…

#####ログイン画面の準備…

今回は以下のようなフィールドを用意し、ログイン画面を作成します。

| フィールド名 | タイプ | オプション |
|:--|:--|:--|
| g_アカウント名 | テキスト | グローバル |
| g_パスワード | テキスト | グローバル |

<img width="1024" alt="SS_ 2018-12-08 13.57.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/122690/44e68a6d-28b3-3600-bed8-19c29f0fc8e8.png">

なお、ログイン画面のパスワードフィールドには、前述の「パスワード暗号化」スクリプトを、スクリプトトリガに設定し、ここでも暗号化されるようにしています。

<img width="1279" alt="SS_ 2018-12-08 14.16.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/122690/b96e6c86-3c31-5a6e-8836-c92ce22ada1e.png">

#####スクリプトの準備…

今回は以下のようなものを準備しました。

######ログインボタンを押した時に動作させるスクリプト

<pre>
# ##################################################
# ログインする。
# 
# 最終更新：
# Kei Urakawa
# 
# 引数・初期設定変数：
変数を設定 [ $引数 ; 値: Get ( スクリプト引数 ) ] 
変数を設定 [ $レイアウト名 ; 値: Get ( レイアウト名 ) ] 
# ##################################################
# 
エラー処理 [ オン ]
ユーザによる強制終了を許可 [ オフ ]
# 
レコード/検索条件確定 [ ダイアログあり: オフ ] 
# 
# アカウント名とパスワードのいずれかが空欄の場合、処理を終了する。
If [ IsEmpty ( qiita_password::g_アカウント名 ) or IsEmpty ( qiita_password::g_パスワード ) ] 
	カスタムダイアログを表示 [ "エラー" ; "アカウント名とパスワードが正しく入力されていません。¶OKボタンをクリックし、再度入力して下さい。" ] 
	現在のスクリプト終了 [ テキスト結果:    ] 
End If
# 
# ユーザーマスタからパスワードを取得し、照合する。
変数を設定 [ $認証 ; 値: ExecuteSQL ( "SELECT \"パスワード\" FROM \"ユーザーマスタ\" WHERE \"アカウント名\" = ?" ; "" ; "" ; qiita_password::g_アカウント名 ) ] 
If [ CryptDecryptBase64 ( qiita_password::g_パスワード ; HexDecode ( "6D32514765376D385064677A" ) ) = CryptDecryptBase64 ( $認証 ; HexDecode ( "6D32514765376D385064677A" ) ) ] 
	レイアウト切り替え [ 「メインメニュー」 (qiita_password) ; アニメーション: なし ]
	スクリプト実行 [ 指定: 一覧から ; 「グローバルフィールドクリア」 ; 引数:    ]
Else
	カスタムダイアログを表示 [ "エラー" ; "入力されたアカウント名とパスワードが一致しませんでした。¶OKボタンをクリックし、再度入力して下さい。" ] 
End If
</pre>

動作としては、以下のようになっています。

1.　アカウント名とパスワードのいずれかが空欄かどうかチェックし、空欄の場合はダイアログを表示し、スクリプトを終了。
2.　ユーザーマスタからパスワードを取得する。今回はExecuteSQLを使っています。
3.　取得したパスワードと、入力されてるパスワードを照合する。※ここで文字列を復号化しています。
4-1.　照合した結果、合っていればメインメニューへレイアウトを切り替え、ログイン画面のアカウント名とパスワードのグローバルフィールドをクリアします。
4-2. 照合した結果、合っていなければダイアログを表示します。

#まとめ

CryptEncryptBase64関数とCryptDecryptBase64関数、HexEncode関数とHexDecode関数を使うことで、容易に暗号化することが出来るようになりました。

また、ログイン時に入力するパスワードもフィールドから抜けると暗号化することで、極力そのままの値を保存することを控え、セキュリティを高めることが出来ます。

ただ、そもそもパスワードを今回のようにマスタにそもそも保存しておく必要があるのかなども考える必要がありそうです。

#サンプルファイル

今回のサンプルファイルは、以下からダウンロード頂けるようにしています。
管理者アカウントはデフォルトのままでパスワードは設定していませんので、自由に改変してお使い頂けると幸いです。

https://github.com/jaga30/FileMaker_Sample

※こんな風に変えてみたよ！という方はコメントで教えて頂けると嬉しいです :)

######注意事項
このページ内でご紹介した方法は、非常に単純なパスワード暗号化の方法です。
サンプルファイル内のスクリプトをそのまま利用されますと、容易に復号化出来ますので、最低限ご自身でキーを変更して頂くか、CryptAuthCode関数の利用などを検討されて下さい。