GoのWebフレームワーク Echoでの開発記録(bcryptを利用してログインパスワードを暗号化)
はじめに
今回は bcrypt
を利用してログインパスワードを暗号化します。
今回は以前にまとめた内容のアップデート版。 yhidetoshi.hatenablog.com
bcryptについて
説明については以下の記事がわかりやすく参照しました。
- bcrypt はこの
ソルト
とストレッチング
を実施してくれる
■ handler/hash/hash.go
- サインアップとログイン時に利用するために以下のコードを用意
PasswordEncrypt()
平文の文字列を受け取り暗号化するCheckHashPassword()
暗号化された文字列と平文の文字列を受け取り一致するか確認する
package hash import "golang.org/x/crypto/bcrypt" // 暗号化 (hash) func PasswordEncrypt(password string) (string, error) { hashPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) return string(hashPassword), err } // 暗号化パスワードと比較 func CheckHashPassword(hashPassword, password string) error { return bcrypt.CompareHashAndPassword([]byte(hashPassword), []byte(password)) }
■ bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
について確認
- costはストレッチ回数を決めるパラーメタのようですね。(コストは 4〜31で指定可能)今回はデフォルト値を指定
■ bcryptのコード参照
const ( MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword maxSaltSize = 16 ) func GenerateFromPassword(password []byte, cost int) ([]byte, error) { p, err := newFromPassword(password, cost) if err != nil { return nil, err } return p.Hash(), nil } func newFromPassword(password []byte, cost int) (*hashed, error) { if cost < MinCost { cost = DefaultCost } }
bcryptのソースコードに関しては、下記の記事で詳細に説明されています。(しっかり読んでいくには時間がかかりそうなので.. 。)
■ サインアップ時
PasswordEncrypt()に 入力フォームに入力された文字列を渡して暗号化して DBに登録する
handler/auth/auth.go
(一部抜粋)
package auth signUpForm := FormData{ Username: c.FormValue("username"), Password: c.FormValue("password"), } username := html.EscapeString(signUpForm.Username) password := html.EscapeString(signUpForm.Password) users := new(user.User) users.Password, _ = hash.PasswordEncrypt(password) user.CreateUser(users) // DBに書き込む
■ ログイン時
ログイン時に以下の2つのデータを比較してログインチェックを実施する
user.Password
: DBから取得したパスワード(暗号化済み)u.Password
: 入力フォームで入力されたパスワード(平文)
handler/auth/auth.go
(一部抜粋)
package auth func Login(c echo.Context) error { loginForm := FormData{ Username: c.FormValue("username"), Password: c.FormValue("password"), } // Formからデータ取得 username := html.EscapeString(loginForm.Username) password := html.EscapeString(loginForm.Password) u := new(user.User) u.Username = username u.Password = password if err := c.Bind(u); err != nil { return err } user := user.GetUser( &user.User{Username: u.Username}, ) err := hash.CheckHashPassword(user.Password, u.Password) if u.Username != user.Username || err != nil { // FormとDBのデータを比較 return &echo.HTTPError{ Code: http.StatusUnauthorized, Message: "Invalid Username or Password", } }
■ model/user/user.go
(一部抜粋)
package user type User struct { ID int `gorm:"primaryKey"` Username string Password string CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorn:"autoUpdateTime"` } func CreateUser(u *User) { db.Create(u) } func GetUser(u *User) User { var user User db.Where(u). First(&user) return user }
確認
■ サインアップページでアカウント作成
- ID/PW は
test/hogehoge
で作成。
- DBに保存されたパスワードを確認↓
mysql> select id, username, password from users where username="test"; +----+----------+--------------------------------------------------------------+ | id | username | password | +----+----------+--------------------------------------------------------------+ | 2 | test | $2a$10$uuY/yhmk8DMaIEI4CMzXAeOjQGBaJ6lWkr.YUDAkza.uPxfU3AIXe | +----+----------+--------------------------------------------------------------+
パスワードの "hogehoge" が暗号化されていますね!
- ログイン確認
❯ curl -u test:hogehoge http://localhost:8080/login -o /dev/null -w '%{http_code}\n' -s 200
goでbcryptライブラリを利用してログインパスワードを暗号化して管理できるようになりました。