quarkus-security
Проверка безопасности приложения Quarkus
Что получите
Рекомендации и примеры изменений кода и настроек Quarkus для выбранных механизмов защиты.
Что передать
Код и настройки приложения Quarkus, схема входа пользователей и ожидаемые права доступа.
Проверен состав файлов. Запуск не тестировался. Что проверено
Оценки пользователей
Как использовать
Помогает разработчикам Quarkus добавлять и проверять вход пользователей, права доступа и обработку пользовательских данных. Подходит также для работы с секретами, ограничением запросов и уязвимыми зависимостями.
- Укажите, что нужно добавить или проверить: вход через JWT или OIDC, роли, владение ресурсами либо проверку входных данных.
- Попросите рассмотреть связанные риски: запросы к базе, хранение паролей и секретов, доступ браузера к серверу и ограничения публичных запросов.
- Определите сценарии проверки входа и прав доступа, включая отказ пользователю без нужных полномочий.
Условия работы
- В рабочей среде требуется HTTPS. Пароли хранят как хеши BCrypt, секреты — в Vault или переменных окружения.
- Пример собственного фильтра входа содержит заглушку проверки токена; её нужно заменить реальной проверкой.
- Для ограничения запросов нельзя напрямую доверять переданному клиентом адресу в X-Forwarded-For. Используют фактический адрес соединения или подтверждённую идентичность; за доверенным прокси нужна соответствующая настройка.
Пример запроса после установки
Проверь сервис Quarkus с входом через OIDC: пользователи должны видеть только свои заказы, администраторы — все. Предложи изменения прав доступа и проверки входных данных.
Пример составлен по инструкции автора; результат запуска не проверялся.
Основание: инструкция закреплённой версииДля установки
Node.js, npm и Git для команды выше. Подключения и инструменты для работы скилла настраиваются отдельно.
Совместимые агенты
Совместимость указана для инструкций. Запуск в каждом приложении не тестировался; для веб-чата способ подключения может отличаться.
Файлы скилла
GitHubЧитать инструкцию SKILL.md
Исходный текст закреплённой версии.
---
name: quarkus-security
description: Quarkus Security best practices for authentication, authorization, JWT/OIDC, RBAC, input validation, CSRF, secrets management, and dependency security. Use when reviewing Quarkus authn/authz, JWT or OIDC, RBAC, validation, or secrets.
metadata:
origin: ECC
---
# Quarkus Security Review
Best practices for securing Quarkus applications with authentication, authorization, and input validation.
## When to Activate
- Adding authentication (JWT, OIDC, Basic Auth)
- Implementing authorization with @RolesAllowed or SecurityIdentity
- Validating user input (Bean Validation, custom validators)
- Configuring CORS or security headers
- Managing secrets (Vault, environment variables, config sources)
- Adding rate limiting or brute-force protection
- Scanning dependencies for CVEs
- Working with MicroProfile JWT or SmallRye JWT
## Authentication
### JWT Authentication
```java
// Resource protected with JWT
@Path("/api/protected")
@Authenticated
public 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.pem
mp.jwt.verify.issuer=https://auth.example.com
# OIDC
quarkus.oidc.auth-server-url=https://auth.example.com/realms/myrealm
quarkus.oidc.client-id=backend-service
quarkus.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);
// Reject immediately if header is absent or malformed
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) {
// Token validation logic
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) {
// Check ownership
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
@ApplicationScoped
public 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
// BAD: No validation
@POST
public Response createUser(UserDto dto) {
return Response.ok(userService.create(dto)).build();
}
// GOOD: Validated DTO
public 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}$");
}
}
// Usage
public record CreateUserDto(
@ValidUsername String username,
@NotBlank @Email String email
) {}
```
## SQL Injection Prevention
### Panache Active Record (Safe by Default)
```java
// GOOD: Parameterized queries with Panache
List<User> users = User.list("email = ?1 and active = ?2", email, true);
Optional<User> user = User.find("username", username).firstResultOptional();
// GOOD: Named parameters
List<User> users = User.list("email = :email and age > :minAge",
Parameters.with("email", email).and("minAge", 18));
```
### Native Queries (Use Parameters)
```java
// BAD: String concatenation
@Query(value = "SELECT * FROM users WHERE name = '" + name + "'", nativeQuery = true)
// GOOD: Parameterized native query
@Entity
public 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
@ApplicationScoped
public class PasswordService {
public String hash(String plainPassword) {
return BcryptUtil.bcryptHash(plainPassword);
}
public boolean verify(String plainPassword, String hashedPassword) {
return BcryptUtil.matches(plainPassword, hashedPassword);
}
}
// In service
@ApplicationScoped
public 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.properties
quarkus.http.cors=true
quarkus.http.cors.origins=https://app.example.com,https://admin.example.com
quarkus.http.cors.methods=GET,POST,PUT,DELETE
quarkus.http.cors.headers=accept,authorization,content-type,x-requested-with
quarkus.http.cors.exposed-headers=Content-Disposition
quarkus.http.cors.access-control-max-age=24H
quarkus.http.cors.access-control-allow-credentials=true
```
## Secrets Management
```properties
# application.properties - NO SECRETS HERE
# Use environment variables
quarkus.datasource.username=${DB_USER}
quarkus.datasource.password=${DB_PASSWORD}
quarkus.oidc.credentials.secret=${OIDC_CLIENT_SECRET}
# Or use Vault
quarkus.vault.url=https://vault.example.com
quarkus.vault.authentication.kubernetes.role=my-role
```
### HashiCorp Vault Integration
```java
@ApplicationScoped
public class SecretService {
@ConfigProperty(name = "api-key")
String apiKey; // Fetched from Vault
public String getSecret(String key) {
return ConfigProvider.getConfig().getValue(key, String.class);
}
}
```
## Rate Limiting
**Security Note**: Never use `X-Forwarded-For` directly — clients can spoof it.
Use the actual remote address from the servlet request, or an authenticated
identity (API key, JWT subject) when available.
```java
@ApplicationScoped
public 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)); // 100 requests per second
if (!limiter.tryAcquire()) {
requestContext.abortWith(
Response.status(429)
.entity(Map.of("error", "Too many requests"))
.build()
);
}
}
private String getClientIdentifier() {
// Use the container-provided remote address (not X-Forwarded-For).
// If behind a trusted proxy, configure quarkus.http.proxy.proxy-address-forwarding=true
// so getRemoteAddr() returns the real client IP.
return servletRequest.getRemoteAddr();
}
}
```
## Security Headers
```java
@Provider
public class SecurityHeadersFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext request, ContainerResponseContext response) {
MultivaluedMap<String, Object> headers = response.getHeaders();
// Prevent clickjacking
headers.putSingle("X-Frame-Options", "DENY");
// XSS protection
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 — avoid 'unsafe-inline' for script-src as it negates XSS protection;
// use nonces or hashes instead. 'unsafe-inline' for style-src is acceptable
// when CSS frameworks require it, but prefer nonces where possible.
headers.putSingle("Content-Security-Policy",
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'");
}
}
```
## Audit Logging
```java
@ApplicationScoped
public 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());
}
}
// Usage in resource
@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
# Maven
mvn org.owasp:dependency-check-maven:check
# Gradle
./gradlew dependencyCheckAnalyze
# Check Quarkus extensions
quarkus extension list --installable
```
## Best Practices
- Always use HTTPS in production
- Enable JWT or OIDC for stateless authentication
- Use `@RolesAllowed` for declarative authorization
- Validate all input with Bean Validation
- Hash passwords with BCrypt (never plaintext)
- Store secrets in Vault or environment variables
- Use parameterized queries to prevent SQL injection
- Add security headers to all responses
- Implement rate limiting for public endpoints
- Audit sensitive operations
- Keep dependencies updated and scan for CVEs
- Use SecurityIdentity for programmatic checks
- Set appropriate CORS policies
- Test authentication and authorization paths
Проверка и особенности
Автоматическая проверка источника и файлов
Автоматически проверены публичный репозиторий, лицензия, заголовок SKILL.md, состав текстового пакета и Git-хеш каждого файла. Пакеты с исполняемым кодом и срабатываниями применённых статических правил не включены. Ручной аудит всех инструкций и функциональный запуск не проводились. Внешние инструменты и сервисы не проверялись. Проверка не гарантирует безопасность.
У репозитория было 268,302 звёзд GitHub при проверке 2026-09-27. Порог добавления — строго больше 100 000. Звёзды относятся ко всему репозиторию, а не к качеству или установкам скилла.
Есть ссылки на внешние инструкции или инструменты: их содержимое и доступность могут изменяться отдельно от скилла.
- Проверка состава
- 27.09.2026
- Проверенная версия
e482e579415f- Файлы с кодом
- Не указаны в данных проверки
- Изменения на GitHub
- Нужна повторная проверкаПоследняя проверка: 11.10.2026
Репозиторий и лицензия
Это показатели всего репозитория, а не установки скилла. Снимок требует обновления.
- Последняя активность GitHub
- 11.10.2026
- Лицензия
- MIT
- Добавлен в каталог
- 27.09.2026
Полный текст лицензии
MIT License Copyright (c) 2026 Affaan Mustafa Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Ещё в этой категории
Все в категорииОбсуждение
ПравилаПользовались этим скиллом? Делитесь опытом без регистрации. Все комментарии проверяет модератор.
