Эта статья — не критика CertiK. Это честный технический разбор того, что аудит делает, чего не делает, и как извлечь из него максимум пользы. Написано на основе опыта сопровождения Security Token Offering через полный цикл аудита CertiK, где я прошёл через 29 замечаний разной критичности и исправление уязвимостей в ERC-1400 контракте на 1,100 строк.


Анатомия процесса аудита

Прежде чем разбирать findings, важно понять, как устроен процесс изнутри. Многие думают, что аудит — это «прогнали код через сканер и получили отчёт». В реальности это многоэтапный процесс, занимающий 2-4 недели.

Scoping (1-2 дня). Определение объёма работ: какие контракты входят в scope, какие сети, какой timeline. На этом этапе формируется цена. Для моего ERC-1400 контракта базовая цена составила $16,000 за полный пакет Skynet Services — после переговоров договорились на $10,500.

Automated Analysis (2-3 дня). Запуск внутренних инструментов CertiK, Slither, MythX, AI-powered review. Это первичный скан, который находит очевидные проблемы: unused variables, missing zero-address checks, inconsistent error handling.

Manual Review (5-15 дней). Главная часть аудита. Аудиторы читают код строка за строкой, анализируют бизнес-логику, ищут edge cases и экономические атаки. Именно здесь находят критические уязвимости, которые автоматика пропускает.

Formal Verification (опционально, 7-14 дней). Математическое доказательство корректности. Дорого, но для критичных протоколов — must have. CertiK использует это для L1 блокчейнов и крупных DeFi.

Report & Remediation (3-7 дней). Команда получает Initial Findings, исправляет код, отправляет на повторную проверку. Повторный аудит исправлений — бесплатно. Финальный отчёт публикуется на Skynet.


Что входит в полный пакет Skynet Services

Важный момент: CertiK продаёт не просто аудит кода, а комплексный security package. Вот что я получил за $10,500:

Security Audit — детальный code review с категоризацией всех findings по severity (Critical, High, Medium, Low, Informational). В моём случае нашли 29 замечаний. Повторная проверка исправлений включена в цену.

Skynet Boost — публичный security score на skynet.certik.com, featured позиции в рейтингах, custom profile banner. Это важно для institutional investors — они проверяют score перед инвестицией.

Penetration Testing — проверка off-chain компонентов: backend APIs, cloud infrastructure, network security. Критично для Security Token, где есть off-chain зависимости вроде KYC провайдеров.

CertiK KYC — верификация команды проекта. CertiK проверяет founder backgrounds и выдаёт KYC badge.

Contract Verification — подтверждение, что deployed on-chain код соответствует аудированной версии.

Bug Bounty (SkyRecon) — доступ к community white hat хакеров для continuous security после запуска.

Active Monitor — real-time мониторинг 24/7 с incident response.


Реальные findings: разбор моего аудита

Теперь к самому интересному — что именно находит CertiK. Разберу на примере своего аудита ERC-1400 Security Token контракта.

Статистика

Всего findings: 29 Resolved в коде: 13 (45%) Acknowledged: 16 (55%)

«Acknowledged» — это не «проигнорировано». Это осознанное решение с документированным обоснованием, почему finding не требует изменения кода. CertiK принимает такие ответы, если команда предоставляет адекватное объяснение.

Critical: Controller Transfer обходит Compliance

Самая серьёзная находка. В Security Token есть функция controllerTransfer, которая позволяет контроллеру принудительно перемещать токены — это требование стандарта ERC-1400 для регуляторного compliance.

Проблема: функция не проверяла, находится ли получатель в whitelist и не заморожен ли он.

// КОД ДО АУДИТА (уязвимый):
function controllerTransfer(
    address from, 
    address to, 
    uint256 value
) public onlyRole(CONTROLLER_ROLE) {
    if (balanceOf[from] < value) revert TransferInsufficientBalance();
    if (from == address(0) || to == address(0)) revert TransferFailure();
    
    // Нет проверок compliance!
    _performTransfer(from, to, value);
}

Для обычного DeFi протокола это было бы Major — нарушение бизнес-логики. Для Security Token это Critical, потому что:

  • Можно перевести токены на адрес без KYC (нарушение AML)

  • Можно обойти санкционные списки (frozen addresses)

  • Регуляторные последствия для эмитента

  • Потенциальная уголовная ответственность

Исправление:

// КОД ПОСЛЕ АУДИТА:
function controllerTransfer(
    address from, 
    address to, 
    uint256 value
) public onlyRole(CONTROLLER_ROLE) {
    if (!whitelistedAccounts.contains(to)) 
        revert RecipientNotWhitelisted();
    if (frozen[from] || frozen[to]) 
        revert AccountFrozen();
    if (balanceOf[from] < value) 
        revert TransferInsufficientBalance();
        
    _performTransfer(from, to, value);
    emit ControllerTransfer(msg.sender, from, to, value);
}

High: Race Condition в Batch Dividend Processing

В контракте была система batch-обработки дивидендов для оптимизации gas при большом количестве держателей.

Проблема: если во время обработки batch (например, на 50-й итерации из 100) вызвать новый distributeDividend(), индекс batch сбрасывался на 0, и предыдущий batch терялся.

// ПРОБЛЕМА:
function distributeDividend(uint256 totalProfit) external 
    onlyRole(DIVIDEND_MANAGER_ROLE) 
{
    // ...
    currentDividendBatchIndex = 0;  // Сброс без проверки!
    // ...
}

Исправление — добавили флаг блокировки:

bool public batchProcessingInProgress;

modifier noBatchInProgress() {
    if (batchProcessingInProgress) revert BatchProcessingInProgress();
    _;
}

function distributeDividend(uint256 totalProfit) external 
    onlyRole(DIVIDEND_MANAGER_ROLE) 
    noBatchInProgress
{
    // ...
}

Medium: Precision Loss в расчёте дивидендов

Классическая проблема целочисленной арифметики:

return (balanceAtTime * snapshot.amount) / snapshot.totalSupplyAt;
// При balance=1, amount=99, totalSupply=100:
// owed = (1 * 99) / 100 = 0  — потеря!

Наш ответ: Acknowledged. В контексте нашего STO минимальный баланс составляет 1,000 токенов, precision loss несущественен. Задокументировано как known limitation.

CertiK принял это обоснование — finding закрыт как Acknowledged.


Типичные паттерны ошибок

На основе 29 findings из моего аудита — вот что CertiK ищет и находит чаще всего.

Inconsistent Error Handling

CertiK не любит, когда в одном контракте используются разные стили обработки ошибок:

// ❌ Микс стилей:
function transfer(...) { require(balance >= amount, "Insufficient"); }
function burn(...) { if (balance < amount) revert InsufficientBalance(); }

// ✅ Единообразие:
function transfer(...) { if (balance < amount) revert InsufficientBalance(); }
function burn(...) { if (balance < amount) revert InsufficientBalance(); }

Unchecked Return Values

// ❌ Игнорирование return value:
IERC20(token).transfer(owner(), amount);  // Может молча провалиться!

// ✅ SafeERC20:
IERC20(token).safeTransfer(owner(), amount);

Zero Address Validation

// ❌ Нет проверки:
constructor(address _dividendToken) {
    dividendToken = IERC20(_dividendToken);
}

// ✅ С проверкой:
constructor(address _dividendToken) {
    if (_dividendToken == address(0)) revert ZeroAddressNotAllowed();
    dividendToken = IERC20(_dividendToken);
}

Centralization Risks

CertiK всегда отмечает функции, дающие owner'у чрезмерный контроль:

// Будет отмечено как Centralization Risk:
function emergencyWithdraw(address token) external onlyOwner {
    uint256 balance = IERC20(token).balanceOf(address(this));
    IERC20(token).safeTransfer(owner(), balance);
}

Рекомендации: Timelock, Multi-sig, ограничение максимальной суммы. В моём случае я ответил Acknowledged с обоснованием, что это Security Token с регуляторными требованиями, где централизованный контроль — это feature, а не bug.

Recovery Limits

Если в контракте есть механизм recovery (восстановление доступа при потере ключей), CertiK проверит наличие лимитов:

// ❌ Без лимита:
function requestRecovery(address newAddress) external {
    recoveryRequests[msg.sender] = RecoveryRequest({...});
}

// ✅ С rolling window:
uint256 public constant MAX_RECOVERY_PER_YEAR = 2;

function requestRecovery(address newAddress) external {
    if (block.timestamp >= lastRecoveryTimestamp[msg.sender] + 365 days) {
        recoveryCount[msg.sender] = 0;
    }
    
    if (recoveryCount[msg.sender] >= MAX_RECOVERY_PER_YEAR) {
        revert RecoveryLimitExceeded();
    }
    
    recoveryCount[msg.sender]++;
    // ...
}

Что аудит НЕ проверяет

Это критически важно понимать. Аудит смарт-контракта — это проверка кода, не более.

В scope:

  • Код смарт-контрактов

  • Бизнес-логика (в рамках спецификации)

  • Базовые экономические атаки

Вне scope:

  • Key management и operational security

  • Off-chain компоненты (backend, frontend, API)

  • Реализация third-party интеграций

  • Social engineering

  • Будущие upgrades

90% «взломов» CertiK-аудированных проектов — это не баги в коде, а компрометация приватных ключей. Gala Games ($216M) — украден admin key. WazirX ($234M) — манипуляция интерфейсом подписи. Аудит кода здесь бессилен.


Почему аудит не спасает: три кейса

Gala Games — $216M (2024)

Компрометация привилегированного ключа. Злоумышленник получил доступ к admin wallet и минтанул 5 миллиардов токенов GALA.

Контракт был написан корректно. Функция mint требовала роль MINTER_ROLE. Проблема: один скомпрометированный ключ давал полный контроль. Это operational security, не код.

WooFi — $85M (2024)

Flash loan + oracle manipulation. Атакующий манипулировал ценой в synthetic pool и вывел средства по искажённой цене.

Это экономическая атака, требующая моделирования поведения в условиях экстремальной ликвидности. Стандартный code review такое не ловит — нужен отдельный economic audit.

Merlin DEX — $1.82M (2023)

Rug pull через «легитимную» функцию emergencyWithdraw. CertiK нашёл и отметил это как Centralization Risk с severity Informational. Команда ответила «Will be addressed post-launch». Не адресовали.

Аудит находит централизованные риски, но не может предсказать злой умысел команды.


Сколько стоит аудит CertiK

Реальные цифры из моего опыта и переговоров:

Простой ERC-20 (200-500 строк): $8-10K базовая, $6-8K после переговоров

NFT коллекция (500-1000 строк): $12-15K базовая, ~$10K после переговоров

Security Token ERC-1400/ERC-3643 (1000-1500 строк): $16-20K базовая, $10-12K после переговоров

DeFi Protocol (2000-5000 строк): $25-50K

Complex DeFi/Bridge (5000+ строк): $50-100K+

Факторы, влияющие на цену:

  • Сложность логики: +30-50% для DeFi с экономикой

  • Срочность: +30-50% за 1 неделю вместо 3

  • Только audit без Skynet: -30-40%

  • Повторный клиент: -10-20%

Мой совет: не экономьте на полном Skynet package. Разница $3-5K, но вы получаете ongoing monitoring и public score — это критично для institutional investors.


Как подготовить проект к аудиту

Pre-audit checklist

Документация:

  • Полная спецификация бизнес-логики

  • Архитектурные диаграммы

  • Описание ролей и permissions

  • Known risks и trade-offs (задокументированы!)

Код:

  • Feature freeze минимум за неделю до аудита

  • 100% test coverage критических paths

  • Slither запущен, findings устранены

  • NatSpec комментарии для всех public/external функций

  • Единообразная обработка ошибок

  • Zero-address проверки везде

Тесты:

  • Unit tests

  • Integration tests

  • Fuzz tests (Foundry)

  • Invariant tests

Как читать отчёт

Critical/High findings → Must fix before deploy Medium findings → Should fix, обсудить trade-offs Low/Informational → Prioritize by effort/impact

Red flags в отчёте:

  • «Acknowledged» без плана исправления

  • «Centralization risk» без mitigation

  • «Will be fixed in future version»

После аудита

Аудит — это baseline, не финал.

Bug Bounty. Запустите программу на Immunefi. Стандартная модель: 10% от TVL как максимальная выплата за critical.

Runtime Monitoring. CertiK Skynet (если в пакете), Forta (open-source альтернатива), OpenZeppelin Defender.

Incident Response Plan. Pause mechanisms, emergency contacts, communication templates. Когда (не если) найдут баг — у вас должен быть план.

Continuous Security. Re-audit при значительных изменениях кода.


Заключение

Аудит от CertiK — это:

  • Профессиональный code review

  • Проверка на известные уязвимости

  • Повышение доверия инвесторов

  • Compliance requirement для многих платформ

Аудит — это НЕ:

  • Гарантия безопасности

  • Защита от operational failures

  • Страховка от экономических атак

  • Верификация намерений команды

Формула безопасности:

Audit + Bug Bounty + Monitoring + Incident Response + Continuous Review

Каждый элемент важен. Пропустите один — и вы в статистике потерь следующего года.


Если вы готовите проект к аудиту и хотите обсудить подготовку — контакты в профиле. Также буду рад обратной связи по статье в комментариях.

Комментарии (0)