Quarkus Security

affaan-m/ECC/docs/ja-JP/skills/quarkus-security

by affaan-mef648e01899ba3e8dc6371642deaaf64b4477775No license275K starsListed Oct 9, 2026Updated Oct 9, 2026Repository updated 4 days ago

Quarkus認証、認可、JWT/OIDC、RBAC、入力検証、CSRF、シークレット管理、依存関係セキュリティのセキュリティベストプラクティス。

Instructions onlySecurity
AI-generated overview

Best practices for securing Quarkus applications with authentication, authorization, validation and secrets handling.

What it does
This skill provides guidance and code patterns for securing Quarkus applications. It covers JWT/OIDC authentication, role-based and programmatic authorization, Bean Validation for input, SQL injection prevention, password hashing, CORS, secrets management, rate limiting, security headers, audit logging and dependency CVE scanning. It produces configuration snippets and Java code examples rather than executable artifacts.
When to use it
Use it when adding or reviewing authentication and authorization in a Quarkus service, validating user input, configuring CORS or security headers, managing secrets, adding rate limiting, or scanning dependencies for vulnerabilities.
Requirements
No scripts are shipped; it is instructions and code examples only. Following the examples assumes a Quarkus project with relevant extensions such as SmallRye JWT, OIDC, Hibernate/Panache and Bean Validation, plus optional access to an OIDC provider, HashiCorp Vault and Maven or Gradle for dependency scanning.

Quarkus Security Review

認証、認可、入力検証によってQuarkusアプリケーションを保護するためのベストプラクティス。

When to Activate

  • 認証追加(JWT、OIDC、Basic認証)
  • @RolesAllowedまたはSecurityIdentityで認可実装
  • ユーザー入力検証(Bean Validation、カスタムバリデータ)
  • CORS設定またはセキュリティヘッダー構成
  • シークレット管理(Vault、環境変数、設定ソース)
  • レート制限またはブルートフォース対策追加
  • CVEの依存関係スキャン
  • MicroProfile JWTまたはSmallRye JWT操作

Authentication

JWT Authentication

java
// JWT で保護されたリソース@Path("/api/protected")@Authenticatedpublic class ProtectedResource {
  @Inject  JsonWebToken jwt;
  @Inject  SecurityIdentity securityIdentity;
  @GET  public Response getData() {    String username = jwt.getName();    Set<String> roles = jwt.getGroups();    return Response.ok(Map.of(        "username", username,        "roles", roles,        "principal", securityIdentity.getPrincipal().getName()    )).build();  }}

Configuration (application.properties):

properties
mp.jwt.verify.publickey.location=publicKey.pemmp.jwt.verify.issuer=https://auth.example.com
# OIDCquarkus.oidc.auth-server-url=https://auth.example.com/realms/myrealmquarkus.oidc.client-id=backend-servicequarkus.oidc.credentials.secret=${OIDC_SECRET}

Custom Authentication Filter

java
@Provider@Priority(Priorities.AUTHENTICATION)public class CustomAuthFilter implements ContainerRequestFilter {
  @Inject  SecurityIdentity identity;
  @Override  public void filter(ContainerRequestContext requestContext) {    String authHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
    // ヘッダーが無いまたは不正形式の場合は即座に拒否    if (authHeader == null || !authHeader.startsWith("Bearer ")) {      requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());      return;    }
    String token = authHeader.substring(7);    if (!validateToken(token)) {      requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());    }  }
  private boolean validateToken(String token) {    // トークン検証ロジック    return true;  }}

Authorization

Role-Based Access Control

java
@Path("/api/admin")@RolesAllowed("ADMIN")public class AdminResource {
  @GET  @Path("/users")  public List<UserDto> listUsers() {    return userService.findAll();  }
  @DELETE  @Path("/users/{id}")  @RolesAllowed({"ADMIN", "SUPER_ADMIN"})  public Response deleteUser(@PathParam("id") Long id) {    userService.delete(id);    return Response.noContent().build();  }}
@Path("/api/users")public class UserResource {
  @Inject  SecurityIdentity securityIdentity;
  @GET  @Path("/{id}")  @RolesAllowed("USER")  public Response getUser(@PathParam("id") Long id) {    // 所有権確認    if (!securityIdentity.hasRole("ADMIN") &&        !isOwner(id, securityIdentity.getPrincipal().getName())) {      return Response.status(Response.Status.FORBIDDEN).build();    }    return Response.ok(userService.findById(id)).build();  }
  private boolean isOwner(Long userId, String username) {    return userService.isOwner(userId, username);  }}

Programmatic Security

java
@ApplicationScopedpublic class SecurityService {
  @Inject  SecurityIdentity securityIdentity;
  public boolean canAccessResource(Long resourceId) {    if (securityIdentity.isAnonymous()) {      return false;    }
    if (securityIdentity.hasRole("ADMIN")) {      return true;    }
    String userId = securityIdentity.getPrincipal().getName();    return resourceRepository.isOwner(resourceId, userId);  }}

Input Validation

Bean Validation

java
// 悪い例:検証なし@POSTpublic Response createUser(UserDto dto) {  return Response.ok(userService.create(dto)).build();}
// 良い例:検証DTOpublic record CreateUserDto(    @NotBlank @Size(max = 100) String name,    @NotBlank @Email String email,    @NotNull @Min(18) @Max(150) Integer age,    @Pattern(regexp = "^\\+?[1-9]\\d{1,14}$") String phone) {}
@POST@Path("/users")public Response createUser(@Valid CreateUserDto dto) {  User user = userService.create(dto);  return Response.status(Response.Status.CREATED).entity(user).build();}

Custom Validators

java
@Target({ElementType.FIELD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Constraint(validatedBy = UsernameValidator.class)public @interface ValidUsername {  String message() default "Invalid username format";  Class<?>[] groups() default {};  Class<? extends Payload>[] payload() default {};}
public class UsernameValidator implements ConstraintValidator<ValidUsername, String> {  @Override  public boolean isValid(String value, ConstraintValidatorContext context) {    if (value == null) return false;    return value.matches("^[a-zA-Z0-9_-]{3,20}$");  }}
// 使用例public record CreateUserDto(    @ValidUsername String username,    @NotBlank @Email String email) {}

SQL Injection Prevention

Panache Active Record (Safe by Default)

java
// 良い例:Panacheでのパラメータ化クエリList<User> users = User.list("email = ?1 and active = ?2", email, true);
Optional<User> user = User.find("username", username).firstResultOptional();
// 良い例:名前付きパラメータList<User> users = User.list("email = :email and age > :minAge",    Parameters.with("email", email).and("minAge", 18));

Native Queries (Use Parameters)

java
// 悪い例:文字列連結@Query(value = "SELECT * FROM users WHERE name = '" + name + "'", nativeQuery = true)
// 良い例:パラメータ化ネイティブクエリ@Entitypublic class User extends PanacheEntity {  public static List<User> findByEmailNative(String email) {    return getEntityManager()        .createNativeQuery("SELECT * FROM users WHERE email = :email", User.class)        .setParameter("email", email)        .getResultList();  }}

Password Hashing

java
@ApplicationScopedpublic class PasswordService {
  public String hash(String plainPassword) {    return BcryptUtil.bcryptHash(plainPassword);  }
  public boolean verify(String plainPassword, String hashedPassword) {    return BcryptUtil.matches(plainPassword, hashedPassword);  }}
// サービスで使用@ApplicationScopedpublic class UserService {  @Inject  PasswordService passwordService;
  @Transactional  public User register(CreateUserDto dto) {    String hashedPassword = passwordService.hash(dto.password());    User user = new User();    user.email = dto.email();    user.password = hashedPassword;    user.persist();    return user;  }
  public boolean authenticate(String email, String password) {    return User.find("email", email)        .firstResultOptional()        .map(u -> passwordService.verify(password, u.password))        .orElse(false);  }}

CORS Configuration

properties
# application.propertiesquarkus.http.cors=truequarkus.http.cors.origins=https://app.example.com,https://admin.example.comquarkus.http.cors.methods=GET,POST,PUT,DELETEquarkus.http.cors.headers=accept,authorization,content-type,x-requested-withquarkus.http.cors.exposed-headers=Content-Dispositionquarkus.http.cors.access-control-max-age=24Hquarkus.http.cors.access-control-allow-credentials=true

Secrets Management

properties
# application.properties - シークレットはここに置かない
# 環境変数を使用quarkus.datasource.username=${DB_USER}quarkus.datasource.password=${DB_PASSWORD}quarkus.oidc.credentials.secret=${OIDC_CLIENT_SECRET}
# またはVaultを使用quarkus.vault.url=https://vault.example.comquarkus.vault.authentication.kubernetes.role=my-role

HashiCorp Vault Integration

java
@ApplicationScopedpublic class SecretService {
  @ConfigProperty(name = "api-key")  String apiKey; // Vault から取得
  public String getSecret(String key) {    return ConfigProvider.getConfig().getValue(key, String.class);  }}

Rate Limiting

セキュリティ注意: X-Forwarded-For を直接使用しないでください — クライアントで偽装できます。 サーブレットリクエストからの実際のリモートアドレスを使用するか、利用可能な場合は認証ID(APIキー、JWTサブジェクト)を使用します。

java
@ApplicationScopedpublic class RateLimitFilter implements ContainerRequestFilter {  private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
  @Inject  HttpServletRequest servletRequest;
  @Override  public void filter(ContainerRequestContext requestContext) {    String clientId = getClientIdentifier();    RateLimiter limiter = limiters.computeIfAbsent(clientId,        k -> RateLimiter.create(100.0)); // 1秒あたり100リクエスト
    if (!limiter.tryAcquire()) {      requestContext.abortWith(          Response.status(429)              .entity(Map.of("error", "Too many requests"))              .build()      );    }  }
  private String getClientIdentifier() {    // コンテナが提供するリモートアドレスを使用(X-Forwarded-Forではない)    // 信頼されたプロキシの背後にある場合、quarkus.http.proxy.proxy-address-forwarding=trueを設定して    // getRemoteAddr()が実クライアントIPを返すようにします    return servletRequest.getRemoteAddr();  }}

Security Headers

java
@Providerpublic class SecurityHeadersFilter implements ContainerResponseFilter {
  @Override  public void filter(ContainerRequestContext request, ContainerResponseContext response) {    MultivaluedMap<String, Object> headers = response.getHeaders();
    // クリックジャッキング防止    headers.putSingle("X-Frame-Options", "DENY");
    // XSS保護    headers.putSingle("X-Content-Type-Options", "nosniff");    headers.putSingle("X-XSS-Protection", "1; mode=block");
    // HSTS    headers.putSingle("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
    // CSP — script-src用の'unsafe-inline'は避けてください。XSS保護を無効化します。    // 代わりにnoncesまたはhashesを使用します。CSSフレームワークが必要な場合、    // style-srcの'unsafe-inline'は許容ですが、可能な場合はnoncesを優先してください。    headers.putSingle("Content-Security-Policy",        "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'");  }}

Audit Logging

java
@ApplicationScopedpublic class AuditService {  private static final Logger LOG = Logger.getLogger(AuditService.class);
  @Inject  SecurityIdentity securityIdentity;
  public void logAccess(String resource, String action) {    String user = securityIdentity.isAnonymous()        ? "anonymous"        : securityIdentity.getPrincipal().getName();
    LOG.infof("AUDIT: user=%s action=%s resource=%s timestamp=%s",        user, action, resource, Instant.now());  }}
// リソースでの使用@Path("/api/sensitive")public class SensitiveResource {  @Inject  AuditService auditService;
  @GET  @RolesAllowed("ADMIN")  public Response getData() {    auditService.logAccess("sensitive-data", "READ");    return Response.ok(data).build();  }}

Dependency Security Scanning

bash
# Mavenmvn org.owasp:dependency-check-maven:check
# Gradle./gradlew dependencyCheckAnalyze
# Quarkus拡張機能チェックquarkus extension list --installable

Best Practices

  • 本番環境では常にHTTPSを使用
  • ステートレス認証にはJWTまたはOIDCを有効化
  • 宣言的認可に@RolesAllowedを使用
  • Bean Validationで全入力検証
  • BCryptでパスワードハッシュ化(プレーンテキスト厳禁)
  • VaultまたはLambda環境変数でシークレット保存
  • SQLインジェクション防止にパラメータ化クエリを使用
  • 全レスポンスにセキュリティヘッダー追加
  • 公開エンドポイントにレート制限実装
  • 機密操作を監査ログに記録
  • 依存関係を最新に保ちCVEスキャン実施
  • プログラム的チェックにSecurityIdentityを使用
  • 適切なCORSポリシー設定
  • 認証・認可経路をテスト

Source and attribution

Source:affaan-m/ECCindocs/ja-JP/skills/quarkus-securityat commitef648e0

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal