
Solidity-Sicherheit
Dieser Beitrag soll eine relativ ausführliche und aktuelle Einführung sein, die die früheren Fehler von Solidity-Entwicklern detailliert beschreibt, um zukünftige Entwickler davor zu bewahren, die Geschichte zu wiederholen.
Eine der Funktionen von Ethereum-Smart-Contracts ist die Fähigkeit, Code anderer externer Verträge aufzurufen und zu nutzen. Verträge handhaben typischerweise auch Ether und senden daher oft Ether an verschiedene externe Benutzeradressen. Der Vorgang, externe Verträge aufzurufen oder Ether an eine Adresse zu senden, erfordert, dass der Vertrag einen externen Aufruf absetzt. Diese externen Aufrufe können von Angreifern gekapert werden, indem sie den Vertrag zwingen, weiteren Code auszuführen (z. B. über eine Fallback-Funktion), einschließlich Aufrufen zurück zu sich selbst. Somit "tritt" die Codeausführung erneut in den Vertrag ein. Angriffe dieser Art wurden beim berüchtigten DAO-Hack eingesetzt.
Für weiterführende Literatur zu Re-Entrancy-Angriffen siehe Reentrancy Attack On Smart Contracts und Consensus - Ethereum Smart Contract Best Practices.
Dieser Angriff kann auftreten, wenn ein Vertrag Ether an eine unbekannte Adresse sendet. Ein Angreifer kann an einer externen Adresse sorgfältig einen Vertrag konstruieren, der bösartigen Code in der Fallback-Funktion enthält. Wenn ein Vertrag also Ether an diese Adresse sendet, wird der bösartige Code aufgerufen. Typischerweise führt der bösartige Code eine Funktion auf dem verwundbaren Vertrag aus und führt dabei Operationen durch, die der Entwickler nicht erwartet hat. Der Name "Re-Entrancy" rührt daher, dass der externe bösartige Vertrag eine Funktion auf dem verwundbaren Vertrag zurückruft und an einer beliebigen Stelle des verwundbaren Vertrags erneut in die Codeausführung "eintritt".
Zur Veranschaulichung betrachten wir den einfachen verwundbaren Vertrag, der als Ethereum-Tresor fungiert und Einzahlern erlaubt, nur 1 Ether pro Woche abzuheben.
EtherStore.sol:```solidity contract EtherStore {
uint256 public withdrawalLimit = 1 ether;
mapping(address => uint256) public lastWithdrawTime;
mapping(address => uint256) public balances;
function depositFunds() public payable {
balances[msg.sender] += msg.value;
}