Unofficial site, not affiliated with modrinth.com.What is this?
Все версииOPShield 1.9.0

OPShield 1.9.0

Release1 мес. назад

Список изменений

[1.9.0] - 2026-05-15 — Async Auth, Session Timeout & Security Hardening

🐛 Bug Fixes

FIX [Critical] — PBKDF2 verification was running on the main server thread

  • Root cause: handlePrivilegeCommand() called PasswordHasher.verify() synchronously during onCommand(), which executes on the main thread. With 120,000 PBKDF2 iterations, each failed attempt caused a measurable TPS drop (~50–200 ms). Under coordinated brute-force from multiple clients this could stall the server tick loop.
  • Fix: Password verification is now dispatched via Bukkit.getScheduler().runTaskAsynchronously(). The result is delivered back to the main thread via runTask() for all Bukkit API side-effects. Zero blocking on the main thread.
  • New status field: /opshield status now shows pending verification count (0/4).

FIX [Critical] — No session timeout after successful authentication

  • Root cause: After a correct password was accepted, no state was stored. Every subsequent /op or /deop required re-entering the password. In practice admins worked around this by keeping a note of the password nearby, which is a security anti-pattern.
  • Fix: A ConcurrentHashMap<UUID, Long> (authenticatedSessions) tracks expiry epoch-ms per player. After the first successful auth within a session window, password entry is skipped.
  • Config key: security.session_timeout_minutes (default 30; set 0 to disable).
  • Session lifecycle: granted on successful auth → cleared on player quit (PlayerQuitEvent) → cleared on manual /opshield unlock → purged by cleanup task.

FIX [High] — No global rate limit on PBKDF2 operations

  • Root cause: A coordinated attack with many accounts spamming /op <name> <guess> queued unbounded async hashing tasks, saturating CPU cores.
  • Fix: AtomicInteger globalAuthCount caps server-wide concurrent PBKDF2 operations at MAX_CONCURRENT_AUTH = 4. Requests over the cap receive auth_server_busy and are dropped without queuing.
  • Per-sender dedup: Map<String, AtomicInteger> authPending prevents the same player from queuing more than one verification at a time. Excess requests receive auth_pending.

FIX [High] — cleanupRuntimeState() ran on the main thread

  • Root cause: Bukkit.getScheduler().runTaskTimer() was used instead of runTaskTimerAsynchronously(). The cleanup method holds synchronized blocks on LockoutRecord objects; under load this briefly stalled the tick.
  • Fix: Scheduler call changed to runTaskTimerAsynchronously(). All collections involved are ConcurrentHashMap/ConcurrentLinkedQueue — safe for async access.

FIX [Medium] — Shutdown race condition between async periodic flush and onDisable sync flush

  • Root cause: onDisable() called flushPersistentDataSync() while the 200-tick async flush task could be mid-execution. Although compareAndSet prevented double saves, the two threads could both have a reference to dataFile and compete on the same YamlConfiguration serialisation.
  • Fix: onDisable() now cancels both async task IDs (periodicFlushTaskId, cleanupTaskId) before calling flushPersistentDataSync(), ensuring only one flush can run at shutdown.

FIX [Medium] — ShadowBanManager used HashMap for command→message mapping

  • Root cause: HashMap has undefined iteration order. Commands like deop op, ban-ip, or give op could match the wrong message key depending on JVM run, leading to non-deterministic fake responses.
  • Fix: Replaced with LinkedHashMap preserving explicit insertion order. More specific and longer tokens (e.g. "inventory", "teleport", "deop") are declared before shorter substrings ("clear", "tp", "op") so prefix-matching is always predictable.

FIX [Low] — config-version field was written but never read

  • Root cause: config-version: 2 was introduced in v1.8.0 but reloadConfiguration() never checked it. There was no warning when admins ran an old config against a new build.
  • Fix: reloadConfiguration() now compares config-version against EXPECTED_CONFIG_VERSION = 3. It logs a warning if the file is outdated (new options missing → defaults used) or from a newer build (unknown keys may be ignored). The version is bumped in-place so the warning fires only once.

FIX [Low] — PlayerQuitEvent not handled; stale session UUIDs accumulated

  • Root cause: authenticatedSessions was new in this version, but without a quit handler a UUID could remain in the map until the cleanup task ran, creating a window where a reconnecting player could bypass auth.
  • Fix: @EventHandler onPlayerQuit() immediately removes the UUID from authenticatedSessions on disconnect.

✨ New Features

Authenticated Session Tokens

  • First successful /op or /deop authentication grants a timed session.
  • Subsequent privilege commands within the window skip password entry.
  • Session duration: security.session_timeout_minutes (default 30; 0 = always require password).
  • Sessions are cleared on disconnect, manual unlock, or config reload.
  • New language keys: auth_session_granted, auth_pending, auth_server_busy.

Async PBKDF2 Authentication Pipeline

  • All password verifications now run asynchronously.
  • Bukkit API side-effects (setOp, messages, broadcasts) still execute on the main thread via callback.
  • Extracted completePrivilegeCommand() to consolidate post-auth logic (hash upgrade, session grant, lockout clear, setOp, broadcast).

Enhanced /opshield status Output

Two new status lines:

Active auth sessions: 2
Pending PBKDF2 verifications: 0/4
Session timeout: 30 min

🔧 Configuration Changes

KeyChangeDefault
config-versionBumped 233
security.session_timeout_minutesNEW — auth session TTL in minutes; 0 = disabled30

No existing keys were removed or renamed. Old config-version: 2 files are read normally with a one-time console warning.


🌍 Language Files

Three new message keys added to all language files (en, vn, ru):

KeyPurpose
auth_pendingSender already has a PBKDF2 verification in flight
auth_server_busyServer-wide auth cap reached
auth_session_grantedConfirmation message shown after session is created

📊 Code Quality Metrics

Metricv1.8.0v1.9.0
PBKDF2 on main thread✅ yes❌ no (async)
Session timeout
Global auth rate limit✅ (cap=4)
Per-sender auth dedup
Cleanup on main thread✅ yes❌ no (async)
Shutdown race condition✅ present❌ fixed
ShadowBan match ordernon-deterministicdeterministic
config-version enforced
PlayerQuitEvent handled

📝 Migration Notes from v1.8.0

  1. No breaking changes — existing config.yml and data.yml are fully compatible.
  2. New config key — add security.session_timeout_minutes: 30 (or let the default apply on first reload).
  3. config-version is auto-bumped from 23 on first reload; a one-time warning will appear in console.
  4. Permission setup is unchanged from v1.8.0.

🔮 Planned for v1.10.0

  • Extract AutoPunishmentManager — move all ban/kick/firewall logic out of OPShield.java
  • Extract IpLimitManager — move IP tracking and flagging
  • Extract CommandRestrictionManager
  • Unit tests for LockoutManager and ShadowBanManager
  • Consider Argon2id as an optional stronger hashing algorithm

Метаданные

Канал релиза

Release

Номер версии

1.9.0

Загрузчики

Bukkit
Paper
Purpur
Spigot

Версии игры

1.21–1.21.11

Загрузок

9

Дата публикации

1 мес. назад

Загрузил

ID версии

Главная