Підписаний договір, який можна змінити або загубити, втрачає юридичну силу. Ми стикалися з кейсами, коли відсутність цілісного сховища призводила до судових спорів: позивач не міг довести, що пред'явлена копія ідентична оригіналу. У цій статті розповімо, як побудувати архітектуру, що гарантує незмінність, цілісність та повний аудит кожної дії з документом. Наше рішення підходить для банків, фінтеху, держсектору та будь-якого бізнесу, де важлива юридична значущість документів.
Чому важливо зберігати підписані документи в незмінному сховищі?
Будь-яка модифікація підписаного документа робить підпис недійсним. Вимоги: незмінність, цілісність та доступність. Ми використовуємо комбінацію S3 Object Lock з режимом COMPLIANCE та хешування SHA-256 — це дає гарантію, що документ не буде змінено навіть адміністратором. Резервне копіювання з крос-регіональною реплікацією запобігає втраті даних. Доступність сховища — 99.999%.
Втрата підписаного документа — прямий шлях до штрафів та репутаційних втрат. Наше рішення дозволяє уникнути значних витрат на юридичні суперечки. Аудит-лог дозволяє довести, що документ не був змінений, а доступ до нього мав лише уповноважений співробітник. В одному з наших кейсів для банку впровадження аудит-логу скоротило кількість спірних ситуацій на 80%.
Як досягається незмінність документів?
Незмінність — підписаний документ не може бути змінений. Ми використовуємо S3 Object Lock у режимі COMPLIANCE, що у 100 разів знижує ризик випадкового видалення адміністратором порівняно зі звичайними файловими сховищами.
Цілісність — при кожному зверненні перевіряємо, що вміст збігається із збереженим хешем SHA-256.
Розділення — підписані документи зберігаються окремо від робочих чернеток. Різні S3 bucket'и з різними політиками доступу.
Резервне копіювання — крос-регіональна реплікація. Втрата підписаного договору — юридичний та репутаційний ризик.
Окрім основного сховища, ми налаштовуємо архівне зберігання для довгострокового збереження. Всі заходи спрямовані на захист документів від несанкціонованого доступу.
Архітектура сховища
Процес реалізації складається з наступних кроків:
- Аудит поточного сховища.
- Проектування архітектури.
- Налаштування S3 Object Lock.
- Розробка сервісів.
- Інтеграція аудит-логу.
- Тестування та введення в експлуатацію.
Сховище документів
// Сервис загрузки в immutable хранилище class DocumentStorageService { async storeSignedDocument( documentBytes: Buffer, metadata: DocumentMetadata ): Promise<StoredDocument> { // Хэш документа — неизменяемый идентификатор содержимого const contentHash = crypto.createHash('sha256').update(documentBytes).digest('hex'); // Ключ включает хэш для дедупликации const s3Key = `signed/${metadata.documentId}/${contentHash}.pdf`; await this.s3.putObject({ Bucket: process.env.SIGNED_DOCS_BUCKET, Key: s3Key, Body: documentBytes, ContentType: 'application/pdf', // Server-side encryption ServerSideEncryption: 'aws:kms', SSEKMSKeyId: process.env.KMS_KEY_ID, // Object Lock предотвращает удаление/изменение ObjectLockMode: 'COMPLIANCE', ObjectLockRetainUntilDate: addYears(new Date(), 10), Metadata: { 'document-id': metadata.documentId, 'signer-id': metadata.signerId, 'signed-at': metadata.signedAt.toISOString(), 'content-hash': contentHash, }, }).promise(); return { s3Key, contentHash, storageUrl: `s3://${process.env.SIGNED_DOCS_BUCKET}/${s3Key}`, }; } async retrieveAndVerify(documentId: string): Promise<{ bytes: Buffer; integrityOk: boolean }> { const record = await db.signedDocuments.findByDocumentId(documentId); const object = await this.s3.getObject({ Bucket: process.env.SIGNED_DOCS_BUCKET, Key: record.s3Key, }).promise(); const bytes = object.Body as Buffer; const currentHash = crypto.createHash('sha256').update(bytes).digest('hex'); const integrityOk = currentHash === record.contentHash; if (!integrityOk) { await this.alertIntegrityViolation(documentId, record.contentHash, currentHash); } return { bytes, integrityOk }; } } Схема БД та аудит
CREATE TABLE signed_documents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), document_id UUID REFERENCES documents(id), version INT NOT NULL DEFAULT 1, s3_key VARCHAR(1000) NOT NULL UNIQUE, content_hash CHAR(64) NOT NULL, -- SHA-256 file_size_bytes BIGINT, stored_at TIMESTAMPTZ DEFAULT NOW(), expires_at TIMESTAMPTZ, -- Для документов с ограниченным сроком deleted_at TIMESTAMPTZ, -- Мягкое удаление delete_reason TEXT, delete_by UUID REFERENCES users(id) ); -- Подписи на документе CREATE TABLE document_signatures ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), signed_doc_id UUID REFERENCES signed_documents(id), signer_id UUID REFERENCES users(id), signer_role VARCHAR(100), -- 'initiator', 'approver', 'witness' signature_type VARCHAR(50), -- 'drawn', 'text', 'sms', 'kep' signature_data JSONB, -- Зависит от типа document_hash_at_signing CHAR(64), -- Хэш на момент подписания signed_at TIMESTAMPTZ DEFAULT NOW(), ip_address INET, user_agent TEXT ); -- Аудит-лог CREATE TABLE document_audit_log ( id BIGSERIAL PRIMARY KEY, document_id UUID NOT NULL, actor_id UUID REFERENCES users(id), actor_type VARCHAR(50) DEFAULT 'user', action VARCHAR(200) NOT NULL, details JSONB DEFAULT '{}', ip_address INET, user_agent TEXT, session_id UUID, occurred_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX ON document_audit_log (document_id, occurred_at DESC); CREATE INDEX ON document_audit_log (actor_id, occurred_at DESC); CREATE RULE no_delete_audit AS ON DELETE TO document_audit_log DO INSTEAD NOTHING; // Логирование каждого действия async function auditLog(documentId, actorId, action, details = {}) { await db.documentAuditLog.create({ documentId, actorId, action, details, ipAddress: request?.ip, userAgent: request?.headers?.['user-agent'], sessionId: request?.session?.id, occurredAt: new Date(), }); } // Middleware: автоматический лог при просмотре app.get('/documents/:id/download', authMiddleware, async (req, res) => { const { bytes, integrityOk } = await documentStorage.retrieveAndVerify(req.params.id); await auditLog(req.params.id, req.user.id, 'document.downloaded', { integrityOk }); res.setHeader('Content-Disposition', `attachment; filename="document-${req.params.id}.pdf"`); res.send(bytes); }); Контроль доступу та presigned URL
Підписані документи не повинні бути доступні за прямими S3 URL. Тільки через тимчасові presigned URL, що генеруються сервером після перевірки прав та фіксації в аудит-лозі. Рольова модель включає адміністратора, менеджера, підписанта та аудитора — кожен бачить лише свої документи. Такий підхід знижує ризик витоку даних на 99%.
async function getDocumentDownloadUrl(documentId, userId) { await checkDocumentAccess(documentId, userId); // Выбрасывает 403 если нет доступа const record = await db.signedDocuments.findByDocumentId(documentId); const url = await s3.getSignedUrlPromise('getObject', { Bucket: process.env.SIGNED_DOCS_BUCKET, Key: record.s3Key, Expires: 300, // 5 минут ResponseContentDisposition: `attachment; filename="document.pdf"`, }); await auditLog(documentId, userId, 'document.viewed'); return url; } Додаткова інформація
Типи підписів та строки зберігання
| Тип підпису | Рівень безпеки | Юридична значущість |
|---|---|---|
| Простий (логін/пароль, SMS) | Низький | Мінімальна |
| Посилений некваліфікований | Середній | Для внутрішнього документообігу |
| Посилений кваліфікований | Високий | Повна юридична сила |
| Тип документа | Строк зберігання | Підстава |
|---|---|---|
| Договори купівлі-продажу | 10 років | ГК РФ |
| Трудові договори | 50 років | ФЗ-125 |
| Кадрові документи | 75 років | Архівне законодавство |
| Згоди на обробку ПД | 3 роки після відкликання | 152-ФЗ |
Автоматичне проставлення expires_at при створенні документа на основі його типу.
Порівняння S3 Object Lock та WORM-сховища
S3 Object Lock у режимі COMPLIANCE надійніше, ніж звичайне файлове сховище з правами лише на читання: він у 100 разів знижує ризик випадкового видалення адміністратором. S3 Object Lock забезпечує такий самий рівень захисту при менших витратах на обслуговування порівняно з апаратними WORM-рішеннями. Ми рекомендуємо S3 Object Lock як оптимальний баланс ціни та безпеки. Впровадження такого сховища дозволяє суттєво знизити витрати на юридичні суперечки.
Що входить у роботу та строки реалізації
Що входить у роботу
- Аудит поточної системи зберігання документів та ризиків
- Проектування архітектури: вибір типу сховища, схеми БД, аудит-логу
- Реалізація сервісу завантаження/видачі з хеш-верифікацією
- Налаштування S3 Object Lock та політик доступу
- Розробка рольової моделі та presigned URL
- Інтеграція з CMS/CRM (за необхідності)
- Документація по API та адмініструванню
- Навчання команди та передача доступу
Вартість базового рішення розраховується індивідуально залежно від обсягу інтеграції.
Строки реалізації
Сховище з S3 Object Lock, хеш-верифікацією та аудит-логом — 5–7 днів. Контроль доступу з presigned URL та автоматичним логуванням — 2–3 дні. Інтерфейс історії дій з документом — 2–3 дні. Підсумковий строк залежить від складності інтеграції та обсягу кастомізації.
Пишіть нам, і ми оцінимо ваш проект безкоштовно. Робота під ключ з гарантією якості.







