s011: Deny-by-Default für Berechtigungen
- Englischer technischer Begriff
- Deny by Default Authorization
- Priorität
- 10/10
- Warum wichtig
- Sichere Systeme erlauben nur explizit freigegebene Aktionen. Alles andere wird abgelehnt.
- Typischer Fehler
- Neue Endpunkte fallen versehentlich durch eine permissive Default-Regel.
- Enterprise-Einordnung
- Zentral für APIs, Admin-Funktionen, Mandantenfähigkeit und Governance.
Ausgangspunkt-Code
public final class PermissionGuard {
public boolean allowed(User user, String permission) {
return true; // während Entwicklung vergessen
}
}Ziel-Code
import java.util.Set;
public final class PermissionGuard {
public void require(User user, String permission) {
if (!user.permissions().contains(permission)) {
throw new AccessDeniedException(permission);
}
}
}
record User(String subject, Set<String> permissions) {}
final class AccessDeniedException extends RuntimeException { AccessDeniedException(String p){ super("missing permission: " + p); } }s012: Mandant aus Token statt Request lesen
- Englischer technischer Begriff
- Tenant from Token not Request
- Priorität
- 10/10
- Warum wichtig
- Der Mandant ist eine Sicherheitsgrenze. Er darf nicht aus frei manipulierbaren Request-Parametern kommen.
- Typischer Fehler
- tenantId wird aus Query-Parametern übernommen.
- Enterprise-Einordnung
- Relevant für SaaS, Shared Databases, Partner-Portale und Backoffice-Sichten.
Ausgangspunkt-Code
public final class OrderQueryService {
public List<Order> find(String tenantIdFromRequest) {
return database.findByTenant(tenantIdFromRequest);
}
private final OrderDatabase database = new OrderDatabase();
}
record Order(String id) {}
class OrderDatabase { List<Order> findByTenant(String tenant){ return List.of(new Order("o-1")); } }Ziel-Code
import java.util.List;
public final class OrderQueryService {
private final OrderDatabase database = new OrderDatabase();
public List<Order> find(SecurityPrincipal principal) {
return database.findByTenant(principal.tenantId());
}
}
record SecurityPrincipal(String subject, String tenantId) {}
record Order(String id) {}
class OrderDatabase { List<Order> findByTenant(String tenant){ return List.of(new Order("o-1")); } }s013: BOLA/IDOR bei Ressourcen prüfen
- Englischer technischer Begriff
- Broken Object Level Authorization
- Priorität
- 10/10
- Warum wichtig
- Objektzugriffe brauchen Besitz- oder Mandantenprüfung. Rollen allein reichen nicht.
- Typischer Fehler
- Jeder Benutzer mit Rolle USER kann jedes Objekt per ID lesen.
- Enterprise-Einordnung
- Kritisch für Order, Invoice, Contract, Document und Customer APIs.
Ausgangspunkt-Code
public final class InvoiceController {
public Invoice get(String invoiceId, User user) {
if (user.hasRole("USER")) return repo.find(invoiceId);
throw new SecurityException();
}
private final InvoiceRepository repo = new InvoiceRepository();
}Ziel-Code
public final class InvoiceController {
private final InvoiceRepository repo = new InvoiceRepository();
public Invoice get(String invoiceId, User user) {
Invoice invoice = repo.find(invoiceId);
if (!invoice.tenantId().equals(user.tenantId())) throw new SecurityException("tenant mismatch");
return invoice;
}
}
record User(String tenantId) { boolean hasRole(String role){ return true; } }
record Invoice(String id, String tenantId) {}
class InvoiceRepository { Invoice find(String id){ return new Invoice(id, "tenant-a"); } }s014: Rollen in fachliche Permissions übersetzen
- Englischer technischer Begriff
- Role to Permission Mapping
- Priorität
- 9/10
- Warum wichtig
- Rollen sind organisatorisch, Permissions sind fachlich-technisch prüfbar. Diese Trennung reduziert Wildwuchs.
- Typischer Fehler
- Im Code werden überall konkrete Rollennamen geprüft.
- Enterprise-Einordnung
- Relevant für Audits, Admin-Portale und fein granulare Enterprise-Berechtigungen.
Ausgangspunkt-Code
if (user.role().equals("SUPER_ADMIN") || user.role().equals("ORDER_MANAGER")) {
order.cancel();
}Ziel-Code
enum Permission { ORDER_CANCEL, ORDER_READ, INVOICE_READ }
public final class RolePermissionMapper {
public java.util.Set<Permission> permissionsFor(String role) {
return switch (role) {
case "ORDER_MANAGER" -> java.util.Set.of(Permission.ORDER_CANCEL, Permission.ORDER_READ);
case "AUDITOR" -> java.util.Set.of(Permission.ORDER_READ, Permission.INVOICE_READ);
default -> java.util.Set.of();
};
}
}s015: Method Security zentral erzwingen
- Englischer technischer Begriff
- Central Method Security
- Priorität
- 9/10
- Warum wichtig
- Sicherheitsprüfungen müssen dort sitzen, wo fachliche Aktionen ausgelöst werden, nicht nur am Controller.
- Typischer Fehler
- Batch, REST und Messaging umgehen unterschiedliche Controller-Prüfungen.
- Enterprise-Einordnung
- Wichtig für Mehrkanal-Systeme: REST, Batch, JMS, Scheduler und Partner-Portale.
Ausgangspunkt-Code
public final class CancelOrderService {
public void cancel(String orderId) {
repository.cancel(orderId); // keine fachliche Berechtigungsprüfung
}
}Ziel-Code
public final class CancelOrderService {
private final AuthorizationPolicy authorization;
private final OrderRepository repository;
public void cancel(SecurityPrincipal principal, String orderId) {
authorization.require(principal, "order:cancel");
repository.cancel(orderId);
}
public CancelOrderService(AuthorizationPolicy authorization, OrderRepository repository) {
this.authorization = authorization; this.repository = repository;
}
}
interface AuthorizationPolicy { void require(SecurityPrincipal p, String permission); }
interface OrderRepository { void cancel(String id); }
record SecurityPrincipal(String subject) {}s016: Admin-Rechte fachlich schneiden
- Englischer technischer Begriff
- Least Privilege Admin Scope
- Priorität
- 8/10
- Warum wichtig
- Admin ist selten eine einzige Berechtigung. Fachlich geschnittene Admin-Rechte reduzieren Schaden bei Fehlkonfiguration.
- Typischer Fehler
- Ein globales ADMIN darf Benutzer, Preise, Zahlungen und Logs ändern.
- Enterprise-Einordnung
- Relevant für große Backoffice-Landschaften mit mehreren Fachbereichen.
Ausgangspunkt-Code
boolean allowed = user.roles().contains("ADMIN");Ziel-Code
import java.util.Set;
public final class AdminScopePolicy {
public void requirePriceAdmin(Principal p) {
if (!p.permissions().contains("price:write")) throw new SecurityException("price admin required");
}
}
record Principal(String subject, Set<String> permissions) {}s017: Permission Cache invalidieren
- Englischer technischer Begriff
- Permission Cache Invalidation
- Priorität
- 7/10
- Warum wichtig
- Berechtigungsänderungen müssen zeitnah wirken. Sonst bleiben entfernte Rechte bis zum Cache-Ablauf aktiv.
- Typischer Fehler
- Permissions werden für Stunden gecacht und nie aktiv invalidiert.
- Enterprise-Einordnung
- Relevant für Admin-Portale, HR-getriebene Rollenwechsel und Incident-Reaktionen.
Ausgangspunkt-Code
public final class PermissionCache {
private final Map<String, Set<String>> cache = new HashMap<>();
public Set<String> permissions(String user) { return cache.computeIfAbsent(user, this::load); }
private Set<String> load(String user) { return Set.of("order:write"); }
}Ziel-Code
import java.time.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public final class ExpiringPermissionCache {
private final Map<String, Entry> cache = new ConcurrentHashMap<>();
public Set<String> permissions(String user) {
Entry entry = cache.get(user);
if (entry == null || entry.validUntil().isBefore(Instant.now())) {
entry = new Entry(load(user), Instant.now().plus(Duration.ofMinutes(5)));
cache.put(user, entry);
}
return entry.permissions();
}
public void invalidate(String user) { cache.remove(user); }
private Set<String> load(String user) { return Set.of("order:read"); }
record Entry(Set<String> permissions, Instant validUntil) {}
}s018: UI-Prüfung nie als Security betrachten
- Englischer technischer Begriff
- UI Authorization is not Security
- Priorität
- 9/10
- Warum wichtig
- Buttons verstecken ist Usability, keine Sicherheit. Der Server muss jede Aktion selbst prüfen.
- Typischer Fehler
- Ein gefährlicher Endpoint verlässt sich darauf, dass der Button nicht angezeigt wird.
- Enterprise-Einordnung
- Relevant für Angular/React/Thymeleaf/JSP-Portale und Backoffice-Funktionen.
Ausgangspunkt-Code
public final class PriceController {
public void updatePrice(String sku, BigDecimal price) {
priceService.update(sku, price); // UI hat Button versteckt, API prüft nicht
}
}Ziel-Code
import java.math.BigDecimal;
public final class PriceController {
private final AuthorizationPolicy auth;
private final PriceService priceService;
public void updatePrice(Principal principal, String sku, BigDecimal price) {
auth.require(principal, "price:write");
priceService.update(sku, price);
}
public PriceController(AuthorizationPolicy auth, PriceService priceService) { this.auth = auth; this.priceService = priceService; }
}
interface AuthorizationPolicy { void require(Principal p, String permission); }
interface PriceService { void update(String sku, BigDecimal price); }
record Principal(String subject) {}s019: Zugriffsverweigerung auditieren
- Englischer technischer Begriff
- Audit Access Denied
- Priorität
- 8/10
- Warum wichtig
- Abgelehnte Zugriffe zeigen Fehlkonfigurationen, Angriffe oder Rollenprobleme. Ohne Audit fehlt die Spur.
- Typischer Fehler
- AccessDenied wird nur als HTTP 403 beantwortet und nirgends protokolliert.
- Enterprise-Einordnung
- Wichtig für Compliance, SOC, Incident-Analyse und Partner-Streitfälle.
Ausgangspunkt-Code
catch (AccessDeniedException ex) {
return Response.status(403);
}Ziel-Code
public final class AccessDeniedHandler {
private final AuditSink audit;
public Response handle(Principal p, String action, RuntimeException ex) {
audit.record(new AuditEvent("ACCESS_DENIED", p.subject(), action, java.time.Instant.now()));
return Response.status(403, "access denied");
}
}
interface AuditSink { void record(AuditEvent event); }
record AuditEvent(String type, String subject, String action, java.time.Instant at) {}
record Principal(String subject) {}
record Response(int status, String body) { static Response status(int s, String b){ return new Response(s,b); } }s020: Berechtigungsregeln testbar halten
- Englischer technischer Begriff
- Testable Authorization Policy
- Priorität
- 8/10
- Warum wichtig
- Policies müssen in Unit-Tests ohne Webcontainer prüfbar sein. Sonst verlagert sich Security in schwer testbare Infrastruktur.
- Typischer Fehler
- Regeln sind über mehrere Controller, Filter und UI-Flags verteilt.
- Enterprise-Einordnung
- Relevant für große Teams und langlebige Berechtigungsmodelle.
Ausgangspunkt-Code
public final class OrderController {
boolean allowed(User u) { return u.role().equals("ADMIN") || u.name().equals("batch"); }
}Ziel-Code
public final class OrderPolicy {
public boolean canCancel(User user, Order order) {
return user.permissions().contains("order:cancel") && user.tenantId().equals(order.tenantId());
}
}
record User(String tenantId, java.util.Set<String> permissions) {}
record Order(String id, String tenantId) {}