Edited at

「逆に考えるんだ、『流出しちゃってもいいさ』と考えるんだ」精神で、パスワードをWebサイトで管理したい


はじめに

Googleアカウント, Amazon, Twitter等、今時はアカウント制のサービスが溢れています。

これは仕事現場でも同様で、勤怠管理システムやredmine、github、slackと、関わる世界が広がれば広がるだけユーザーアカウントが増えていく。

こんなアカウント達のパスワード管理について、例えば1Passwordのようなツールで管理されている方も多いと思います。

ただ、中には「アプリに脆弱性が見つかって流出!が怖い…」なんて不安を持つ方もいるのかなと思ったり。

"パスワード管理"アプリの時点でそれは限界があるよな~、なんかいい手はないものかと。

「なにジョジョ?保管したパスワードの流出が怖い?

逆に考えるんだ、『流出しちゃってもいいさ』と考えるんだ」

ジョ、ジョースター卿!!そうだ!管理するのは補足情報だけにして、+キーフレーズを入力してパスワードを生成すればいいのでは!?

以降はパス"ワード"よりも文章で表現できるパス"フレーズ"という名称を使っていきます。


どんなもの?

実物はこちら(https://server.developerkikikaikaienjoy.work:60443/)に用意しました。

GeneratePassphrase.png

基本機能はこんな感じです。


  1. パスフレーズの生成(左枠)


    • Passphrase informationをベースに複雑なパスフレーズを生成します。



  2. Passphrase informationの管理


    • アカウントに対するPassphrase informationをベースに複雑なパスフレーズを生成します。



  3. アカウント管理(右枠)


    • Passphrase informationを保存するためのユーザーアカウントを管理します。




パスフレーズの生成

Passphrase informationの各項目を設定します。Keyphrase以外は覚えやすいフレーズを設定する想定です。

項目
概要
備考

Title
TwitterやQiitaといった、対象サービス情報を入力
設定の識別子にも使います

Keyphrase
普段使っているパスワードを入力する

Algorithm select
パスフレーズ生成に使用するハッシュアルゴリズムを選択する
選択肢変更でパスワード定期変更に対応可能

extra Info
おまけ情報
ここもパスワード定期変更用に用意

max length
パスフレーズ最大長
サイトによっては制限があるので

Use symbol in passphrase
パスフレーズに記号を入れるか?
サイトによっては記号必須
or 制限があるので


Passphrase informationの管理

1で扱う情報を毎回入力するのは面倒なので、Keyphrase以外をWebサーバー内に保存します。Save setting押下で設定を保存。

保存した設定は、右枠のPassphrase Settings内Get settingで一覧を取得。出てきたTitleをクリックすると左側の設定が更新されます。

PassphraseSettings.png

サーバーにはpassphraseに関わる情報を保存しているわけではないので、サーバーの情報を得た時点でやっと攻撃のスタートラインに立てるという感じ。

認証は凝らずにDigest認証にしています。しょぼい


アカウント管理

上記管理を行う為のアカウントの管理をします。Createは認証なしで利用出来、UpdateとDeleteはそのまま。しょぼい


実際使えるの?

正直使ってみないと分かんないな、ということで公開しちゃおうと思いました。

逆に考えるんだ、『公開しちゃってもいいさ』と考えるんだ

(サーバー監視の仕組みはどのタイミングで入れておくもんなんだろうか?)


どうやって作ったの?


基本情報


パスフレーズ生成アルゴリズム

基本的な考え方はこのような感じです。


  1. サービス名+キーフレーズ+その他を結合した文字列を作成

  2. ハッシュ化

  3. 使える文字をフル活用してコンバート

最初は1, 2だけでいいかと思っていましたが、長さ制限のあるサイトもある && 記号、大文字も使えた方がいい(友人からのアドバイス)というわけで小細工を追加しました。

こういう仕組みを考える時間はほんと楽しいですね。こんな感じにしました。


  • 16進数と対応する文字テーブルを作る

ハッシュ値の各16進数文字を変換していくので、最初にテーブルを用意して数値⇒その位置の文字に変えます。

というわけでinitializeHashListで16進数と対応する16文字のテーブル作成。

記号なし向けには0-9,1-z,A-Z+利用頻度の低い(であろう)q, zを追加した64文字。

記号あり向けには上記に加えて記号32文字を加えた96文字を利用してテーブルを作成。


hash/convert.go

func initializeHashList() {

//テーブルを詰めるためのmap
wordsWithoutSymbol = make(map[int][]string, 0)
wordsWithSymbol = make(map[int][]string, 0)
/*記号なし向け*/
tmpWithoutSymbol := make([]string, 4);
tmpWithoutSymbol[0] = "0123456789abcdef";
tmpWithoutSymbol[1] = "ghijklmnopqrstuv";
tmpWithoutSymbol[2] = "wxyzABCDEFGHIJKL";
tmpWithoutSymbol[3] = "MNOPQRSTUVWXYZqz";
wordsWithoutSymbol[1] = tmpWithoutSymbol

/*記号あり向け*/
tmpWithSymbol := make([]string, 6);
_ = copy(tmpWithSymbol, tmpWithoutSymbol);
tmpWithSymbol[4] = "!\"#$%&'()*+,-./:";
tmpWithSymbol[5] = ";<=>?@[]^_`{|}~, ";
wordsWithSymbol[1] = tmpWithSymbol


同様に1byteと対応する文字テーブルも作成。

今度は長さが短くなる場合のテーブル作成。2文字=1byteなので256文字を利用しないといけないので、用意する文字が一気に増えます。。。

上で用意した記号なし64文字、あり96文字では足りないので何回か同じ文字を出すようにしています。コードは省略


  • 各16進数文字を変換

抜粋。hashTable構造体に上で用意したテーブルと何分の一にするか(16進何桁を1文字にするか)の情報を詰めます。

で、convertの中で16進文字を実値に変更してhexvalueに詰め、対応する文字テーブルの文字を取得します。

1/1スケールの場合は文字テーブルがいっぱいあるので、どのテーブルを使うかも実値+α情報で切り替えるようにしました。

(いい感じで使える文字をまんべんなく利用できることを期待しています)


hash/convert.go

type hashTable struct {

//文字テーブル
table []string
//何分の一にするのか?
scaledown int
}

func (this hashTable) convert(hashString string) string {

hexvalue := 0
result := ""
hashStringLength := len(hashString)
for i := 0; i < hashStringLength ; i++ {
hexvalue = hexvalue << 4//1 byte
tmpval, _ := hex.DecodeString("0" + string(hashString[i]))
hexvalue |= int(tmpval[0])
if (i % this.scaledown) == (this.scaledown - 1) {
index_table := (hexvalue + i) % len(this.table)
index_value := hexvalue % len(this.table[index_table])
result += string(this.table[index_table][index_value])
hexvalue = 0
}
}
return result
}


単純な仕組みの組み合わせですが、使ってみるといい感じに散らばったパスワードが得られます。


メッセージ制御

クライアントとのメッセージはこんな感じにREST APIっぽくしました。

内部でメッセージを捌くためにこのような実装(override形式の方)を採用しています。


Digest認証

abbot/go-http-authを利用しました。詳細はこちら


その他

SSL/TLSで使用可能なcipher suiteの中で3DESを無効にするためechoに修正を加えています。詳細はこちら


後何する?


  • 機能が一目でわかる画面設計


    • かっこよくなくてもいいから、提供しているものが分かるようにしないといけない





  • 認証をかっこよくしたい


    • Digest認証はmd5ハッシュなので(規格上は別ハッシュも使えるが、各社ブラウザが非サポート)

    • 見た目云々の前にただただ使いにくい



  • サーバー状態監視したい

  • セキュリティ面平気?


    • ユーザー管理⇒md5のパスワード直保存は危険なので、工夫する



  • 設定のバックアップ

  • 管理者に設定がばれてしまう問題


    • 例えばDBには認証パスワードを利用して暗号化した文字列を詰めるようにするとか



  • トランザクション制御(稀だけど、同時リクエストが並列処理された場合のケアをしてない)


参考

DB関連

MySQLインストール時にやること(DBとユーザーの作成等)

主キーの設定・削除、AUTO_ICREMENT属性の設定

外部キー制約

MySQLでGo

Go, echoのtemplate操作で使う構造体はグローバルにしようぜって話

Simple if not working go template

golang, ハッシュアルゴリズムサポート一覧

https://golang.org/pkg/crypto/

各API

https://golang.org/pkg/crypto/sha256/

https://golang.org/pkg/crypto/sha512/

https://godoc.org/golang.org/x/crypto/blake2s

https://godoc.org/golang.org/x/crypto/blake2b

タイトル参考

「逆に考えるんだ」を使う5つのポイント