1.

認証 (AuthN) と認可 (AuthZ) 完全ガイド

編集
この記事の要点
  • 認証 (Authentication / AuthN) = 「あなたは誰?」(本人確認)
  • 認可 (Authorization / AuthZ) = 「あなたは何ができる?」(権限判定)
  • 認証要素: 知識 (パスワード) / 所有 (端末・トークン) / 生体 (指紋・顔)
  • 認可モデル: RBAC (ロール) / ABAC (属性) / ACL (リスト)
  • 実装: Session/Cookie (古典) / JWT (ステートレス) / OAuth 2.0 + OIDC (連携) / SAML (企業 SSO)

認証と認可の違い

項目認証 (Authentication)認可 (Authorization)
略称AuthNAuthZ
問いあなたは誰?あなたは何ができる?
ログイン (ID + パスワード)管理画面アクセス可否
方式パスワード / MFA / 生体 / 証明書RBAC / ABAC / ACL
処理タイミングセッション開始時各リクエスト時
標準仕様OpenID Connect, SAML, FIDO2, WebAuthnOAuth 2.0 (認可スコープ), XACML

「OAuth は認証規格」とよく誤解されますが、OAuth は本来認可の仕様で、認証は OAuth に「認証情報」を載せた OpenID Connect (OIDC) が担います。

認証の 3 要素 (3 ファクタ)

要素説明
知識情報 (Something you know)本人だけが知っている情報パスワード、PIN、秘密の質問
所有情報 (Something you have)本人だけが持っているものスマホ (TOTP / SMS)、IC カード、FIDO2 鍵
生体情報 (Something you are)本人の身体的特徴指紋、顔、虹彩、声紋

MFA (多要素認証) = 異なる種類の要素を 2 つ以上組み合わせる。パスワード (知識) + SMS (所有) は 2 要素、パスワード + 秘密の質問は1 要素 (知識のみ) で MFA 扱いされません。

パスワード認証の落とし穴と対策

// ❌ 平文保存 (絶対 NG)
INSERT INTO users(email, password) VALUES('a@b', 'mypass123');

// ❌ MD5 / SHA-1 単純ハッシュ (今や数秒で解読)
$hash = md5($password);

// ✅ bcrypt / Argon2 + ソルト自動
$hash = password_hash($password, PASSWORD_BCRYPT);
// または最新の Argon2id
$hash = password_hash($password, PASSWORD_ARGON2ID);

// 検証
if (password_verify($input, $stored_hash)) {
    // ログイン成功
}

// レインボーテーブル対策: ソルトは password_hash が自動付与
// タイミング攻撃対策: hash_equals 利用
if (hash_equals($expected, $given)) { /* ... */ }

セッション / Cookie 認証

最も伝統的なステートフル方式。ログイン後にサーバがセッション IDを発行し、Cookie でクライアントに保持させます:

1. POST /login (email, password)
2. サーバ: パスワード検証 → セッション ID 発行 → DB/Redis に保存
3. サーバ: Set-Cookie: PHPSESSID=abc123; HttpOnly; Secure; SameSite=Lax
4. 以降のリクエスト: Cookie: PHPSESSID=abc123
5. サーバ: セッションストア参照 → ユーザ特定

注意:
- Cookie には HttpOnly (JS から読めない) / Secure (HTTPS のみ) / SameSite=Lax|Strict (CSRF 対策) を付ける
- ログアウト時はセッション ID を破棄
- 定期的にセッション ID を再生成 (session_regenerate_id) で固定化攻撃対策

JWT (JSON Web Token)

サーバステートを持たない署名付きトークン。API / SPA / モバイルで広く使用:

構造: header.payload.signature (Base64URL エンコード)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwibmFtZSI6IkFsaWNlIiwiZXhwIjoxNzAwMDAwMDAwfQ.signature

復号:
header  : { "alg": "HS256", "typ": "JWT" }
payload : { "sub": "1", "name": "Alice", "exp": 1700000000 }
signature: HMAC_SHA256(secret, header.payload)

特徴:
- ステートレス (DB 参照不要)
- 改ざん不可 (署名検証)
- 中身は誰でも見れる (機密情報を入れない)
- 失効が困難 → 短い有効期限 + Refresh Token

OAuth 2.0 + OpenID Connect

サードパーティログイン「Google でログイン」「GitHub でログイン」の標準。

役割OAuth 2.0OIDC
用途認可 (リソース アクセス権)認証 (本人特定)
トークンAccess TokenID Token (JWT 形式)
ユーザ情報API 呼出が必要ID Token に含まれる

OAuth 2.0 認可コードフロー (PKCE)

1. ユーザがアプリで「Google ログイン」をクリック
2. アプリ → ブラウザ
   https://accounts.google.com/o/oauth2/v2/auth?
     client_id=xxx&
     redirect_uri=https://myapp/callback&
     response_type=code&
     scope=openid profile email&
     code_challenge=...&            ← PKCE
     code_challenge_method=S256
3. ユーザが Google でログイン + 同意
4. Google → アプリの callback に code を redirect
   https://myapp/callback?code=AUTH_CODE&state=xxx
5. アプリ → Google にトークン交換
   POST https://oauth2.googleapis.com/token
     grant_type=authorization_code&
     code=AUTH_CODE&
     code_verifier=...
6. レスポンス:
   {
     "access_token": "...",
     "id_token": "...",        ← OIDC で追加 (本人情報入り JWT)
     "refresh_token": "...",
     "expires_in": 3600
   }

認可モデル: RBAC / ABAC / ACL

RBAC (Role-Based Access Control)

もっとも一般的。ロールを介してユーザに権限を割り当てます:

ユーザ → ロール → 権限

[Alice]   ──→ [Admin]     ──→ users.create, users.delete, posts.*
[Bob]     ──→ [Editor]    ──→ posts.create, posts.update
[Carol]   ──→ [Viewer]    ──→ posts.read

ABAC (Attribute-Based Access Control)

ユーザ属性 / リソース属性 / 環境属性を組み合わせたポリシー評価。柔軟だが複雑:

ポリシー例: 「部署が同じで、勤務時間内なら閲覧可」

ALLOW IF
  user.department == resource.department
  AND env.time BETWEEN 9:00 AND 18:00
  AND env.ip IN company_network

ACL (Access Control List)

リソースごとに「誰が何をできる」のリストを持つ。Unix ファイルパーミッションが典型:

file: report.pdf
ACL:
  alice@example.com   : read, write
  bob@example.com     : read
  admin-group         : read, write, delete

Laravel での認証 / 認可

// ===== 認証 (AuthN) =====

// Guard と Provider (config/auth.php)
'guards' => [
    'web' => ['driver' => 'session', 'provider' => 'users'],
    'api' => ['driver' => 'sanctum', 'provider' => 'users'],
],

// ログイン
if (Auth::attempt(['email' => $email, 'password' => $password])) {
    $request->session()->regenerate();
    return redirect()->intended('/dashboard');
}

// ミドルウェア
Route::middleware('auth')->group(function () {
    Route::get('/dashboard', DashboardController::class);
});

// ===== 認可 (AuthZ) =====

// Gate (簡易ポリシー)
Gate::define('update-post', function (User $user, Post $post) {
    return $user->id === $post->user_id;
});

// 利用
if (Gate::allows('update-post', $post)) { /* ... */ }
$this->authorize('update-post', $post);

// Policy クラス
class PostPolicy
{
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id || $user->is_admin;
    }
}

// ロール (spatie/laravel-permission)
$user->assignRole('editor');
$user->givePermissionTo('posts.create');
if ($user->can('posts.create')) { /* ... */ }

Laravel Passport vs Sanctum

項目PassportSanctum
用途フル OAuth 2.0 サーバSPA / モバイル API トークン
仕組みOAuth 2.0 全フローAPI トークン or Cookie (SPA)
依存サーバ側で OAuth 各種フロー対応シンプル (トークンを DB に持つだけ)
推奨サードパーティアプリにトークン発行する API プロバイダ自社 SPA / モバイルアプリ

Spring Security の構成

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form.loginPage("/login").permitAll())
            .logout(logout -> logout.permitAll())
            .oauth2Login();  // OIDC ログイン

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

// メソッドレベル認可
@PreAuthorize("hasRole('ADMIN') or #post.userId == authentication.principal.id")
public void updatePost(Post post) { ... }

FAQ

Q: JWT とセッション、どちらが安全?
A: 「正しく実装されていれば」どちらも安全。JWT は失効が困難 → 短い expires + Refresh Token 必須。セッションは XSS / セッション固定化に注意。SPA + API なら Sanctum (Cookie 方式) が安全と勧められることが多い。

Q: SMS の MFA は危険?
A: SIM スワップ攻撃のリスクあり。重要度が高い場合はTOTP (Google Authenticator) / FIDO2 ハードウェアキー推奨。

Q: パスワードハッシュは bcrypt と Argon2 のどちらを使うべき?
A: 新規実装は Argon2id 推奨 (OWASP 推奨)。bcrypt も依然安全だが、メモリハード性で Argon2 が優位。

📸 参考画像

※ 旧バージョンから引き継いだ参考画像です。手順・図解の補助としてご覧ください。

参考画像

編集
Post Share
子ページ
  1. OAuth2.0
同階層のページ

同階層のページはありません

最近更新/作成されたページ