タイトル: 認証・認可
SEOタイトル: 認証 (AuthN) と認可 (AuthZ) 完全ガイド
| この記事の要点 |
|
認証と認可の違い
| 項目 | 認証 (Authentication) | 認可 (Authorization) |
|---|---|---|
| 略称 | AuthN | AuthZ |
| 問い | あなたは誰? | あなたは何ができる? |
| 例 | ログイン (ID + パスワード) | 管理画面アクセス可否 |
| 方式 | パスワード / MFA / 生体 / 証明書 | RBAC / ABAC / ACL |
| 処理タイミング | セッション開始時 | 各リクエスト時 |
| 標準仕様 | OpenID Connect, SAML, FIDO2, WebAuthn | OAuth 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.0 | OIDC |
|---|---|---|
| 用途 | 認可 (リソース アクセス権) | 認証 (本人特定) |
| トークン | Access Token | ID 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
| 項目 | Passport | Sanctum |
|---|---|---|
| 用途 | フル 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 が優位。
📸 参考画像
※ 旧バージョンから引き継いだ参考画像です。手順・図解の補助としてご覧ください。
