はじめに
本記事は、Golangを使用したECサイトの基本的なユーザー管理機能の実装方法について記載しています。
セキュリティ対策についても言及していますが、 本記事の実装があらゆる攻撃に対して安全であることを保証するものではありません。
実際に運用する際は、以下の点に十分注意してください:
- 最新のセキュリティ対策を適用する(本記事の実装が古くなる可能性があるため)
- 専門家によるコードレビューを受ける(より高い安全性を確保するため)
- テスト環境で十分な検証を行う(本番環境に導入する前に脆弱性を洗い出す)
本記事を参考にした実装によるいかなる損害・損失についても、筆者は一切の責任を負いません。
自己責任で実装を行い、適切なセキュリティ対策を講じてください。
本記事では、主にGORMやSQLite, bcryptを利用した実装方法を記載していきます。
それでは本編へ
📕 対象読者
- Golangの基本文法がわかる開発者
- 基本的なユーザー管理機能を実装したい方
🔍 目次
1. GORMとは?
GORM とは、Golang用のオープンソースORM(Object-Relational Mapping)ライブラリです。データベースとのやりとりを簡単に行うことができます。
GORMを使うことで、SQLを書かずに以下のような処理を簡単に実装できます。
- データベースへの接続
- テーブルの作成・マイグレーション
- データの挿入・更新・削除・検索
本記事では、GORMを使用してSQLiteデータベースと連携し、ユーザー情報を管理します。
2. SQLiteとは?
SQLite は、軽量で自己完結型のリレーショナルデータベース管理システム(RDBMS)です。サーバーを必要とせず、アプリケーション内のファイルとしてデータを保存できるため、小規模なアプリケーションや開発環境に適しています。
GORMはSQLiteをサポートしており、本記事では sqlite3
を使用してデータベースを管理します。
3. ユーザー登録の実装
GORMを用いたデータベース処理とパスワードのハッシュ化を行います。
ユーザー登録のコードを見る
package main
import (
"fmt"
"log"
"net/http"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"golang.org/x/crypto/bcrypt"
)
var db *gorm.DB
type User struct {
ID uint `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
}
func init() {
var err error
db, err = gorm.Open("sqlite3", "./users.db")
if err != nil {
log.Fatal(err)
}
db.AutoMigrate(&User{})
}
func registerUser(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
username := r.FormValue("username")
password := r.FormValue("password")
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
user := User{Username: username, Password: string(hashedPassword)}
db.Create(&user)
fmt.Fprintf(w, "User registered successfully!")
}
}
func main() {
http.HandleFunc("/register", registerUser)
log.Fatal(http.ListenAndServe(":8080", nil))
}
4. ログイン / ログアウトの実装
✅ ログイン処理
ユーザーのパスワードをハッシュと比較し、認証を行います。
ログインのコードを見る
func loginUser(w http.ResponseWriter, r *http.Request) {
username := r.FormValue("username")
password := r.FormValue("password")
var user User
db.Where("username = ?", username).First(&user)
if user.ID == 0 {
http.Error(w, "User not found", http.StatusNotFound)
return
}
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
if err != nil {
http.Error(w, "Invalid password", http.StatusUnauthorized)
return
}
fmt.Fprintf(w, "Login successful!")
}
func main() {
http.HandleFunc("/login", loginUser)
}
✅ ログアウト処理
ログアウトのコードを見る
func logoutUser(w http.ResponseWriter, r *http.Request) {
// クッキーを削除(過去の時間に設定することで無効化)
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: "",
MaxAge: -1,
Path: "/",
})
fmt.Fprintf(w, "Logged out successfully!")
}
func main() {
http.HandleFunc("/logout", logoutUser)
}
5. ユーザー削除の実装
ユーザーを削除するAPIを実装します。
ユーザー削除のコードを見る
func deleteUser(w http.ResponseWriter, r *http.Request) {
username := r.FormValue("username")
var user User
db.Where("username = ?", username).First(&user)
if user.ID == 0 {
http.Error(w, "User not found", http.StatusNotFound)
return
}
db.Delete(&user)
fmt.Fprintf(w, "User deleted successfully!")
}
func main() {
http.HandleFunc("/delete", deleteUser)
}
6. 動作確認方法
実装した内容が正しく動作するか確認するため、以下の手順を実施します。
下記はusernameがtestuser
, passwordが1234
のユーザーで動作確認する場合の例です。
プログラムの実行
cd {上記プログラムを保存したパス}
go run main.go(main.go以外で保存した場合は該当するファイル名)
ユーザー登録
curl -X POST -d "username=testuser&password=1234" http://localhost:8080/register
データベースにユーザーが登録されているかの確認
testuserが登録されていることを確認します。
sqlite3 users.db "SELECT * FROM users;"
ログイン
curl -X POST -d "username=testuser&password=1234" http://localhost:8080/login -c cookies.txt
※ -c cookies.txt をつけることで、クッキーを保存し、ログアウト時に使用できるようにしています。
ログアウト
curl -X GET http://localhost:8080/logout -b cookies.txt
ユーザー削除
curl -X POST -d "username=testuser" http://localhost:8080/delete
データベースでユーザーが削除されているかの確認
testuserが削除されていることを確認します。
sqlite3 users.db "SELECT * FROM users;"
7. セキュリティの考え方と対策
ECサイトのユーザー管理機能を実装する際は、以下のセキュリティ対策を必ず考慮しましょう。
🔐 1. パスワードの安全な管理
- ユーザーのパスワードは平文で保存せず、
bcrypt
などのハッシュ化手法を用いて暗号化する。 - ハッシュ化することで、万が一データベースが流出してもパスワードが直接漏洩するリスクを軽減できる。
🚨 2. 適切なエラーハンドリング
-
ユーザーが存在しない場合や、パスワードが間違っている場合でも「認証エラー」として統一する。
- 例:
"ユーザー名またはパスワードが間違っています"
- 具体的な理由を明かさないことで、攻撃者に情報を与えないようにする。
- 例:
🔒 3. HTTPの使用(本番環境では必須)
- ユーザーの情報が暗号化されていないHTTP通信で送信されると、盗聴や改ざんのリスクがある。
- 本番環境では必ずHTTPSを使用し、通信内容を暗号化すること。
8. SQLiteエラーの解決方法
❌ no such table: users
のエラーが出る場合
✅ 対処法
-
go run main.go
を実行したパスと同じパスでsqlite3
コマンドを実行しているか確認(pwd
で確認) -
users.db
が存在するか確認 (ls -l users.db
) -
db.LogMode(true)
を追加し、SQLの実行を確認 -
go run main.go
を適切なディレクトリで実行 (pwd
で確認)
あとがき
動作確認手順も示しているので、開発環境などで一度試してみてください。
何かありましたら、コメントお願いいたします。