Play Framework認証

認証ライブラリPlay FrameworkScalaJavaWeb開発セキュリティ

認証ライブラリ

Play Framework認証

概要

Play Framework認証は、Play FrameworkアプリケーションでのWebアプリケーション認証を実装するための組み込み機能です。基本的な認証・認可機能を提供し、カスタム認証スキームの実装も可能です。

詳細

Play Framework認証は、Play Frameworkに組み込まれたシンプルで軽量な認証システムです。フレームワーク自体に完全な認証メカニズムは提供されておらず、開発者が独自に実装する必要がありますが、必要な基本コンポーネントは提供されています。Security.AuthenticatorパターンやSecureモジュールを使用してセッション管理、CSRF保護、XSS防止などの基本的なセキュリティ機能を実装できます。2024年現在でも、セキュリティベストプラクティスに従った堅牢な認証システムの構築が可能で、カスタム要件に応じた柔軟な実装ができることが特徴です。Scala、Javaの両言語でサポートされており、RESTful APIとWebアプリケーション両方での認証に対応しています。

メリット・デメリット

メリット

  • フレームワーク統合: Play Frameworkとの完全統合で一貫した開発体験
  • セッション管理: 署名付きセッション(非暗号化)による安全なセッション管理
  • CSRF保護: 組み込みのCSRF(Cross-Site Request Forgery)保護機能
  • カスタマイザブル: 要件に応じた柔軟な認証スキームの実装が可能
  • パフォーマンス: 軽量で高速な認証処理
  • セキュリティ: 自動的なXSSエスケープとセキュリティヘッダー
  • 言語サポート: ScalaとJavaの両方でサポート

デメリット

  • 基本機能のみ: 高度な認証機能は自分で実装する必要がある
  • セッション暗号化なし: セッションデータは署名されるが暗号化されない
  • 外部依存: 複雑な認証要件では外部ライブラリとの併用が必要
  • 学習コスト: Play Framework固有の認証パターンの習得が必要
  • OAuth対応: OAuth/OpenIDは別途実装または他ライブラリが必要

主要リンク

書き方の例

基本的なAuthenticator実装

// Scala での認証実装
import play.api.mvc._
import play.api.mvc.Security.Authenticator

class MyAuthenticator extends Authenticator {
  def getUsername(request: RequestHeader): Option[String] = {
    // セッションからユーザー名を取得
    request.session.get("username")
  }
  
  def onUnauthorized(request: RequestHeader): Result = {
    // 未認証時のリダイレクト
    Results.Redirect(routes.AuthController.loginPage())
  }
}

// コントローラーでの使用
@Security.Authenticated(new MyAuthenticator)
def protectedAction = Action { implicit request =>
  Ok(s"Hello, ${request.username}")
}

セッション管理とログイン

// ログイン処理
def login = Action { implicit request =>
  loginForm.bindFromRequest.fold(
    formWithErrors => {
      BadRequest(views.html.login(formWithErrors))
    },
    userData => {
      // ユーザー認証処理
      if (authenticateUser(userData.email, userData.password)) {
        Redirect(routes.Dashboard.index())
          .withSession("username" -> userData.email)
      } else {
        BadRequest(views.html.login(loginForm.withGlobalError("Invalid credentials")))
      }
    }
  )
}

// ログアウト処理
def logout = Action {
  Redirect(routes.AuthController.loginPage())
    .withNewSession
    .flashing("info" -> "You have been logged out")
}

CSRF保護の実装

// CSRF トークンの生成と検証
import play.filters.csrf._

def form = Action { implicit request =>
  Ok(views.html.form()(request, messagesProvider))
}

def submitForm = Action { implicit request =>
  // CSRFフィルターが自動的に検証
  CSRF.getToken match {
    case Some(token) => 
      // フォーム処理
      processForm(request)
    case None =>
      Forbidden("CSRF token missing")
  }
}

Java版認証実装

// Java での認証実装
import play.mvc.*;
import play.mvc.Security.*;

public class AuthAction extends Authenticator {
    @Override
    public String getUsername(Context ctx) {
        // セッションからユーザー名を取得
        return ctx.session().get("username");
    }
    
    @Override
    public Result onUnauthorized(Context ctx) {
        // 未認証時のリダイレクト
        return redirect(routes.AuthController.login());
    }
}

// コントローラーでの使用
@Security.Authenticated(AuthAction.class)
public Result dashboard() {
    String username = session("username");
    return ok("Welcome, " + username);
}

カスタム認証スキーム

// APIキー認証の実装例
class ApiKeyAuthAction extends ActionBuilder[Request, AnyContent] {
  override def invokeBlock[A](request: Request[A], block: Request[A] => Future[Result]): Future[Result] = {
    request.headers.get("X-API-Key") match {
      case Some(apiKey) if isValidApiKey(apiKey) =>
        block(request)
      case _ =>
        Future.successful(Unauthorized("Invalid API key"))
    }
  }
  
  private def isValidApiKey(key: String): Boolean = {
    // API キーの検証ロジック
    ApiKeyRepository.validate(key)
  }
}

// 使用例
def apiEndpoint = ApiKeyAuthAction { request =>
  Ok(Json.obj("data" -> "Protected API response"))
}

セキュリティフィルターの設定

// application.conf でのセキュリティ設定
play.filters.enabled += "play.filters.csrf.CSRFFilter"
play.filters.enabled += "play.filters.headers.SecurityHeadersFilter"
play.filters.enabled += "play.filters.hosts.AllowedHostsFilter"

// CSRFの設定
play.filters.csrf {
  token.name = "csrfToken"
  contentType.blackList = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"]
}

// セキュリティヘッダーの設定
play.filters.headers {
  frameOptions = "DENY"
  xssProtection = "1; mode=block"
  contentTypeOptions = "nosniff"
  contentSecurityPolicy = "default-src 'self'"
}