diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e9fd193..5b28c89f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Savedata corruption defense (tier 1): bounded decompression in nullcomp prevents OOM from crafted payloads, bounds-checked delta patching prevents buffer overflows, compressed payload size limits (512KB) and decompressed size limits (1MB) reject oversized saves, rotating savedata backups (3 slots, 30-minute interval) provide recovery points +- Savedata corruption defense (tier 2): SHA-256 checksum on decompressed savedata verified on every load, atomic DB transactions wrapping character data + house data + hash + backup in a single commit, per-character save mutex preventing concurrent save races +- Database migration `0007_savedata_integrity` (rotating backup table + integrity checksum column) - Tests for `logoutPlayer`, `saveAllCharacterData`, and transit message handlers - Alliance `scanAllianceWithGuilds` test for missing guild (nil return from GetByID) - Handler dispatch table test verifying all expected packet IDs are mapped diff --git a/server/migrations/sql/0007_savedata_backups.sql b/server/migrations/sql/0007_savedata_integrity.sql similarity index 50% rename from server/migrations/sql/0007_savedata_backups.sql rename to server/migrations/sql/0007_savedata_integrity.sql index 984822a7f..ff557090f 100644 --- a/server/migrations/sql/0007_savedata_backups.sql +++ b/server/migrations/sql/0007_savedata_integrity.sql @@ -1,4 +1,6 @@ --- Rotating savedata backup table (3 slots per character, time-gated). +-- Savedata integrity protections: rotating backups + checksum verification. + +-- Rotating backup table (3 slots per character, time-gated). -- Prevents permanent data loss from save corruption by keeping recent snapshots. CREATE TABLE IF NOT EXISTS savedata_backups ( char_id INTEGER NOT NULL REFERENCES characters(id) ON DELETE CASCADE, @@ -7,3 +9,8 @@ CREATE TABLE IF NOT EXISTS savedata_backups ( saved_at TIMESTAMPTZ NOT NULL DEFAULT now(), PRIMARY KEY (char_id, slot) ); + +-- SHA-256 checksum column for savedata integrity verification. +-- Stored as 32 raw bytes (not hex). NULL means no hash computed yet +-- (backwards-compatible with existing data). +ALTER TABLE characters ADD COLUMN IF NOT EXISTS savedata_hash BYTEA; diff --git a/server/migrations/sql/0008_savedata_hash.sql b/server/migrations/sql/0008_savedata_hash.sql deleted file mode 100644 index 04c19856b..000000000 --- a/server/migrations/sql/0008_savedata_hash.sql +++ /dev/null @@ -1,4 +0,0 @@ --- Add SHA-256 checksum column for savedata integrity verification. --- Stored as 32 raw bytes (not hex). NULL means no hash computed yet --- (backwards-compatible with existing data). -ALTER TABLE characters ADD COLUMN IF NOT EXISTS savedata_hash BYTEA;