From b69075cbacfa543c180a5a7272f6a1db8e953b5a433b860dc4b2ac8ef798cae0 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Thu, 15 Jan 2026 15:24:25 +0300 Subject: [PATCH] 15 01 25 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавил мелких доп проверок (Все тесты тесты проходят) --- .../main/java/blockchain/BchBlockEntry.java | 39 ++++++++++++++++++- .../blockchain/body/BodyRecordParser.java | 6 ++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/shine-server-blockchain/src/main/java/blockchain/BchBlockEntry.java b/shine-server-blockchain/src/main/java/blockchain/BchBlockEntry.java index ce2c2bc..c565102 100644 --- a/shine-server-blockchain/src/main/java/blockchain/BchBlockEntry.java +++ b/shine-server-blockchain/src/main/java/blockchain/BchBlockEntry.java @@ -37,6 +37,18 @@ public final class BchBlockEntry { public static final int SIGNATURE_LEN = 64; public static final int HASH_LEN = 32; + /** + * Максимальный допустимый размер блока (preimage+signature), чтобы не уложить сервер по памяти/диску. + * 4 МБ — нормальный “потолок” под тексты/метаданные, и при этом защищает от мусора/атаки. + */ + public static final int MAX_BLOCK_FULL_BYTES = 4 * 1024 * 1024; + + /** + * Насколько блок может “обгонять” текущее время (защита от кривых часов/вбросов). + * Если timestamp больше now + 60 сек — блок считаем неверным. + */ + public static final long MAX_FUTURE_SECONDS = 60; + /** Размер фиксированного RAW-заголовка без body */ public static final int RAW_HEADER_SIZE = 32 // prevHash32 @@ -80,6 +92,9 @@ public final class BchBlockEntry { if (fullBytes.length < RAW_HEADER_SIZE + SIGNATURE_LEN) { throw new IllegalArgumentException("Block too short"); } + if (fullBytes.length > MAX_BLOCK_FULL_BYTES) { + throw new IllegalArgumentException("Block too large: " + fullBytes.length + " > " + MAX_BLOCK_FULL_BYTES); + } ByteBuffer bb = ByteBuffer.wrap(fullBytes).order(ByteOrder.BIG_ENDIAN); @@ -93,10 +108,19 @@ public final class BchBlockEntry { if (blockSize + SIGNATURE_LEN != fullBytes.length) { throw new IllegalArgumentException("blockSize mismatch: blockSize=" + blockSize + " fullLen=" + fullBytes.length); } + if (blockSize + SIGNATURE_LEN > MAX_BLOCK_FULL_BYTES) { + throw new IllegalArgumentException("Block too large by blockSize: " + (blockSize + SIGNATURE_LEN) + " > " + MAX_BLOCK_FULL_BYTES); + } this.blockNumber = bb.getInt(); this.timestamp = bb.getLong(); + // запрет “в будущее” больше чем на 1 минуту + long now = Instant.now().getEpochSecond(); + if (this.timestamp > now + MAX_FUTURE_SECONDS) { + throw new IllegalArgumentException("timestamp is too far in future: ts=" + this.timestamp + " now=" + now + " maxFutureSec=" + MAX_FUTURE_SECONDS); + } + this.type = bb.getShort(); this.subType = bb.getShort(); this.version = bb.getShort(); @@ -116,7 +140,7 @@ public final class BchBlockEntry { // hash32 = sha256(preimage) this.hash32 = BchCryptoVerifier.sha256(preimage); - // parse body по header.type/subType/version + // parse body по header.type/subType/version + ОБЯЗАТЕЛЬНЫЙ check() this.body = BodyRecordParser.parse(this.type, this.subType, this.version, this.bodyBytes); this.fullBytes = Arrays.copyOf(fullBytes, fullBytes.length); @@ -147,6 +171,12 @@ public final class BchBlockEntry { if (prevHash32.length != 32) throw new IllegalArgumentException("prevHash32 != 32"); if (signature64.length != SIGNATURE_LEN) throw new IllegalArgumentException("signature64 != 64"); + // запрет “в будущее” больше чем на 1 минуту + long now = Instant.now().getEpochSecond(); + if (timestamp > now + MAX_FUTURE_SECONDS) { + throw new IllegalArgumentException("timestamp is too far in future: ts=" + timestamp + " now=" + now + " maxFutureSec=" + MAX_FUTURE_SECONDS); + } + this.prevHash32 = Arrays.copyOf(prevHash32, 32); this.blockNumber = blockNumber; this.timestamp = timestamp; @@ -158,7 +188,12 @@ public final class BchBlockEntry { this.blockSize = RAW_HEADER_SIZE + this.bodyBytes.length; - // parse body по header + int fullLen = this.blockSize + SIGNATURE_LEN; + if (fullLen > MAX_BLOCK_FULL_BYTES) { + throw new IllegalArgumentException("Block too large: " + fullLen + " > " + MAX_BLOCK_FULL_BYTES); + } + + // parse body по header + ОБЯЗАТЕЛЬНЫЙ check() this.body = BodyRecordParser.parse(this.type, this.subType, this.version, this.bodyBytes); // build preimage diff --git a/shine-server-blockchain/src/main/java/blockchain/body/BodyRecordParser.java b/shine-server-blockchain/src/main/java/blockchain/body/BodyRecordParser.java index 8c26114..355cae3 100644 --- a/shine-server-blockchain/src/main/java/blockchain/body/BodyRecordParser.java +++ b/shine-server-blockchain/src/main/java/blockchain/body/BodyRecordParser.java @@ -17,7 +17,7 @@ public final class BodyRecordParser { // ключ = (type<<16)|version (как раньше по смыслу), но берём из HEADER int key = (t << 16) | v; - return switch (key) { + BodyRecord r = switch (key) { case HeaderBody.KEY -> new HeaderBody(subType, version, bodyBytes); case TextBody.KEY -> new TextBody(subType, version, bodyBytes); case ReactionBody.KEY -> new ReactionBody(subType, version, bodyBytes); @@ -28,5 +28,9 @@ public final class BodyRecordParser { t, v, (subType & 0xFFFF) )); }; + + // 1) “построили” объект + // 2) ОБЯЗАТЕЛЬНО прогнали валидацию + return r.check(); } } \ No newline at end of file