Kapatmak için ESC'ye basın

Kripto Köprüleri Nasıl Çalışır ve Neden Hacklenir? 2026

Cross-chain köprüler ağlar arasında doğrudan kurulan gizli bir tünel değil, iki izole akıllı sözleşme ve bir off-chain röleden (relay node) oluşan sıradan bir finansal aracıdır. Blokzincirler birbirleriyle doğrudan haberleşmeyi bilmezler: Ethereum, Solana’nın içinde ne dönüp bittiğinden habersizdir; Bitcoin ise Arbitrum’un varlığından bile bihaberdir.

Bir kullanıcı Ethereum ağından Arbitrum’a 10 ETH aktardığında, köprü aslında bu token’ları fiziken bir yerden bir yere taşımaz. Kaynak ağdaki akıllı sözleşmede orijinal 10 ETH’yi kilitler (Lock) ve eş zamanlı olarak hedef ağda 10 sentetik "wrapped" (wETH) muadilini basar (Mint).

Relay node
 

Cross-chain mimarisinin en kritik zafiyeti ara katmanda, yani doğrulama (proof verification) işlemini üstlenen relayer ve validator'larda saklıdır. Bu katman, hedef ağdaki sözleşmeye token’ların dürüstçe kilitlendiğini kanıtladığı anda sözleşme arkasına bakmadan yeni varlıkları basmaya başlar. Bu kanıtın sahtelenmesi ise likidite havuzlarının saniyeler içinde sıfırlanmasına yol açar.

Hack'ler Neden Yaşanıyor: Kanıt Sahteciliği ve Anahtar Sızıntıları

Köprülerdeki kritik zafiyetlerin çoğu temel olarak iki ana kategoride toplanır: kriptografik/mantıksal doğrulama hataları ve off-chain altyapının ele geçirilmesi (multisig özel anahtarlarının sızdırılması).

Haziran 2026’da Syscoin Bridge, kanıt doğrulama mantığındaki mimari bir çöküş nedeniyle 10 milyon dolarlık bir darbe yedi. Köprü, EVM tarafında (NEVM) varlık basmadan önce Syscoin’in UTXO tarafındaki token yakma/kilitleme işlemlerini doğrulamak adına SPV (Simplified Payment Verification) mekanizmasını kullanıyordu.

Saldırı kriptografinin kırılmasıyla değil, röle parser’ındaki saçma bir hatadan kaynaklandı. Saldırgan, bozuk bayt yapısına sahip özel bir veri paketi hazırladı. Girdinin bayt dizisi uzunluğu ve formatı sıkı bir şekilde doğrulanmadığı için parser, sahte bir hash’i gerçek bir yakma işleminin geçerli SPV kanıtı olarak kabul etti. Köprü, NEVM üzerinde tek bir coin dahi kilitlenmeden UTXO zincirinde 5 milyar SYS token basılmasına onay verdi.

İkinci temel zafiyet vektörü ise imzanın yeniden kullanılmasıdır (Signature Reuse). Temmuz 2026’da Cardano ve BNB Chain arasındaki Wanchain Bridge bu yüzden yaklaşık 13 milyon dolar kaybetti. Validator mantığı, daha önce kullanılmış geçerli kriptografik imzaları sözleşmenin global storage’ında `spent` (kullanıldı) olarak işaretlemiyordu. Saldırgan, önceden gerçekleşmiş meşru bir işlemi alıp alıcı adresini değiştirerek tekrar çağırdı. Sözleşme validator imzasını kontrol etti — imza önceden üretildiği için matematiksel olarak doğruydu — ve fonları hiç sorgulamadan tekrar serbest bıraktı.

2026 Köprü Hack Günlüğü

Yalnızca 2026’nın ilk birkaç ayında, köprülerdeki ve cross-chain altyapısındaki açıklar nedeniyle DeFi ekosisteminden yüz milyonlarca dolar buharlaştı.

Proje / KöprüTarihZarar MiktarıSaldırı Vektörü / Teknik Neden
Aethir Bridge (OFT Adapter)Nisan 2026~$5.0MBNB Chain ve Tron arasında ATH token transferi sırasında cross-chain mesajlaşma mantık hatası
Syscoin BridgeHaziran 2026~$10.0MSPV kanıtı parse etme hatası. Karşılıksız 5 milyar SYS basımı
Wanchain BridgeTemmuz 2026~$13.0MValidator tarafında imza tekrarı zafiyeti (Signature-reuse flaw)
AFX Trade BridgeTemmuz 2026$24.15MArbitrum üzerindeki köprü validator’larının özel anahtarlarının sızdırılması
Verus Ethereum BridgeTemmuz 2026$7.54MRezerv kontrolünün olmaması (teminat doğrulanmadan çekim çağrısına izin verilmesi)

Geliştiricilerin buradaki hataları sistematik bir hal almış durumda. En büyük etken, amansız TVL (Total Value Locked) yarışı ve rakip projelerden önce piyasaya ürün çıkarma hırsı. Geliştiriciler durum makinesi denetimlerini (state machine audit) es geçiyor; karmaşık kontrolleri doğrulanabilir ZK şemalarına (Zero-Knowledge Proofs) taşımak yerine Solidity veya Go üzerinde hantal veritabanı/parse mantıkları yazmaya çalışıyorlar.

Güvenli Bir Köprünün Prodüksiyon Seviye Kodu (Solidity 0.8.24)

Aşağıda kaynak ağ için hazırlanan `SecureBridgeVault` sözleşme örneğini görebilirsiniz. İçerisinde Replay Attack koruması, relayer imzalarını doğrulamak için güvenli EIP-712 mekanizması, Reentrancy Guard ve sıkı nonce durum kaydı (logging) yer almaktadır.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
/**
 * @title SecureBridgeVault v3.0
 * @author EXMON Engineering Team (https://exmon.pro)
 * @dev Lead Architect & Security Audit: EXMON Core Team
 * @notice Production-grade cross-chain bridge vault contract.
 */
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
/**
* @title SecureBridgeVault v3.0
* @author EXMON Engineering Team
* @notice Tekil depositHash kimliğine, EnumerableSet ile katı M-of-N relayer kontrolüne
* ve konfigürasyon hatalarına karşı korumaya sahip prodüksiyon kasa sözleşmesi.
*/
contract SecureBridgeVault is EIP712, ReentrancyGuard, AccessControl, Pausable {
   using SafeERC20 for IERC20;
   using ECDSA for bytes32;
   using EnumerableSet for EnumerableSet.AddressSet;
   bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
   bytes32 public constant EMERGENCY_ADMIN_ROLE = keccak256("EMERGENCY_ADMIN_ROLE");
   // lock ile yapısal olarak özdeş olan unlock imzası için tekil Typehash
   bytes32 private constant UNLOCK_TYPEHASH = keccak256(
       "Unlock(bytes32 depositHash,address token,address sender,address recipient,uint256 amount,uint256 sourceChainId,uint256 targetChainId,uint256 depositId)"
   );
   // Sözleşme depolama alanındaki relayer kayıt defteri
   EnumerableSet.AddressSet private _relayers;
   uint256 public requiredSignatures;
   uint256 public totalDepositCount;
   mapping(uint256 => bool) public supportedChains;
   mapping(address => bool) public supportedTokens;
   mapping(bytes32 => bool) public executedHashes;
   mapping(bytes32 => bool) public knownDeposits; // Yerel olarak oluşturulan yatırma işlemlerinin takibi
   event Locked(
       bytes32 indexed depositHash,
       uint256 indexed depositId,
       address indexed token,
       address sender,
       address recipient,
       uint256 amount,
       uint256 targetChainId
   );
   event Unlocked(
       bytes32 indexed depositHash,
       address indexed token,
       address recipient,
       uint256 amount,
       uint256 sourceChainId
   );
   event RelayerAdded(address indexed relayer);
   event RelayerRemoved(address indexed relayer);
   event ChainStatusUpdated(uint256 indexed chainId, bool supported);
   event TokenStatusUpdated(address indexed token, bool supported);
   event RequiredSignaturesUpdated(uint256 newThreshold);
   error ZeroAddress();
   error ZeroAmount();
   error UnsupportedChain();
   error UnsupportedToken();
   error TransactionAlreadyExecuted();
   error InvalidSignatureThreshold();
   error InvalidSignaturesLength();
   error DuplicateOrUnsortedSignature();
   error InvalidSigner();
   error RelayerAlreadyExists();
   error RelayerDoesNotExist();
   constructor(
       address admin,
       address emergencyAdmin,
       uint256 _requiredSignatures,
       address[] memory initialRelayers
   ) EIP712("EXMON_Bridge_Vault", "3.0.0") {
       if (admin == address(0) || emergencyAdmin == address(0)) revert ZeroAddress();
       
       _grantRole(DEFAULT_ADMIN_ROLE, admin);
       _grantRole(EMERGENCY_ADMIN_ROLE, emergencyAdmin);
       uint256 relayerLength = initialRelayers.length;
       for (uint256 i = 0; i < relayerLength; ) {
           address relayer = initialRelayers[i];
           if (relayer == address(0)) revert ZeroAddress();
           
           if (_relayers.add(relayer)) {
               _grantRole(RELAYER_ROLE, relayer);
               emit RelayerAdded(relayer);
           }
           unchecked { ++i; }
       }
       if (_requiredSignatures == 0 || _requiredSignatures > _relayers.length()) {
           revert InvalidSignatureThreshold();
       }
       requiredSignatures = _requiredSignatures;
   }
   // --- RELAYER VE YÖNETİŞİM YÖNETİMİ ---
   function addRelayer(address relayer) external onlyRole(DEFAULT_ADMIN_ROLE) {
       if (relayer == address(0)) revert ZeroAddress();
       if (!_relayers.add(relayer)) revert RelayerAlreadyExists();
       _grantRole(RELAYER_ROLE, relayer);
       emit RelayerAdded(relayer);
   }
   function removeRelayer(address relayer) external onlyRole(DEFAULT_ADMIN_ROLE) {
       if (!_relayers.remove(relayer)) revert RelayerDoesNotExist();
       if (_relayers.length() < requiredSignatures) revert InvalidSignatureThreshold();
       _revokeRole(RELAYER_ROLE, relayer);
       emit RelayerRemoved(relayer);
   }
   function setRequiredSignatures(uint256 _required) external onlyRole(DEFAULT_ADMIN_ROLE) {
       if (_required == 0 || _required > _relayers.length()) {
           revert InvalidSignatureThreshold();
       }
       requiredSignatures = _required;
       emit RequiredSignaturesUpdated(_required);
   }
   function getRelayers() external view returns (address[] memory) {
       return _relayers.values();
   }
   function getRelayerCount() external view returns (uint256) {
       return _relayers.length();
   }
   // --- İDARİ AĞ VE TOKEN AYARLARI ---
   function setChainSupport(uint256 chainId, bool supported) external onlyRole(DEFAULT_ADMIN_ROLE) {
       supportedChains[chainId] = supported;
       emit ChainStatusUpdated(chainId, supported);
   }
   function setTokenSupport(address token, bool supported) external onlyRole(DEFAULT_ADMIN_ROLE) {
       if (token == address(0)) revert ZeroAddress();
       supportedTokens[token] = supported;
       emit TokenStatusUpdated(token, supported);
   }
   function pause() external onlyRole(EMERGENCY_ADMIN_ROLE) {
       _pause();
   }
   function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
       _unpause();
   }
   function emergencyWithdraw(
       address token,
       address to,
       uint256 amount
   ) external onlyRole(DEFAULT_ADMIN_ROLE) whenPaused {
       if (to == address(0)) revert ZeroAddress();
       IERC20(token).safeTransfer(to, amount);
   }
   // --- KİLİTLEME VE KİLİT AÇMA MANTIĞI ---
   /**
    * @notice Deterministik depositHash üretimi ve kilitleme kaydı
    */
   function lock(
       address token,
       uint256 amount,
       address recipient,
       uint256 targetChainId
   ) external nonReentrant whenNotPaused returns (bytes32 depositHash) {
       if (amount == 0) revert ZeroAmount();
       if (recipient == address(0)) revert ZeroAddress();
       if (!supportedTokens[token]) revert UnsupportedToken();
       if (!supportedChains[targetChainId]) revert UnsupportedChain();
       uint256 depositId;
       unchecked {
           depositId = ++totalDepositCount;
       }
       // abi.encode ile tekil depositHash'in güvenli hesaplanması
       depositHash = keccak256(
           abi.encode(
               block.chainid,
               targetChainId,
               token,
               msg.sender,
               recipient,
               amount,
               depositId
           )
       );
       knownDeposits[depositHash] = true;
       IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
       emit Locked(depositHash, depositId, token, msg.sender, recipient, amount, targetChainId);
   }
   /**
    * @notice M-of-N imza doğrulaması ile tekil depositHash üzerinden fon çekimi
    * @dev İmzalar ZORUNLU OLARAK imzalayan adreslerin küçükten büyüğe sıralamasına göre Off-chain sıralanmalıdır.
    */
   function unlock(
       bytes32 depositHash,
       address token,
       address sender,
       address recipient,
       uint256 amount,
       uint256 sourceChainId,
       uint256 depositId,
       bytes[] calldata signatures
   ) external nonReentrant whenNotPaused {
       if (signatures.length != requiredSignatures) revert InvalidSignaturesLength();
       if (amount == 0) revert ZeroAmount();
       if (recipient == address(0) || sender == address(0)) revert ZeroAddress();
       if (!supportedTokens[token]) revert UnsupportedToken();
       if (!supportedChains[sourceChainId]) revert UnsupportedChain();
       // Parametre hash'inin beyan edilen depositHash ile eşleşip eşleşmediğinin kontrolü
       bytes32 expectedDepositHash = keccak256(
           abi.encode(
               sourceChainId,
               block.chainid,
               token,
               sender,
               recipient,
               amount,
               depositId
           )
       );
       if (expectedDepositHash != depositHash) revert InvalidSigner();
       bytes32 structHash = keccak256(
           abi.encode(
               UNLOCK_TYPEHASH,
               depositHash,
               token,
               sender,
               recipient,
               amount,
               sourceChainId,
               block.chainid,
               depositId
           )
       );
       bytes32 txHash = _hashTypedDataV4(structHash);
       if (executedHashes[txHash]) revert TransactionAlreadyExecuted();
       // Adres sıralaması ve imza eşiği kontrolü
       address lastSigner = address(0);
       for (uint256 i = 0; i < requiredSignatures; ) {
           address signer = txHash.recover(signatures[i]);
           if (!_relayers.contains(signer)) revert InvalidSigner();
           if (signer <= lastSigner) revert DuplicateOrUnsortedSignature();
           lastSigner = signer;
           unchecked { ++i; }
       }
       executedHashes[txHash] = true;
       IERC20(token).safeTransfer(recipient, amount);
       emit Unlocked(depositHash, token, recipient, amount, sourceChainId);
   }
}

Köprüler, iki tamamen farklı sistemin konsensüsünü zincir dışı kırılgan bir kod katmanıyla bağlamaya çalıştıkları için Web3 altyapısındaki en zayıf halka olmaya devam ediyor. Bayt formatı doğrulamasındaki ufacık bir hata, EIP-712 yapı kontrolündeki bir gözden kaçırma ya da validator sunucularından basit bir anahtar sızıntısı, yüz milyonlarca dolarlık akıllı sözleşmeyi bir anda hacker'lar için açık bir büfeye dönüştürebilir.

Bu blog yazısını şununla özetleyin:

FAQ

Cross-chain bridge sistemleri, durum değişikliklerini hedef zincirdeki akıllı sözleşmeler veya eşik imzaları (ECDSA/BLS) kullanan doğrulayıcı ağları üzerinden kriptografik kanıtlar (SPV Merkle kökleri veya Zero-Knowledge kanıtları) ile doğrular. Varlıklar kaynak zincirdeki akıllı sözleşmede kilitlendiğinde (Lock), relayer ağı işlemin kesinliğini onaylar ve EIP-712 standartlarına uygun imzalı veriyi hedef sözleşmeye iletir; sözleşme M-of-N imza eşiğini doğruladıktan sonra sentetik jetonları üretir (Mint).

Saldırılar çoğunlukla kanıt ayrıştırma (proof-parsing) hatalarından (bayt dizisi uzunluğu ve yapısının doğrulanmaması), aynı geçerli imzanın tekrar kullanılmasına izin veren signature-reuse zafiyetlerinden ve çoklu imza (multisig) doğrulayıcı düğümlerinin özel anahtar (private key) sızıntılarından kaynaklanır. Saldırganlar ayrıştırıcı mantığındaki açıkları kullanarak teminatsız çekim taleplerini onaylatır veya sahte kilitlenme olaylarını doğrulanmış gibi sisteme işler.

Replay attack koruması, block.chainid ve sözleşme adresini içeren EIP-712 veri hash'lemesi yapısı kurulması ve işlenen benzersiz işlem kimliklerinin kalıcı hafızada (executedHashes) saklanması ile sağlanır. Doğrulama fonksiyonu, varlık transferini gerçekleştirmeden önce ilgili hash değerini işlenmiş olarak işaretlemeli, imza doğrulamasını yapmadan önce mapping durumunu denetlemeli ve M-of-N eşik kontrolünde mükerrer imza kullanımını engellemek için girdi imzalarının sıralı olmasını şart koşmalıdır.
Astra EXMON

Astra is the official voice of EXMON and the editorial collective dedicated to bringing you the most timely and accurate information from the crypto market. Astra represents the combined expertise of our internal analysts, product managers, and blockchain engineers.

...

Yorumunuzu paylaşın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar işaretlendi *