Безопасность. Атака griefing
Еще одна атака, которая трудна для понимания с первого раза, и что еще хуже, практически не заметна для новичков.
В ру сегменте я не смог найти достойной статьи про данную атаку, поэтому сделал перевод основной части отсюда.
Griefing Attack или griefing gas attack, как мы поняли из второго названия этой атаки, тесно связана с использованием газа в транзакции в момент вызова функции в другом контракте. Из-за того, что не был установлен и подтвержден лимит газа, мошенник может подключиться к функции и использовать имеющийся газ в своих целях.
Эта атака не столько приносит "радости" мошеннику, сколько может принести убытков атакуемому контракту.
Рассмотрим пример.
contract Relayer {
function relay (Target target, bytes memory _data) public returns (bool) {
(bool, success,) = address(target).call(abi.encodeWithSignature("execute(bytes)", _data));
return success;
}
}
contract Target {
bool piblic result = false;
function execute(bytes memory _data) public {
uint j = 0;
for (uint i; i < 100; i++) {
j++;
}
result = true;
}
function setresult (bool v) public {
result = v;
}
}
Если мы развернем оба контракта и вызовем в первом relay(), то все пройдет успешно. Но если мы попробуем узнать значение переменной result во втором контракте, то оно останется неизменным.
Это происходит потому, что второму контракту не хватило газа на выполнение функции. Проверить это можно, повысив gasLimit для функции в первом контракте, и вызвал result еще раз.
Мне пока сложно представить, как мошенник может использовать этот газ в своих целях, так как опыта в этом мало, но из статьи понятно, что такая утечка может привести к значительным финансовым потерям владельца первого контракта.
Как я понял из статьи, на данный момент нет 100% защиты от этой атаки, но ее шансы можно значительно сократить следующим образом.
Во-первых, проверять в первом контракте, что транзакция успешно прошла, например добавить после строки (bool success,) условие "if\else".
Во-вторых, проверять лимит газа. Например так.
В первом контракте ввести две дополнительные переменные:
uint estimateGasValue = 1000000;
uint gasNeededBetweenCalls = 5000;
а затем модифицировать функцию relay():
uint gasAvailable = gasLeft() - gasNeededBetweenCalls;
require (gasAvailable - gasAvailable \ 64 >= estimateGasValue, 'not enough gas provided');
(bool success,) = address(target).call(abi.encodeWithSignature("execute(bytes)", _data, estimateGasValue));
Решения с оценкой газа могут стать не эффективными со временем, когда цена на газ вырастет.
Лучшим решением будет позволить EVM рассчитать необходимый газ для каждого вызова и откатить транзакцию, в случае его нехватки.
#griefing #безопасность
Post #363
194
- 👍 1