Эта статья — не критика 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
Каждый элемент важен. Пропустите один — и вы в статистике потерь следующего года.
Если вы готовите проект к аудиту и хотите обсудить подготовку — контакты в профиле. Также буду рад обратной связи по статье в комментариях.