1312 lines
54 KiB
Markdown
1312 lines
54 KiB
Markdown
[English](../docs.en/security.md) | **Русский**
|
||
|
||
# Безопасность Digital Legal Entity (DLE)
|
||
|
||
## Содержание
|
||
|
||
1. [Введение](#введение)
|
||
2. [Модель безопасности](#модель-безопасности)
|
||
3. [Контроль доступа на основе токенов](#контроль-доступа-на-основе-токенов)
|
||
4. [Безопасность смарт-контрактов](#безопасность-смарт-контрактов)
|
||
5. [Защита от взлома кошельков](#защита-от-взлома-кошельков)
|
||
6. [Безопасность веб-приложения](#безопасность-веб-приложения)
|
||
7. [Управление модулями](#управление-модулями)
|
||
8. [Аудит и мониторинг](#аудит-и-мониторинг)
|
||
9. [Рекомендации по безопасности](#рекомендации-по-безопасности)
|
||
10. [Сценарии атак и защита](#сценарии-атак-и-защита)
|
||
|
||
---
|
||
|
||
## Введение
|
||
|
||
Digital Legal Entity (DLE) построен с фокусом на **безопасность на всех уровнях**:
|
||
- Контроль доступа через блокчейн-токены
|
||
- Защита смарт-контрактов от взлома
|
||
- Невозможность кражи токенов даже при взломе кошелька
|
||
- Управление только через голосование с кворумом
|
||
|
||
### Ключевые принципы безопасности
|
||
|
||
1. **Защита по умолчанию** - все действия запрещены, пока явно не разрешены
|
||
2. **Минимальные привилегии** - каждый получает только необходимые права
|
||
3. **Прозрачность** - все действия записаны на блокчейне
|
||
4. **Неизменяемость** - невозможно подделать историю
|
||
5. **Коллективный контроль** - критичные операции только через голосование
|
||
|
||
---
|
||
|
||
## Модель безопасности
|
||
|
||
### Архитектура безопасности
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Уровни защиты DLE │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ Уровень 1: Блокчейн (Неизменяемая база) │
|
||
│ ┌───────────────────────────────────────────────────────┐ │
|
||
│ │ • Смарт-контракт DLE (проверен, иммутабельный) │ │
|
||
│ │ • Токены управления (ERC20Votes) │ │
|
||
│ │ • История всех операций на блокчейне │ │
|
||
│ │ • Невозможность изменения правил без голосования │ │
|
||
│ └───────────────────────────────────────────────────────┘ │
|
||
│ ↑ │
|
||
│ Уровень 2: Веб-приложение (Backend) │
|
||
│ ┌───────────────────────────────────────────────────────┐ │
|
||
│ │ • Проверка токенов в реальном времени │ │
|
||
│ │ • Аутентификация через кошелек (SIWE) │ │
|
||
│ │ • Шифрование данных (AES-256) │ │
|
||
│ │ • Rate limiting и защита от DDoS │ │
|
||
│ └───────────────────────────────────────────────────────┘ │
|
||
│ ↑ │
|
||
│ Уровень 3: Frontend (Vue.js) │
|
||
│ ┌───────────────────────────────────────────────────────┐ │
|
||
│ │ • Подключение к кошельку │ │
|
||
│ │ • Подпись транзакций │ │
|
||
│ │ • XSS защита (DOMPurify) │ │
|
||
│ │ • CSRF токены │ │
|
||
│ └───────────────────────────────────────────────────────┘ │
|
||
│ ↑ │
|
||
│ Уровень 4: Пользователь │
|
||
│ ┌───────────────────────────────────────────────────────┐ │
|
||
│ │ • Приватный ключ кошелька (MetaMask, WalletConnect) │ │
|
||
│ │ • Подтверждение каждой операции │ │
|
||
│ └───────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Модель угроз
|
||
|
||
| Угроза | Уровень риска | Защита |
|
||
|--------|---------------|--------|
|
||
| **Взлом кошелька** | Средний | Токены нельзя перевести без голосования |
|
||
| **Взлом веб-приложения** | Низкий | Все права проверяются на блокчейне, управление через блокчейн-сканеры |
|
||
| **Компрометация смарт-контракта** | Низкий | Аудит, OpenZeppelin, иммутабельность |
|
||
| **DDoS атака** | Средний | Rate limiting, CDN, резервные сервера |
|
||
| **Фишинг** | Высокий | Обучение пользователей, проверка домена |
|
||
| **Insider threat** | Низкий | Все действия через голосование |
|
||
|
||
### Критически важно: веб-приложение — это только интерфейс
|
||
|
||
**Ключевая особенность архитектуры DLE:**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Веб-приложение (интерфейс) │
|
||
│ │
|
||
│ Frontend + Backend = УДОБСТВО использования │
|
||
│ • Красивый UI │
|
||
│ • Удобная навигация │
|
||
│ • Быстрый доступ к функциям │
|
||
│ │
|
||
│ Может быть взломано/недоступно │
|
||
│ ✅ НО! Активы бизнеса защищены │
|
||
└─────────────────────────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Блокчейн (реальная власть) │
|
||
│ │
|
||
│ Смарт-контракты = РЕАЛЬНОЕ управление активами │
|
||
│ • Токены управления │
|
||
│ • Казна с активами │
|
||
│ • Правила голосования │
|
||
│ • История всех решений │
|
||
│ │
|
||
│ Защищено криптографией │
|
||
│ ✅ Работает независимо от веб-приложения │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Что происходит при взломе веб-приложения:**
|
||
|
||
```
|
||
Веб-приложение взломано/недоступно:
|
||
├── ❌ Веб-интерфейс не работает
|
||
├── ❌ Backend может показывать неверную информацию
|
||
├── ❌ Frontend может быть подменен
|
||
│
|
||
НО:
|
||
├── ✅ Все активы бизнеса остаются на блокчейне
|
||
├── ✅ Смарт-контракты продолжают работать
|
||
├── ✅ Токены невозможно украсть
|
||
├── ✅ Можно управлять через Etherscan/Polygonscan/др.
|
||
└── ✅ Можно создать новый frontend и подключить к тем же контрактам
|
||
```
|
||
|
||
**Реальный пример:**
|
||
|
||
1. Злоумышленник взламывает ваш сервер с веб-приложением
|
||
2. Пытается показать поддельные балансы токенов
|
||
3. **Но**: Реальные балансы на блокчейне остаются неизменными
|
||
4. Вы открываете Etherscan и видите правду
|
||
5. Создаете предложение через Etherscan на восстановление
|
||
6. Голосуете и исполняете предложение
|
||
7. Восстанавливаете веб-приложение
|
||
8. **Результат**: Ни один токен не потерян ✅
|
||
|
||
---
|
||
|
||
## Контроль доступа на основе токенов
|
||
|
||
### Принцип работы
|
||
|
||
**Без токенов доступ к приложению НЕВОЗМОЖЕН.**
|
||
|
||
```
|
||
Попытка доступа к DLE:
|
||
├── 1. Пользователь подключает кошелек
|
||
├── 2. Backend проверяет баланс токенов в смарт-контракте
|
||
├── 3. Если токенов НЕТ → Доступ ЗАПРЕЩЕН
|
||
└── 4. Если токены ЕСТЬ → Доступ разрешен (уровень зависит от количества)
|
||
```
|
||
|
||
### Уровни доступа
|
||
|
||
| Токенов на балансе | Уровень доступа | Права |
|
||
|-------------------|-----------------|-------|
|
||
| **0 токенов** | ❌ Нет доступа | Только страница "Нет доступа" |
|
||
| **1+ токенов** | ✅ ReadOnly | Просмотр данных |
|
||
| **100+ токенов** | ✅ Editor | Редактирование, создание |
|
||
| **Любое количество** | 🗳️ Голосование | 1 токен = 1 голос |
|
||
|
||
### Проверка токенов в реальном времени
|
||
|
||
**Backend** постоянно проверяет баланс токенов:
|
||
|
||
```javascript
|
||
// Каждый запрос проверяет токены
|
||
async function checkAccess(req, res, next) {
|
||
const address = req.session.address;
|
||
|
||
// Получаем баланс токенов из смарт-контракта
|
||
const dleContract = new ethers.Contract(dleAddress, dleAbi, provider);
|
||
const balance = await dleContract.balanceOf(address);
|
||
|
||
if (balance === 0n) {
|
||
return res.status(403).json({
|
||
error: 'Доступ запрещен: нет токенов'
|
||
});
|
||
}
|
||
|
||
// Определяем уровень доступа
|
||
const accessLevel = determineAccessLevel(balance);
|
||
req.user = { address, balance, accessLevel };
|
||
|
||
next();
|
||
}
|
||
```
|
||
|
||
**Важно**: Проверка происходит на **каждом запросе**, поэтому:
|
||
- ✅ Если токены переведены → доступ мгновенно теряется
|
||
- ✅ Если токены получены → доступ мгновенно появляется
|
||
- ✅ Невозможно обойти проверку
|
||
|
||
### Начальное распределение токенов
|
||
|
||
**Токены распределяет владелец при деплое смарт-контракта:**
|
||
|
||
```solidity
|
||
constructor(DLEConfig memory config) {
|
||
// Создаем токены
|
||
_mint(address(this), totalSupply);
|
||
|
||
// Распределяем среди партнеров
|
||
for (uint i = 0; i < config.initialPartners.length; i++) {
|
||
_transfer(
|
||
address(this),
|
||
config.initialPartners[i],
|
||
config.initialAmounts[i]
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
**Процесс**:
|
||
1. Владелец кошелька деплоит смарт-контракт DLE
|
||
2. Указывает адреса партнеров и количество токенов для каждого
|
||
3. Токены автоматически распределяются при деплое
|
||
4. После этого все изменения только через голосование
|
||
|
||
**Пример распределения**:
|
||
```javascript
|
||
const config = {
|
||
initialPartners: [
|
||
'0xAlice...', // Основатель 1
|
||
'0xBob...', // Основатель 2
|
||
'0xCarol...' // Инвестор
|
||
],
|
||
initialAmounts: [
|
||
500000, // 50% для Alice
|
||
300000, // 30% для Bob
|
||
200000 // 20% для Carol
|
||
]
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## Безопасность смарт-контрактов
|
||
|
||
### Защита от переводов токенов
|
||
|
||
**КРИТИЧНО**: Токены управления **НЕЛЬЗЯ** перевести обычными способами!
|
||
|
||
```solidity
|
||
// Переводы ЗАБЛОКИРОВАНЫ
|
||
function transfer(address to, uint256 amount)
|
||
public
|
||
pure
|
||
override
|
||
returns (bool)
|
||
{
|
||
revert ErrTransfersDisabled();
|
||
}
|
||
|
||
// Одобрения ЗАБЛОКИРОВАНЫ
|
||
function approve(address spender, uint256 amount)
|
||
public
|
||
pure
|
||
override
|
||
returns (bool)
|
||
{
|
||
revert ErrApprovalsDisabled();
|
||
}
|
||
|
||
// TransferFrom ЗАБЛОКИРОВАН
|
||
function transferFrom(address from, address to, uint256 amount)
|
||
public
|
||
pure
|
||
override
|
||
returns (bool)
|
||
{
|
||
revert ErrTransfersDisabled();
|
||
}
|
||
```
|
||
|
||
**Что это означает**:
|
||
- ❌ Невозможно отправить токены на биржу
|
||
- ❌ Невозможно продать токены на DEX
|
||
- ❌ Невозможно передать токены другому лицу напрямую
|
||
- ❌ Злоумышленник НЕ может украсть токены даже со взломанного кошелька
|
||
|
||
### Единственный способ перевода токенов
|
||
|
||
**Только через голосование с кворумом:**
|
||
|
||
```solidity
|
||
// Перевод токенов (только через голосование)
|
||
function _transferTokens(address recipient, uint256 amount) internal {
|
||
require(msg.sender == address(this), "Only through governance");
|
||
_transfer(address(this), recipient, amount);
|
||
}
|
||
```
|
||
|
||
**Процесс перевода**:
|
||
```
|
||
1. Создание предложения
|
||
├── "Перевести 1000 токенов на адрес 0xNew..."
|
||
└── Требуется: минимум 1 токен для создания
|
||
|
||
2. Голосование
|
||
├── Длительность: 1-30 дней (настраивается)
|
||
├── Каждый токен = 1 голос
|
||
└── Требуется: кворум (например, 10% от всех токенов)
|
||
|
||
3. Проверка кворума
|
||
├── Если "За" > "Против" И кворум достигнут
|
||
└── → Предложение одобрено
|
||
|
||
4. Исполнение
|
||
├── Смарт-контракт автоматически переводит токены
|
||
└── Событие записано на блокчейне навсегда
|
||
```
|
||
|
||
### Настройка кворума
|
||
|
||
**Кворум устанавливается при деплое** и может быть изменен только через голосование:
|
||
|
||
```solidity
|
||
uint256 public quorumPercentage; // Например, 10%
|
||
|
||
function _hasQuorum(uint256 forVotes, uint256 againstVotes)
|
||
internal
|
||
view
|
||
returns (bool)
|
||
{
|
||
uint256 totalVotes = forVotes + againstVotes;
|
||
uint256 required = (totalSupply() * quorumPercentage) / 100;
|
||
return totalVotes >= required;
|
||
}
|
||
```
|
||
|
||
**Примеры кворума**:
|
||
- 5% - легко достичь (для активных организаций)
|
||
- 10% - стандартный (рекомендуется)
|
||
- 20% - строгий (для критичных решений)
|
||
- 51% - абсолютное большинство
|
||
|
||
### Защита от реентерабельности
|
||
|
||
```solidity
|
||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
||
|
||
contract DLE is ReentrancyGuard {
|
||
function execute(uint256 proposalId)
|
||
external
|
||
nonReentrant // Защита от реентерабельности
|
||
{
|
||
// Исполнение предложения
|
||
}
|
||
}
|
||
```
|
||
|
||
### Защита от flash-loans
|
||
|
||
**Снапшоты голосов** предотвращают атаки с временным займом токенов:
|
||
|
||
```solidity
|
||
// Голоса берутся из ПРОШЛОГО блока
|
||
uint256 public snapshotTimepoint = block.number - 1;
|
||
|
||
function vote(uint256 proposalId, bool support) external {
|
||
// Используем баланс из прошлого блока
|
||
uint256 votingPower = getPastVotes(msg.sender, snapshotTimepoint);
|
||
require(votingPower > 0, "No voting power");
|
||
|
||
// Голосование
|
||
}
|
||
```
|
||
|
||
**Почему это безопасно**:
|
||
1. Злоумышленник берет flash-loan на 1,000,000 токенов
|
||
2. Пытается проголосовать
|
||
3. Смарт-контракт проверяет баланс в **прошлом блоке**
|
||
4. В прошлом блоке у злоумышленника было 0 токенов
|
||
5. Голосование отклонено ❌
|
||
|
||
### Валидация всех параметров
|
||
|
||
```solidity
|
||
// Проверка адресов
|
||
if (address == address(0)) revert ErrZeroAddress();
|
||
|
||
// Проверка наличия токенов
|
||
if (balanceOf(msg.sender) == 0) revert ErrNotHolder();
|
||
|
||
// Проверка длительности голосования
|
||
if (duration < minVotingDuration) revert ErrTooShort();
|
||
if (duration > maxVotingDuration) revert ErrTooLong();
|
||
|
||
// Проверка поддерживаемых сетей
|
||
if (!supportedChains[chainId]) revert ErrUnsupportedChain();
|
||
```
|
||
|
||
### Custom errors для экономии gas
|
||
|
||
```solidity
|
||
// Экономия gas: custom errors вместо require
|
||
error ErrZeroAddress();
|
||
error ErrNotHolder();
|
||
error ErrAlreadyVoted();
|
||
error ErrTransfersDisabled();
|
||
error ErrApprovalsDisabled();
|
||
error ErrProposalMissing();
|
||
error ErrWrongChain();
|
||
```
|
||
|
||
---
|
||
|
||
## Защита от взлома кошельков
|
||
|
||
### Сценарий: Злоумышленник получил приватный ключ
|
||
|
||
**Что может сделать злоумышленник:**
|
||
|
||
```
|
||
Попытки злоумышленника:
|
||
|
||
1. ❌ Отправить токены на свой адрес
|
||
└── БЛОКИРОВАНО: transfer() заблокирован
|
||
|
||
2. ❌ Продать токены на DEX (Uniswap, etc.)
|
||
└── БЛОКИРОВАНО: approve() заблокирован
|
||
|
||
3. ❌ Отправить через transferFrom
|
||
└── БЛОКИРОВАНО: transferFrom() заблокирован
|
||
|
||
4. ❓ Создать предложение "Перевести токены мне"
|
||
└── Требуется голосование других токен-холдеров
|
||
└── Кворум: 10%+ должны проголосовать "За"
|
||
└── СКОРЕЕ ВСЕГО ПРОВАЛИТСЯ
|
||
```
|
||
|
||
### Реальный пример атаки
|
||
|
||
```
|
||
Злоумышленник взломал кошелек Alice (500,000 токенов):
|
||
|
||
1. Создает предложение:
|
||
"Перевести 500,000 токенов на адрес 0xEvil..."
|
||
|
||
2. Голосует "За" своими 500,000 голосами (50%)
|
||
|
||
3. Но кворум = 10% = 100,000 голосов
|
||
Уже достигнут? ДА ✅
|
||
|
||
4. "За" = 500,000 (50%)
|
||
"Против" = 0
|
||
|
||
5. Результат: Предложение ОДОБРЕНО ❌
|
||
|
||
ПРОБЛЕМА: Если у Alice большинство токенов!
|
||
```
|
||
|
||
### Защита от атаки с большинством токенов
|
||
|
||
**Решение 1: Timelock Module**
|
||
|
||
```solidity
|
||
// Отложенное исполнение (например, 3 дня)
|
||
uint256 public constant TIMELOCK_DELAY = 3 days;
|
||
|
||
function scheduleProposal(uint256 proposalId) external {
|
||
// Предложение одобрено, но исполнится только через 3 дня
|
||
executionTime = block.timestamp + TIMELOCK_DELAY;
|
||
}
|
||
```
|
||
|
||
**Преимущество**: Другие токен-холдеры видят опасное предложение и могут:
|
||
- Проголосовать "Против"
|
||
- Создать контр-предложение
|
||
- Принять меры (например, обратиться в суд)
|
||
|
||
**Решение 2: Мультиподпись**
|
||
|
||
```solidity
|
||
// Требуется несколько подписей для критичных операций
|
||
mapping(uint256 => mapping(address => bool)) public approvals;
|
||
|
||
function executeWithApprovals(
|
||
uint256 proposalId,
|
||
address[] calldata signers,
|
||
bytes[] calldata signatures
|
||
) external {
|
||
// Проверяем подписи нескольких токен-холдеров
|
||
require(signers.length >= minSigners, "Not enough signers");
|
||
|
||
for (uint i = 0; i < signers.length; i++) {
|
||
// Проверка подписи
|
||
verifySignature(proposalId, signers[i], signatures[i]);
|
||
}
|
||
|
||
// Исполнение
|
||
}
|
||
```
|
||
|
||
**Решение 3: Холодный кошелек для крупных холдеров**
|
||
|
||
Рекомендуется держать токены на:
|
||
- 🥶 **Hardware wallet** (Ledger, Trezor) - максимальная защита
|
||
- **Мультиподпись** (Gnosis Safe) — требуется несколько подписей
|
||
- ❄️ **Холодное хранение** - offline, не подключен к интернету
|
||
|
||
### Мониторинг подозрительных предложений
|
||
|
||
**Backend автоматически детектирует опасные предложения:**
|
||
|
||
```javascript
|
||
// Детектор подозрительных предложений
|
||
function detectSuspiciousProposal(proposal) {
|
||
const alerts = [];
|
||
|
||
// Проверка 1: Перевод большого количества токенов
|
||
if (proposal.operation.includes('_transferTokens')) {
|
||
const amount = decodeAmount(proposal.operation);
|
||
const percentage = (amount / totalSupply) * 100;
|
||
|
||
if (percentage > 10) {
|
||
alerts.push({
|
||
level: 'HIGH',
|
||
message: `Предложение переводит ${percentage}% всех токенов!`
|
||
});
|
||
}
|
||
}
|
||
|
||
// Проверка 2: Инициатор голосует своими токенами
|
||
if (proposal.forVotes === proposal.initiatorBalance) {
|
||
alerts.push({
|
||
level: 'MEDIUM',
|
||
message: 'Инициатор голосует только своими токенами'
|
||
});
|
||
}
|
||
|
||
// Проверка 3: Быстрое голосование (< 24 часов)
|
||
if (proposal.duration < 86400) {
|
||
alerts.push({
|
||
level: 'MEDIUM',
|
||
message: 'Голосование менее 24 часов - мало времени на проверку'
|
||
});
|
||
}
|
||
|
||
// Отправляем уведомления всем токен-холдерам
|
||
if (alerts.length > 0) {
|
||
notifyAllTokenHolders(proposal, alerts);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Безопасность веб-приложения
|
||
|
||
### Аутентификация через SIWE
|
||
|
||
**Sign-In with Ethereum** - стандарт безопасной аутентификации:
|
||
|
||
```javascript
|
||
// Генерация nonce (одноразовый)
|
||
const nonce = crypto.randomBytes(32).toString('hex');
|
||
|
||
// Сохранение в БД с шифрованием
|
||
await db.query(
|
||
'INSERT INTO nonces (address_encrypted, nonce_encrypted, expires_at) VALUES ($1, $2, $3)',
|
||
[encrypt(address), encrypt(nonce), expiresAt]
|
||
);
|
||
|
||
// Проверка подписи
|
||
const message = new SiweMessage({
|
||
domain: req.get('host'),
|
||
address: ethers.getAddress(address),
|
||
nonce: nonce,
|
||
chainId: 1
|
||
});
|
||
|
||
const isValid = await verifySignature(
|
||
message.prepareMessage(),
|
||
signature,
|
||
address
|
||
);
|
||
```
|
||
|
||
**Безопасность**:
|
||
- ✅ Приватный ключ **никогда** не покидает кошелек
|
||
- ✅ Каждый nonce используется **один раз**
|
||
- ✅ Nonce истекает через 5 минут
|
||
- ✅ Подделать подпись невозможно без приватного ключа
|
||
|
||
### Шифрование данных
|
||
|
||
```javascript
|
||
// AES-256 шифрование
|
||
const crypto = require('crypto');
|
||
|
||
function encrypt(text, key) {
|
||
const iv = crypto.randomBytes(16);
|
||
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
|
||
|
||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||
encrypted += cipher.final('hex');
|
||
|
||
return iv.toString('hex') + ':' + encrypted;
|
||
}
|
||
|
||
function decrypt(encrypted, key) {
|
||
const parts = encrypted.split(':');
|
||
const iv = Buffer.from(parts[0], 'hex');
|
||
const encryptedText = parts[1];
|
||
|
||
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
||
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
|
||
decrypted += decipher.final('utf8');
|
||
|
||
return decrypted;
|
||
}
|
||
```
|
||
|
||
**Что шифруется**:
|
||
- Адреса кошельков в БД
|
||
- Nonces для аутентификации
|
||
- Сессионные данные
|
||
- Приватные сообщения
|
||
|
||
### Rate Limiting
|
||
|
||
```javascript
|
||
const rateLimit = require('express-rate-limit');
|
||
|
||
// Ограничение запросов
|
||
const apiLimiter = rateLimit({
|
||
windowMs: 15 * 60 * 1000, // 15 минут
|
||
max: 100, // максимум 100 запросов
|
||
message: 'Слишком много запросов, попробуйте позже'
|
||
});
|
||
|
||
// Ограничение для аутентификации (более строгое)
|
||
const authLimiter = rateLimit({
|
||
windowMs: 15 * 60 * 1000,
|
||
max: 5, // максимум 5 попыток аутентификации
|
||
skipSuccessfulRequests: true
|
||
});
|
||
|
||
app.use('/api/', apiLimiter);
|
||
app.use('/api/auth/', authLimiter);
|
||
```
|
||
|
||
### CSRF защита
|
||
|
||
```javascript
|
||
const csrf = require('csurf');
|
||
|
||
// CSRF токены для всех форм
|
||
const csrfProtection = csrf({ cookie: true });
|
||
|
||
app.post('/api/action', csrfProtection, async (req, res) => {
|
||
// Проверка CSRF токена автоматическая
|
||
});
|
||
```
|
||
|
||
### XSS защита
|
||
|
||
```javascript
|
||
import DOMPurify from 'dompurify';
|
||
|
||
// Очистка HTML от опасного кода
|
||
function sanitizeHTML(html) {
|
||
return DOMPurify.sanitize(html, {
|
||
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
|
||
ALLOWED_ATTR: ['href']
|
||
});
|
||
}
|
||
|
||
// Использование
|
||
const userInput = req.body.comment;
|
||
const safeHTML = sanitizeHTML(userInput);
|
||
```
|
||
|
||
### Helmet.js для защиты заголовков
|
||
|
||
```javascript
|
||
const helmet = require('helmet');
|
||
|
||
app.use(helmet({
|
||
contentSecurityPolicy: {
|
||
directives: {
|
||
defaultSrc: ["'self'"],
|
||
scriptSrc: ["'self'", "'unsafe-inline'"],
|
||
styleSrc: ["'self'", "'unsafe-inline'"],
|
||
imgSrc: ["'self'", "data:", "https:"],
|
||
}
|
||
},
|
||
hsts: {
|
||
maxAge: 31536000,
|
||
includeSubDomains: true,
|
||
preload: true
|
||
}
|
||
}));
|
||
```
|
||
|
||
### Clean Logs система
|
||
|
||
**Автоматическая очистка логов от чувствительных данных:**
|
||
|
||
```javascript
|
||
function cleanLogs() {
|
||
const sensitivePatterns = [
|
||
/0x[a-fA-F0-9]{40}/g, // Адреса кошельков
|
||
/pk_[a-zA-Z0-9]{64}/g, // Приватные ключи (если случайно залогированы)
|
||
/nonce:\s*[a-f0-9]{64}/gi, // Nonces
|
||
];
|
||
|
||
// Замена чувствительных данных на ***
|
||
let cleanedLog = logContent;
|
||
sensitivePatterns.forEach(pattern => {
|
||
cleanedLog = cleanedLog.replace(pattern, '[REDACTED]');
|
||
});
|
||
|
||
return cleanedLog;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Управление модулями
|
||
|
||
### Только смарт-контракт DLE управляет модулями
|
||
|
||
**Модификатор `onlyDLE`** гарантирует, что только смарт-контракт DLE может вызывать функции модуля:
|
||
|
||
```solidity
|
||
// Модуль: TreasuryModule
|
||
contract TreasuryModule {
|
||
address public immutable dleContract;
|
||
|
||
modifier onlyDLE() {
|
||
require(msg.sender == dleContract, "Only DLE can call");
|
||
_;
|
||
}
|
||
|
||
// Только DLE может переводить токены из казны
|
||
function transferTokens(
|
||
address token,
|
||
address recipient,
|
||
uint256 amount
|
||
) external onlyDLE {
|
||
IERC20(token).transfer(recipient, amount);
|
||
}
|
||
}
|
||
```
|
||
|
||
**Что это означает**:
|
||
- ❌ Владелец кошелька **НЕ может** напрямую вызвать функции модуля
|
||
- ❌ Backend **НЕ может** вызвать функции модуля
|
||
- ❌ Злоумышленник **НЕ может** взаимодействовать с модулем
|
||
- ✅ **Только** смарт-контракт DLE через голосование
|
||
|
||
### Процесс использования модуля
|
||
|
||
```
|
||
1. Токен-холдер создает предложение
|
||
└── "Перевести 1000 USDC из Treasury на маркетинг"
|
||
|
||
2. Предложение содержит закодированный вызов функции модуля
|
||
└── operation = treasuryModule.transferTokens(USDC, marketing, 1000)
|
||
|
||
3. Голосование
|
||
|
||
4. Если одобрено, DLE контракт вызывает модуль:
|
||
└── TreasuryModule.transferTokens() ✅
|
||
└── msg.sender = DLE контракт ✅
|
||
|
||
5. Модуль проверяет msg.sender и исполняет
|
||
```
|
||
|
||
### Добавление нового модуля
|
||
|
||
**Только через голосование:**
|
||
|
||
```solidity
|
||
// Операция добавления модуля
|
||
function _addModule(bytes32 moduleId, address moduleAddress) internal {
|
||
require(msg.sender == address(this), "Only through voting");
|
||
require(moduleAddress != address(0), "Invalid address");
|
||
require(!activeModules[moduleId], "Module already exists");
|
||
|
||
modules[moduleId] = moduleAddress;
|
||
activeModules[moduleId] = true;
|
||
|
||
emit ModuleAdded(moduleId, moduleAddress);
|
||
}
|
||
```
|
||
|
||
**Процесс**:
|
||
1. Деплой нового модуля
|
||
2. Создание предложения: "Добавить модуль X по адресу 0x..."
|
||
3. Голосование
|
||
4. Если одобрено → модуль добавлен
|
||
5. Модуль можно использовать
|
||
|
||
### Удаление скомпрометированного модуля
|
||
|
||
Если модуль оказался уязвимым:
|
||
|
||
```
|
||
1. Токен-холдер создает экстренное предложение
|
||
└── "Удалить скомпрометированный модуль X"
|
||
└── Длительность: 1 день (экстренное голосование)
|
||
|
||
2. Голосование (ускоренное)
|
||
|
||
3. Модуль удален
|
||
└── Больше нельзя вызвать из DLE контракта
|
||
```
|
||
|
||
---
|
||
|
||
## Аудит и мониторинг
|
||
|
||
### Прозрачность на блокчейне
|
||
|
||
**Все действия записаны навсегда:**
|
||
|
||
```solidity
|
||
// События для аудита
|
||
event ProposalCreated(uint256 proposalId, address initiator);
|
||
event ProposalVoted(uint256 proposalId, address voter, bool support);
|
||
event ProposalExecuted(uint256 proposalId, bytes operation);
|
||
event ModuleAdded(bytes32 moduleId, address moduleAddress);
|
||
event TokensTransferred(address recipient, uint256 amount);
|
||
```
|
||
|
||
**Проверка в блокчейн-сканере:**
|
||
```
|
||
https://etherscan.io/address/0xDLE_CONTRACT_ADDRESS
|
||
└── Все транзакции видны публично
|
||
└── Невозможно скрыть или удалить
|
||
```
|
||
|
||
### Мониторинг в реальном времени
|
||
|
||
**Backend отслеживает все события:**
|
||
|
||
```javascript
|
||
// Подписка на события смарт-контракта
|
||
dleContract.on('ProposalCreated', async (proposalId, initiator, event) => {
|
||
logger.info(`Новое предложение #${proposalId} от ${initiator}`);
|
||
|
||
// Получаем детали предложения
|
||
const proposal = await dleContract.proposals(proposalId);
|
||
|
||
// Анализ безопасности
|
||
const risks = analyzeProposalRisks(proposal);
|
||
|
||
// Уведомление токен-холдеров
|
||
if (risks.level === 'HIGH') {
|
||
await notifyAllTokenHolders({
|
||
type: 'SECURITY_ALERT',
|
||
proposalId,
|
||
risks
|
||
});
|
||
}
|
||
});
|
||
```
|
||
|
||
### Система алертов
|
||
|
||
```javascript
|
||
// Критичные события требуют немедленного уведомления
|
||
const criticalEvents = [
|
||
'ProposalCreated', // Новое предложение
|
||
'ModuleAdded', // Добавлен модуль
|
||
'TokensTransferred' // Переведены токены
|
||
];
|
||
|
||
async function sendAlert(event, data) {
|
||
// Email уведомления
|
||
await sendEmail({
|
||
to: allTokenHolders,
|
||
subject: `[DLE Alert] ${event}`,
|
||
body: formatAlert(data)
|
||
});
|
||
|
||
// Telegram уведомления
|
||
await sendTelegram({
|
||
chat: dleNotificationsChat,
|
||
message: formatAlert(data)
|
||
});
|
||
|
||
// Запись в логи
|
||
logger.warn(`ALERT: ${event}`, data);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Рекомендации по безопасности
|
||
|
||
### Для владельцев токенов
|
||
|
||
1. **Используйте hardware wallet** (Ledger, Trezor)
|
||
- Приватный ключ никогда не покидает устройство
|
||
- Подтверждение каждой операции на физическом устройстве
|
||
|
||
2. **Храните seed-фразу безопасно**
|
||
- Напишите на бумаге, не храните в цифровом виде
|
||
- Используйте металлические пластины для долговечности
|
||
- Храните в сейфе или банковской ячейке
|
||
|
||
3. **Включите уведомления**
|
||
- Email алерты на новые предложения
|
||
- Telegram боты для критичных событий
|
||
|
||
4. **Проверяйте все предложения**
|
||
- Читайте описание перед голосованием
|
||
- Проверяйте адреса получателей
|
||
- Используйте timelock для критичных операций
|
||
|
||
5. **Разделите токены**
|
||
- Горячий кошелек: 10-20% для повседневного использования
|
||
- Холодный кошелек: 80-90% для долгосрочного хранения
|
||
|
||
### Для администраторов
|
||
|
||
1. **Регулярные обновления**
|
||
```bash
|
||
# Еженедельное обновление
|
||
docker-compose pull
|
||
docker-compose up -d
|
||
```
|
||
|
||
2. **Резервные копии**
|
||
```bash
|
||
# Ежедневный бэкап базы данных
|
||
docker exec dapp-postgres pg_dump -U user db > backup.sql
|
||
```
|
||
|
||
3. **Мониторинг логов**
|
||
```bash
|
||
# Проверка логов на подозрительную активность
|
||
docker logs dapp-backend | grep -i "error\|warning\|failed"
|
||
```
|
||
|
||
4. **Ротация ключей шифрования**
|
||
- Меняйте ключ шифрования раз в год
|
||
- Храните старые ключи для расшифровки исторических данных
|
||
|
||
5. **Firewall настройка**
|
||
```bash
|
||
# Разрешить только необходимые порты
|
||
ufw allow 80/tcp # HTTP
|
||
ufw allow 443/tcp # HTTPS
|
||
ufw enable
|
||
```
|
||
|
||
### Для разработчиков
|
||
|
||
1. **✅ Аудит смарт-контрактов**
|
||
- Используйте Slither для статического анализа
|
||
- Mythril для поиска уязвимостей
|
||
- Ручной аудит критичного кода
|
||
|
||
2. **Тестирование**
|
||
```bash
|
||
# Запуск тестов
|
||
cd backend
|
||
yarn test
|
||
|
||
# Покрытие кода
|
||
yarn coverage
|
||
```
|
||
|
||
3. **Код-ревью**
|
||
- Все изменения через pull requests
|
||
- Минимум 2 ревьюера для критичных изменений
|
||
|
||
4. **Безопасные зависимости**
|
||
```bash
|
||
# Проверка уязвимостей
|
||
yarn audit
|
||
npm audit fix
|
||
```
|
||
|
||
---
|
||
|
||
## Сценарии атак и защита
|
||
|
||
### Сценарий 1: Фишинговая атака
|
||
|
||
**Атака:**
|
||
```
|
||
1. Злоумышленник создает поддельный сайт: dlle.com (вместо dle.com)
|
||
2. Отправляет фишинговые письма токен-холдерам
|
||
3. Просит подключить кошелек и подписать транзакцию
|
||
```
|
||
|
||
**Защита:**
|
||
```javascript
|
||
// Backend проверяет домен в SIWE сообщении
|
||
const message = new SiweMessage({
|
||
domain: req.get('host'), // Должен быть правильный домен
|
||
uri: req.get('origin')
|
||
});
|
||
|
||
// Frontend показывает предупреждение
|
||
if (window.location.hostname !== 'dle.app') {
|
||
alert('ВНИМАНИЕ: Это НЕ официальный сайт DLE!');
|
||
}
|
||
```
|
||
|
||
**Рекомендации пользователям:**
|
||
- ✅ Всегда проверяйте URL перед подключением кошелька
|
||
- ✅ Используйте закладки браузера
|
||
- ✅ Проверяйте SSL сертификат (зеленый замок)
|
||
|
||
### Сценарий 2: Взлом backend сервера
|
||
|
||
**Атака:**
|
||
```
|
||
1. Злоумышленник получает доступ к backend серверу
|
||
2. Пытается изменить код для обхода проверки токенов
|
||
3. Или полностью выводит веб-приложение из строя
|
||
```
|
||
|
||
**Защита:**
|
||
```
|
||
✅ Проверка токенов происходит на блокчейне (неизменяемо)
|
||
✅ Backend может быть скомпрометирован, но:
|
||
├── Невозможно изменить баланс токенов в смарт-контракте
|
||
├── Невозможно создать поддельные токены
|
||
├── Невозможно изменить правила смарт-контракта
|
||
└── Пользователи увидят расхождение между backend и блокчейном
|
||
```
|
||
|
||
**Критично: Веб-приложение - это только интерфейс!**
|
||
|
||
Даже при **полном взломе** веб-приложения:
|
||
- ✅ **Все активы бизнеса защищены** на блокчейне
|
||
- ✅ **Смарт-контракты продолжают работать** независимо от веб-приложения
|
||
- ✅ **Можно управлять через блокчейн-сканеры** (Etherscan, Polygonscan, и др.)
|
||
|
||
### Управление через блокчейн-сканеры
|
||
|
||
**Если веб-приложение недоступно, используйте блокчейн-сканеры:**
|
||
|
||
#### 1. Проверка баланса токенов
|
||
|
||
```
|
||
1. Откройте Etherscan: https://etherscan.io
|
||
2. Введите адрес смарт-контракта DLE
|
||
3. Перейдите на вкладку "Read Contract"
|
||
4. Вызовите функцию balanceOf(address)
|
||
5. Введите адрес вашего кошелька
|
||
6. Нажмите "Query" - увидите баланс токенов ✅
|
||
```
|
||
|
||
**Пример**:
|
||
```
|
||
Contract Address: 0x1234...DLE
|
||
Function: balanceOf
|
||
Address: 0xYourWallet...
|
||
Result: 500000 (ваш баланс токенов)
|
||
```
|
||
|
||
#### 2. Создание предложения через Etherscan
|
||
|
||
```
|
||
1. Откройте Etherscan и найдите смарт-контракт DLE
|
||
2. Перейдите на вкладку "Write Contract"
|
||
3. Подключите кошелек MetaMask
|
||
4. Найдите функцию createProposal
|
||
5. Заполните параметры:
|
||
- description: "Восстановить веб-приложение"
|
||
- operation: закодированный вызов функции
|
||
- votingDuration: 86400 (1 день в секундах)
|
||
6. Нажмите "Write" и подтвердите транзакцию ✅
|
||
```
|
||
|
||
**Пример создания предложения**:
|
||
```solidity
|
||
// Через Etherscan Write Contract
|
||
createProposal(
|
||
"Перевести 10,000 USDT на восстановление инфраструктуры",
|
||
0x..., // закодированный вызов transferTokens
|
||
86400 // 1 день
|
||
)
|
||
```
|
||
|
||
#### 3. Голосование через Etherscan
|
||
|
||
```
|
||
1. Откройте смарт-контракт DLE на Etherscan
|
||
2. Вкладка "Write Contract"
|
||
3. Подключите кошелек
|
||
4. Функция vote(uint256 proposalId, bool support)
|
||
- proposalId: номер предложения (например, 5)
|
||
- support: true (за) или false (против)
|
||
5. Нажмите "Write" ✅
|
||
```
|
||
|
||
#### 4. Проверка состояния предложения
|
||
|
||
```
|
||
1. Вкладка "Read Contract" на Etherscan
|
||
2. Функция proposals(uint256)
|
||
3. Введите ID предложения
|
||
4. Увидите все детали:
|
||
- Описание
|
||
- Голоса "За" и "Против"
|
||
- Статус (Active, Executed, Failed)
|
||
- Время окончания голосования
|
||
```
|
||
|
||
#### 5. Исполнение одобренного предложения
|
||
|
||
```
|
||
1. Если предложение одобрено и время истекло
|
||
2. Откройте "Write Contract"
|
||
3. Функция execute(uint256 proposalId)
|
||
4. Введите ID предложения
|
||
5. Нажмите "Write" - предложение исполнится ✅
|
||
```
|
||
|
||
### Популярные блокчейн-сканеры для DLE
|
||
|
||
| Сеть | Блокчейн-сканер | URL |
|
||
|------|-----------------|-----|
|
||
| **Ethereum Mainnet** | Etherscan | https://etherscan.io |
|
||
| **Polygon** | Polygonscan | https://polygonscan.com |
|
||
| **Binance Smart Chain** | BscScan | https://bscscan.com |
|
||
| **Arbitrum** | Arbiscan | https://arbiscan.io |
|
||
| **Optimism** | Optimistic Etherscan | https://optimistic.etherscan.io |
|
||
| **Avalanche** | SnowTrace | https://snowtrace.io |
|
||
| **Base** | BaseScan | https://basescan.org |
|
||
|
||
### Преимущества управления через блокчейн-сканеры
|
||
|
||
**1. Полная независимость от веб-приложения**
|
||
```
|
||
Веб-приложение взломано → Блокчейн работает ✅
|
||
Backend сервер упал → Смарт-контракты работают ✅
|
||
Frontend недоступен → Можно управлять через Etherscan ✅
|
||
```
|
||
|
||
**2. Невозможно подделать данные**
|
||
```
|
||
Злоумышленник на backend → Блокчейн-сканер показывает правду
|
||
Поддельный frontend → Etherscan покажет реальные токены
|
||
Измененная логика → Смарт-контракт работает как задумано
|
||
```
|
||
|
||
**3. Доступ 24/7 из любой точки мира**
|
||
```
|
||
Нет доступа к серверу → Etherscan доступен всегда
|
||
Приложение на обслуживании → Управление через блокчейн
|
||
DDoS атака на сайт → Блокчейн недоступен для DDoS
|
||
```
|
||
|
||
**Реакция на взлом:**
|
||
1. Пользователи замечают странное поведение веб-приложения
|
||
2. Проверяют баланс токенов напрямую через Etherscan ✅
|
||
3. Создают предложение через Etherscan на восстановление
|
||
4. Голосуют за восстановление инфраструктуры
|
||
5. Администраторы восстанавливают backend из резервной копии
|
||
6. **Никакие токены и активы не потеряны** ✅
|
||
|
||
**Вывод**: Веб-приложение - это лишь удобный интерфейс. Реальная власть и активы находятся на блокчейне, где они защищены криптографией и невозможно их изменить или украсть через взлом веб-приложения.
|
||
|
||
### Сценарий 3: 51% атака
|
||
|
||
**Атака:**
|
||
```
|
||
1. Злоумышленник покупает или получает >51% всех токенов
|
||
2. Создает предложение "Перевести все активы мне"
|
||
3. Голосует всеми своими токенами "За"
|
||
4. Предложение одобрено
|
||
```
|
||
|
||
**Защита:**
|
||
```javascript
|
||
// Timelock Module дает время на реакцию
|
||
const TIMELOCK = 7 days;
|
||
|
||
// Другие токен-холдеры могут:
|
||
1. Проголосовать "Против" (если у них еще 49%)
|
||
2. Создать контр-предложение
|
||
3. Обратиться в правоохранительные органы
|
||
4. Отменить предложение через emergency функцию (если настроена)
|
||
```
|
||
|
||
**Долгосрочная защита:**
|
||
- Равномерное распределение токенов между партнерами
|
||
- Мультиподпись для крупных операций
|
||
- Квадратичное голосование (опция для будущих версий)
|
||
|
||
### Сценарий 4: Social Engineering
|
||
|
||
**Атака:**
|
||
```
|
||
1. Злоумышленник выдает себя за поддержку DLE
|
||
2. Просит токен-холдера "для верификации" подписать сообщение
|
||
3. Сообщение на самом деле - одобрение перевода токенов
|
||
```
|
||
|
||
**Защита:**
|
||
```javascript
|
||
// Frontend всегда показывает ЧТО подписывается
|
||
function signMessage(message) {
|
||
// Показываем пользователю точное содержимое
|
||
const confirmation = confirm(`
|
||
Вы собираетесь подписать:
|
||
${message}
|
||
|
||
НИКОГДА не подписывайте сообщения по просьбе «поддержки»!
|
||
|
||
Продолжить?
|
||
`);
|
||
|
||
if (!confirmation) return;
|
||
|
||
// Подпись
|
||
}
|
||
```
|
||
|
||
**Рекомендации:**
|
||
- ❌ Поддержка **НИКОГДА** не попросит подписать сообщение
|
||
- ❌ Не доверяйте "срочным" запросам
|
||
- ✅ Всегда проверяйте, что подписываете
|
||
- ✅ При сомнениях - свяжитесь с официальной поддержкой
|
||
|
||
---
|
||
|
||
## Заключение
|
||
|
||
### Многоуровневая защита DLE
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ Уровни безопасности DLE │
|
||
├─────────────────────────────────────────────────────┤
|
||
│ 1️⃣ Блокчейн │
|
||
│ └── Токены нельзя украсть даже со взломом │
|
||
│ │
|
||
│ 2️⃣ Смарт-контракты │
|
||
│ └── Аудит, OpenZeppelin, иммутабельность │
|
||
│ │
|
||
│ 3️⃣ Голосование с кворумом │
|
||
│ └── Невозможно единоличное решение │
|
||
│ │
|
||
│ 4️⃣ Timelock │
|
||
│ └── Время на реакцию при атаке │
|
||
│ │
|
||
│ 5️⃣ Backend проверки │
|
||
│ └── Проверка токенов в реальном времени │
|
||
│ │
|
||
│ 6️⃣ Frontend защита │
|
||
│ └── XSS, CSRF, rate limiting │
|
||
│ │
|
||
│ 7️⃣ Мониторинг и алерты │
|
||
│ └── Детекция подозрительных действий │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Ключевые преимущества безопасности
|
||
|
||
1. **Контроль доступа через токены**
|
||
- Без токенов доступ невозможен
|
||
- Проверка в реальном времени на блокчейне
|
||
|
||
2. **Защита от кражи токенов**
|
||
- Невозможно перевести без голосования
|
||
- Даже при взломе кошелька токены защищены
|
||
|
||
3. **🌐 Независимость от веб-приложения**
|
||
- Даже при взломе веб-приложения активы бизнеса защищены на блокчейне
|
||
- Можно управлять через блокчейн-сканеры (Etherscan, Polygonscan и др.)
|
||
- Смарт-контракты работают независимо от frontend/backend
|
||
- Веб-приложение - это только удобный интерфейс, реальная власть на блокчейне
|
||
|
||
4. **Коллективное управление**
|
||
- Критичные решения только через голосование
|
||
- Кворум предотвращает единоличные действия
|
||
|
||
5. **Модули под контролем смарт-контракта**
|
||
- Только DLE контракт может вызывать модули
|
||
- Никакого прямого доступа
|
||
|
||
6. **📊 Полная прозрачность**
|
||
- Все действия на блокчейне
|
||
- Невозможно скрыть или подделать
|
||
|
||
### Следующие шаги
|
||
|
||
1. [Изучите техническую документацию](./back-docs/blockchain-integration-technical.md)
|
||
2. [Настройте безопасное окружение](./back-docs/setup-instruction.md)
|
||
3. [Прочитайте FAQ](https://github.com/VC-HB3-Accelerator/.github/blob/main/ru/FAQ.md)
|
||
4. [Получите поддержку](https://hb3-accelerator.com/)
|
||
---
|
||
|
||
**© 2024-2025 Тарабанов Александр Викторович. Все права защищены.**
|
||
|
||
**Последнее обновление**: October 2025
|
||
|