跳至主要內容
進階

Banlist

深入了解 Bitcoin Core 的節點封禁機制,如何管理惡意或不良行為的對等節點。

10 分鐘

什麼是 Banlist?

Banlist 是 Bitcoin Core 維護的封禁節點清單,用於記錄因惡意行為或協議違規而被禁止連接的對等節點。 這是節點自我保護機制的重要組成部分,防止資源被惡意節點濫用。

封禁的目的

防止攻擊

  • • 阻止 DoS 攻擊
  • • 防止垃圾交易廣播
  • • 抵禦 Eclipse 攻擊

節省資源

  • • 避免處理無效數據
  • • 保護頻寬
  • • 維護連接品質

封禁 vs 斷開連接

兩者的區別

特性 Disconnect Ban
持久性 臨時 持久(儲存到磁碟)
重新連接 可以立即重連 封禁期間不可連接
預設時長 N/A 24 小時
適用情況 輕微問題 嚴重違規

Discourage(勸阻)

Bitcoin Core 26.0 引入了 discourage 機制,比 ban 更溫和。被勸阻的節點可以連接, 但會被優先斷開以騰出連接槽位給其他節點。

# 查看被勸阻的節點
bitcoin-cli listbanned

# 在輸出中,banned_until = 0 表示 discourage 而非 ban

封禁原因

自動封禁

Bitcoin Core 會自動封禁違反協議規則或行為異常的節點:

  • !
    無效區塊

    發送違反共識規則的區塊

  • !
    無效交易

    發送違反共識規則的交易

  • !
    DoS 行為

    過多的無效請求或垃圾數據

  • !
    協議違規

    不遵循 P2P 協議的行為

DoS 評分系統

// Bitcoin Core 的 DoS 評分機制
interface PeerMisbehavior {
  peerId: number;
  score: number;
  threshold: number;  // 預設 100
}

// 違規行為的懲罰分數示例
const MISBEHAVIOR_SCORES = {
  // 嚴重違規 - 立即封禁
  INVALID_BLOCK: 100,
  INVALID_TX_CONSENSUS: 100,

  // 中等違規
  INVALID_TX_POLICY: 10,
  DUPLICATE_VERSION: 10,
  UNKNOWN_MESSAGE: 1,

  // 輕微違規
  TIMEOUT: 1,
};

class PeerManager {
  private misbehavior: Map = new Map();
  private readonly banThreshold = 100;

  // 記錄違規行為
  misbehaving(peerId: number, howmuch: number, reason: string): void {
    const current = this.misbehavior.get(peerId) || 0;
    const newScore = current + howmuch;

    console.log(`Peer ${peerId} misbehaving: ${reason} (+${howmuch})`);
    this.misbehavior.set(peerId, newScore);

    if (newScore >= this.banThreshold) {
      this.banPeer(peerId);
    }
  }

  private banPeer(peerId: number): void {
    const peer = this.getPeer(peerId);
    if (peer) {
      this.addToBanlist(peer.address, 24 * 60 * 60);  // 24 小時
      this.disconnectPeer(peerId);
    }
  }
}

管理封禁清單

CLI 命令

# 查看所有封禁的節點
bitcoin-cli listbanned

# 輸出示例
[
  {
    "address": "192.168.1.100/32",
    "ban_created": 1703980800,
    "banned_until": 1704067200,
    "ban_duration": 86400,
    "time_remaining": 43200,
    "ban_reason": "manually added"
  }
]

# 手動封禁 IP
bitcoin-cli setban "192.168.1.100" "add" 86400  # 24 小時

# 封禁 IP 範圍(CIDR)
bitcoin-cli setban "192.168.1.0/24" "add" 86400

# 解除封禁
bitcoin-cli setban "192.168.1.100" "remove"

# 清除所有封禁
bitcoin-cli clearbanned

# 斷開特定節點(不封禁)
bitcoin-cli disconnectnode "192.168.1.100:8333"

Banlist 檔案

# 封禁清單儲存位置
~/.bitcoin/banlist.json

# 檔案格式(JSON)
{
  "banned_nets": [
    {
      "version": 1,
      "ban_created": 1703980800,
      "banned_until": 1704067200,
      "address": "192.168.1.100/32"
    }
  ]
}

# 舊版本使用 banlist.dat(二進制格式)

注意: 封禁是基於 IP 地址而非節點 ID。如果惡意節點使用 VPN 或動態 IP, 封禁可能無法完全阻止它們。

實現細節

封禁儲存

interface BanEntry {
  subnet: string;      // IP 或 CIDR
  banCreated: number;  // Unix timestamp
  bannedUntil: number; // Unix timestamp(0 = discourage)
  banReason: string;
}

class BanMan {
  private banlist: Map = new Map();
  private dirty: boolean = false;

  // 檢查是否被封禁
  isBanned(addr: string): boolean {
    const now = Date.now() / 1000;

    for (const [subnet, entry] of this.banlist) {
      if (this.matchSubnet(addr, subnet)) {
        if (entry.bannedUntil > now) {
          return true;
        }
        // 封禁已過期,稍後清理
      }
    }

    return false;
  }

  // 檢查是否被勸阻
  isDiscouraged(addr: string): boolean {
    for (const [subnet, entry] of this.banlist) {
      if (this.matchSubnet(addr, subnet)) {
        // bannedUntil = 0 表示 discourage
        if (entry.bannedUntil === 0) {
          return true;
        }
      }
    }
    return false;
  }

  // 添加封禁
  ban(subnet: string, durationSeconds: number, reason: string): void {
    const now = Date.now() / 1000;
    this.banlist.set(subnet, {
      subnet,
      banCreated: now,
      bannedUntil: now + durationSeconds,
      banReason: reason,
    });
    this.dirty = true;
  }

  // 勸阻(不完全封禁)
  discourage(subnet: string): void {
    const now = Date.now() / 1000;
    this.banlist.set(subnet, {
      subnet,
      banCreated: now,
      bannedUntil: 0,  // 0 表示 discourage
      banReason: 'discouraged',
    });
    this.dirty = true;
  }

  // 儲存到磁碟
  async dumpBanlist(): Promise {
    if (!this.dirty) return;

    const data = {
      banned_nets: Array.from(this.banlist.values()),
    };

    await writeFile('banlist.json', JSON.stringify(data, null, 2));
    this.dirty = false;
  }
}

配置選項

# bitcoin.conf

# 設定封禁時長(秒),預設 86400(24 小時)
bantime=86400

# 禁止自動封禁(不建議)
# 只會 discourage 而不會 ban
# noban=1

# 使用白名單(繞過封禁檢查)
whitelist=192.168.1.0/24

# 白名單權限
whitebind=127.0.0.1:8333
whitelistrelay=1
whitelistforcerelay=1

白名單

白名單中的節點不會被自動封禁或斷開連接,即使它們的行為觸發了 DoS 保護。

# 白名單選項
-whitelist=           # 添加到白名單
-whitebind=         # 綁定地址並將連接加入白名單

# 白名單權限
-whitelistrelay           # 允許中繼來自白名單的交易
-whitelistforcerelay      # 強制中繼(即使不符合 mempool 政策)

最佳實踐

✓ 建議

  • • 讓自動封禁機制正常運作
  • • 定期檢查封禁清單
  • • 為可信節點設定白名單
  • • 監控異常連接模式

✗ 避免

  • • 禁用自動封禁
  • • 過度使用手動封禁
  • • 封禁大範圍 IP
  • • 忽略封禁日誌

總結

  • 封禁機制:防止惡意節點消耗資源
  • DoS 評分:累積違規行為觸發自動封禁
  • Discourage:溫和的處理方式,優先斷開而非封禁
  • 注意:封禁基於 IP,無法阻止使用 VPN 的攻擊者
已複製連結
已複製到剪貼簿