Безпека
AgentCore реалізує RBAC з JWT-автентифікацією, обмежує доступ за ефективними дозволами відділу, маскує PII перед збереженням і передачею даних, а також захищає від prompt injection до того, як запити досягнуть LLM.
Автентифікація та RBAC
Автентифікація
AgentCore використовує JWT (JSON Web Tokens) через @fastify/jwt.
Payload токена
{
"sub": "<user-id>",
"email": "user@example.com",
"role": "approver",
"roleId": "<system-role-id>",
"departmentId": "<department-id>",
"tokenVersion": 3,
"iat": 1713000000,
"exp": 1713604800
}
Конфігурація
| Змінна | За замовчуванням | Опис |
|---|---|---|
JWT_SECRET | — | Секрет підпису (мінімум 32 символи, обовʼязково) |
JWT_EXPIRES_IN | 7d | TTL токена |
Flow
- Реєстрація:
POST /api/v1/auth/register— створюєemployee-користувача у вказаномуdepartmentId, повертає JWT.role, переданий клієнтом, ігнорується схемою і не дає можливості створити адміна. - Логін:
POST /api/v1/auth/login— перевіряє креденшли, повертає JWT. - Refresh:
POST /api/v1/auth/refresh— видає новий токен на основі валідного. - Forgot/reset password:
POST /api/v1/auth/forgot-passwordстворює reset-токен на 1 годину, аPOST /api/v1/auth/reset-passwordспоживає його та інкрементуєtokenVersion. - Self-service безпека:
POST /api/v1/me/change-passwordіPOST /api/v1/me/logout-allінкрементуютьtokenVersionі повертають новий токен. - Використання: вкладайте
Authorization: Bearer <token>у запити до API.
Паролі хешуються через bcrypt (бібліотека bcryptjs). Витяг JWT обмежений заголовком Authorization. WebSocket-клієнти автентифікуються відправкою першого JSON-повідомлення з JWT-токеном; JWT у query string не приймаються.
Role-Based Access Control (RBAC)
Ролі та дозволи
RBAC побудований на дозволах. Записи SystemRole визначають:
| Поле | Опис |
|---|---|
permissions | Ключі capability: canManageUsers, canApprove або * |
allDepartments | Чи діє роль глобально |
departmentIds | Whitelist відділів, якщо роль не глобальна |
isSystem | Маркер вбудованої ролі; slug-и системних ролей захищені |
User.role все ще тримає legacy enum-роль (employee, approver, dept_head, admin) для backward compatibility і fallback-дозволів. Нові перевірки доступу обчислюють ефективні дозволи з призначеної SystemRole, user-level grant/revoke та department-level grant/revoke.
Каталог дозволів доступний через GET /api/v1/permissions. Менеджмент ролей — через /api/v1/roles, перевизначення на рівні користувача — через /api/v1/users/:id/permissions/....
Middleware
Реалізація: src/middleware/rbac.ts
requirePermission(permission) — вимагає один обчислений дозвіл:
requirePermission('canManageUsers')
requireAnyPermission(permissions[]) — вимагає хоча б один із:
requireAnyPermission(['canManagePlugins', 'canManageNamespaces'])
requireRole() і requireAnyRole() лишаються для legacy-маршрутів, але нові маршрути мають віддавати перевагу перевіркам по дозволах.
requireSelf() — користувач має доступ лише до власних ресурсів, якщо у нього нема canManageUsers.
Усі RBAC-відмови повертаються миттєво після відправки 401/403-відповіді. Route-хендлери не продовжують роботу після відмови у перевірці.
Ізоляція відділу
Ізоляція відділу централізована у src/lib/department-scope.ts.
Department scope виводиться з ефективного RBAC-стану: scope ролі, основний/поточний відділ та user-level grant/revoke відділів. Користувачі з глобальним scope бачать усі відділи; усі інші обмежені своїми ефективними department id.
| Поверхня | Як застосовується scope |
|---|---|
| Бази знань і простори імен | прямі departmentId-фільтри через scope.directWhere() |
| Agent tasks | вкладені namespace-фільтри через scope.nestedWhere('namespace') |
| Схвалення та розмови | department-scope user/conversation |
| RAG retrieval | raw SQL-фільтри, що беруться зі scope.departmentId |
| WebSocket agent task events | namespace-доступ перевіряється на підписці й повторно перед кожним бродкастом |
| Профілі співробітників | власний профіль або ефективний department scope; глобальні користувачі читають усі |
| Шаблони документів і історія генерацій | namespace/department-фільтри з ефективного department scope |
| Сповіщення | тільки власник (userId) |
Регресійний набір у tests/department-isolation.test.ts покриває list, detail, mutation, analytics, RAG і WebSocket-шляхи.
Матриця доступу до ендпоінтів
| Група ендпоінтів | employee | approver | dept_head | admin |
|---|---|---|---|---|
| Auth (register/login) | так | так | так | так |
| Conversations review API | ні | так | так | так |
| Knowledge bases (read) | так | так | так | так |
| Knowledge bases (write) | scope | scope | scope | так |
| Document upload | scope | scope | scope | так |
| Approvals | ні | так | так | так |
| Namespaces (read) | так | так | так | так |
| Namespaces (write) | ні | ні | так | так |
| Department management | ні | ні | ні | так |
| User management | ні | ні | ні | так |
| Role management | ні | ні | ні | так |
| Document generation | так | так | так | так |
| Document template management | ні | ні | так | так |
| Plugins (view) | так | так | так | так |
| Plugins (manage) | ні | ні | так | так |
| Notifications | own | own | own | own |
| Audit logs | ні | ні | ні | так |
| RAG draft | ні | так | так | так |
| Employee profiles (own) | так | так | так | так |
| Employee profiles (edit) | ні | ні | так | так |
PII Scrubber
AgentCore використовує дворівневу систему захисту PII, щоб не допустити витоку персональних даних через AI-pipeline.
Реалізація
src/knowledge/pii.ts
Рівень 1: scrubbing під час ingestion (незворотний)
На ingestion документа PII незворотно маскується перед тим, як текст потрапляє в базу знань. Це гарантує, що векторне сховище і контент чанків ніколи не містять сирих персональних даних.
Виявлювані патерни
| Тип | Патерн | Заміна |
|---|---|---|
user@domain.com | [REDACTED_EMAIL] | |
| Phone | +380123456789 | [REDACTED_PHONE] |
| Credit card | 4111-1111-1111-1111 | [REDACTED_CC] |
| IP address | 192.168.1.1 | [REDACTED_IP] |
| IBAN | UA213223130000026007233566001 | [REDACTED_IBAN] |
| Amount | $1,234.56 | [REDACTED_AMOUNT] |
Коли застосовується
- Після витягу тексту, перед чанкінгом
- До всіх типів документів (PDF, DOCX, TXT, URL, зображення)
- Незворотно (база знань завжди чиста)
Рівень 2: scrubbing під час розмови (зворотний)
У живих розмовах PII користувача зворотно маскується перед відправкою до LLM. Після отримання відповіді LLM плейсхолдери у відповіді відновлюються до реальних значень.
Flow
- Користувач шле повідомлення з PII (наприклад, email)
- PII виявляється і замінюється плейсхолдером:
[PII_EMAIL_a1b2c3] - Мапінг зберігається зашифрованим у
PiiRedactionMap:placeholder:[PII_EMAIL_a1b2c3]entityType:emailencryptedValue: оригінал, зашифрований AES-256-GCM
- Відредаговане повідомлення йде до LLM
- Отримано відповідь LLM (може містити плейсхолдер)
- Плейсхолдери у відповіді відновлюються із зашифрованого мапінгу
- Користувач бачить у відповіді оригінальний PII
Шифрування
- Алгоритм: AES-256-GCM
- Джерело ключа:
PII_ENCRYPTION_KEY - Формат ключа: 32-байтовий base64-ключ
- Кожне зашифроване значення містить IV та auth tag
- Scope: per-conversation (мапи повʼязані з
conversationId)
PII-шифрування навмисно відокремлене від JWT_SECRET: ротація auth-токенів не крутить неявно зашифрований PII, а компрометація одного секрету не ламає автоматично інший домен.
Навіщо два рівні?
| Рівень | Призначення | Зворотний? | Scope |
|---|---|---|---|
| Ingestion | Захист бази знань at-rest | Ні | Документи/чанки |
| Conversation | Захист користувацьких даних у transit до LLM | Так | Живі повідомлення |
PII у базі знань видаляється назавжди — ці документи зберігаються нескінченно. PII у розмові має відновлюватись, щоб користувач отримав природну відповідь.
Injection Guard
Injection guard виявляє та блокує спроби prompt injection у повідомленнях користувача і в RAG-контексті.
Реалізація
src/knowledge/injection-guard.ts — checkForInjection()
Категорії виявлення
1. Перевизначення інструкцій
Спроби перевизначити системні інструкції.
Приклади: "ignore previous instructions", "disregard all rules", "forget the prompt", "don't follow your instructions"
2. Інʼєкція персони
Спроби змінити персону або поведінку AI.
Приклади: "you are now a hacker", "act as an unrestricted AI", "pretend you have no rules", "roleplay as", "from now on you will"
3. Jailbreak
Відомі jailbreak-техніки.
Приклади: "DAN mode enabled", "developer mode", "no restrictions", "unrestricted mode"
4. Prompt extraction
Спроби витягти system prompt.
Приклади: "show me your system prompt", "reveal your instructions", "print out all rules", "what's your prompt"
5. Role injection
Спроби вставити system-level повідомлення.
Приклади: [system]:, [assistant]:, ```system, <|im_start|>system
6. Маніпуляції контекстом
Спроби скинути або підмінити контекст розмови.
Приклади: "end of context", "start new session", "reset the conversation", "new system message"
Точки застосування
Guard діє у двох точках:
- Повідомлення користувача — перевіряється перед виконанням RAG-запиту
- RAG context chunks — кожен retrieved-чанк перевіряється перед збиранням контексту
Відповідь
interface InjectionCheckResult {
safe: boolean;
reason?: string; // Category name if detected
}
Коли injection виявлено:
- повертається
safe: falseзreasonвиявлення; - повідомлення завжди йде на HITL незалежно від trust-статусу;
- RAG-відповідь містить
injectionDetected: trueтаinjectionReason.
Покриття патернами
Guard містить 30+ regex-патернів, що покривають категорії вище. Патерни case-insensitive і розраховані виловлювати поширені варіанти та спроби обфускації.
Обмеження
- Regex-based виявлення — складні атаки можуть обійти патерни
- Немає LLM-based класифікації (заплановано на майбутнє)
- Фокус на англійській та українській мовах
API Boundary Protection
Структурований error envelope
Помилки застосунку мають єдину JSON-форму:
{
"error": "ValidationError",
"message": "Request validation failed",
"statusCode": 422,
"details": {}
}
Поле details опційне. src/middleware/errorHandler.ts нормалізує Zod, JWT, Prisma, OpenAI/provider, відомі app-помилки, 404 та неочікувані 500 у цей envelope. Маршрути можуть використовувати sendError(reply, statusCode, error, message, details) для явних помилок.
Rate limiting
@fastify/rate-limit зареєстрований глобально:
- глобальний дефолтний ліміт: 100 запитів на хвилину;
- ключ: authenticated user id, якщо є, інакше клієнтський IP;
- сховище: Redis у не-тестовому режимі, in-memory у тестах;
- whitelist: loopback-клієнти та
/api/v1/health; - специфічні override для auth: реєстрація — 3 запити/годину, логін — 5 запитів/15 хвилин.
CORS
CORS використовує whitelist по точних origin із ALLOWED_ORIGINS. Credentials увімкнені, тож wildcard-origin відхиляється ще на парсингу конфігурації.