0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Go, Gin, Gorm におけるセキュアなモデル作成の方法🐭🚀

Last updated at Posted at 2025-02-09

1. はじめに

  • これはECサイトのオペレーター管理システムにおける中核となるモデル実装である
  • システム管理者(system_admin)と店舗管理者(store_admin)の2つの権限を持つユーザーを管理し、セキュアなパスワード管理とデータの整合性を保証する

環境構成

主要な技術スタック:
Go v1.23.3
Gin Web Framework v1.10.0
GORM v1.25.12
PostgreSQL v17.0

2. モデル設計の特徴

2.1 権限管理システム

const (
    RoleSystemAdmin = "system_admin" // システム管理者権限
    RoleStoreAdmin  = "store_admin"  // 店舗管理者権限
)

定数として権限を定義することで、タイプセーフな権限管理を実現している

  • 権限値の誤入力を防止
  • コード補完による開発効率の向上
  • 権限の一元管理が可能

2.2 詳細なモデル構造

type Operator struct {
    ID           string         `gorm:"type:uuid;primary_key" json:"id"`
    Email        string         `gorm:"unique;not null" json:"email" validate:"required,email"`
    Username     string         `gorm:"not null" json:"username" validate:"required"`
    PasswordHash string         `gorm:"not null" json:"password_hash" validate:"required,password"`
    Role         string         `gorm:"not null" json:"role" validate:"required,oneof=system_admin store_admin"`
    StoreID      string         `gorm:"type:uuid;not null" json:"store_id" validate:"required,uuid"`
    AvatarURL    string         `json:"avatar_url" validate:"omitempty,url"`
    CreatedBy    string         `gorm:"type:uuid;not null" json:"created_by" validate:"required,uuid"`
    UpdatedBy    string         `gorm:"type:uuid" json:"updated_by" validate:"required,uuid"`
    DeletedBy    *string        `gorm:"type:uuid" json:"deleted_by,omitempty" validate:"omitempty,uuid"`
    CreatedAt    time.Time      `gorm:"autoCreateTime;not null" json:"created_at"`
    UpdatedAt    time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
    DeletedAt    gorm.DeletedAt `gorm:"index" json:"deleted_at"`
    Store        Store          `gorm:"foreignKey:StoreID" json:"store"`
}

2.2.1 主要フィールドの詳細解説

  1. 識別子管理

    • ID: UUIDv7形式を採用し、グローバルでユニークな識別子を保証
    • データベース移行やシステム統合時の整合性を確保
  2. 認証情報

    • Email: ユニーク制約付きで重複登録を防止
    • PasswordHash: ハッシュ化されたパスワードを保存し、平文での保存を回避
    • Username: システム内での表示名として使用
  3. 権限とアクセス制御

    • Role: system_admin/store_adminの2値のみを許容
    • StoreID: 所属店舗との紐付けによる適切なアクセス制御
    • Store: 店舗情報との1対1のリレーション
  4. 監査証跡

    • CreatedBy/UpdatedBy/DeletedBy: 操作者のトレーサビリティを確保
    • CreatedAt/UpdatedAt: 自動タイムスタンプによる操作日時の記録
    • DeletedAt: 論理削除のサポート
  5. プロフィール情報

    • AvatarURL: オプショナルなプロフィール画像のURL

2.3 バリデーション機能

各フィールドに対して厳密なバリデーションルールを適用:

validate:"required,email"           // メールアドレスの形式検証
validate:"required,password"        // カスタムパスワードルール
validate:"required,oneof=system_admin store_admin"  // 権限値の制限
validate:"required,uuid"            // UUID形式の検証
validate:"omitempty,url"           // URLフォーマットの検証

3. パスワードセキュリティ

3.1 パスワードポリシー

厳格なパスワードポリシーを実装し、セキュリティ要件を満たしている:

const minPasswordLength = 8

func validatePassword(fl validator.FieldLevel) bool {
    password := fl.Field().String()
    if len(password) < minPasswordLength {
        return false
    }
    return hasRequiredCharacterTypes(password)
}

今回のパスワードは以下の要件を全て満たす

  • 最小8文字以上の長さ
  • 大文字(A-Z)を1文字以上含む
  • 小文字(a-z)を1文字以上含む
  • 数字(0-9)を1文字以上含む
  • 記号(!@#$等)を1文字以上含む

3.2 文字種別チェックの実装

func hasRequiredCharacterTypes(password string) bool {
    var (
        hasUpper, hasLower, hasNumber, hasSymbol bool
    )

    for _, char := range password {
        switch {
        case unicode.IsUpper(char):
            hasUpper = true
        case unicode.IsLower(char):
            hasLower = true
        case unicode.IsNumber(char):
            hasNumber = true
        case unicode.IsPunct(char) || unicode.IsSymbol(char):
            hasSymbol = true
        }

        if hasUpper && hasLower && hasNumber && hasSymbol {
            return true
        }
    }
    return false
}

このチェック機能により:

  • 効率的な文字種別判定(1回のループで全条件をチェック)
  • 早期リターンによるパフォーマンス最適化
  • Unicode対応による多言語サポート

4. データベース連携

4.1 UUIDの自動生成

func (o *Operator) BeforeCreate(tx *gorm.DB) error {
    if o.ID != "" {
        return nil
    }
    id, err := uuid.NewV7()
    if err != nil {
        return err
    }
    o.ID = id.String()
    return nil
}

GORMのフックを利用して:

  • レコード作成前に自動的にUUIDを生成
  • 既存IDの上書きを防止
  • エラーハンドリングによる信頼性確保

4.2 データベース制約

  • gorm:"type:uuid;primary_key": UUIDタイプの主キー
  • gorm:"unique;not null": ユニーク制約と非NULL制約
  • gorm:"index": インデックスによる検索最適化
  • gorm:"foreignKey:StoreID": 外部キー制約による参照整合性の確保

5. 拡張性と保守性

このモデルは以下の特徴により、高い拡張性と保守性を実現しています:

  1. モジュール性

    • 各機能が明確に分離され、独立してテスト可能
    • カスタムバリデーションの追加が容易
  2. 型安全性

    • 厳密な型チェックによるバグの早期発見
    • コンパイル時のエラーチェック
  3. 監査対応

    • 完全な操作履歴の追跡が可能
    • コンプライアンス要件への対応
  4. セキュリティ

    • パスワードの安全な管理
    • アクセス制御の適切な実装

6. まとめ

  • このモデルは、セキュリティ、保守性、拡張性のバランスが取れた実装となっており、実際のプロダクション環境での使用に適している
  • また、GORMやvalidatorなどの標準的なライブラリを活用することで、信頼性の高いシステムを効率的に構築することができる
  • 今後の開発においても、このモデルを基盤として、さらなる機能拡張や改善を行うことが可能
  • セキュリティ要件の変更やビジネスルールの追加にも、柔軟に対応できる設計となっている
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?