@ycforge/orm-security-providers
v0.2.0
Published
Encryption & blind-index providers for @ycforge/ydb-orm (Yandex Cloud KMS, HMAC-SHA256)
Maintainers
Readme
@ycforge/orm-security-providers
Encryption & blind-index providers for @ycforge/ydb-orm v0.2+.
Install
npm install @ycforge/orm-security-providers @ycforge/ydb-orm
# or
yarn add @ycforge/orm-security-providers @ycforge/ydb-ormТребует
@ycforge/ydb-orm >= 0.2.0-beta.0 < 0.3.0: шифротекст хранится в YDB-колонкеBytesкак rawUint8Array(без base64). Base64 используется только на границе с KMS REST API.
Providers
| Subpath | Exports | Description |
|---------|---------|-------------|
| @ycforge/orm-security-providers/yandex-kms | KmsEncryptionProvider, IamTokenManager | Encrypt / decrypt via Yandex Cloud KMS SymmetricCrypto REST API |
| @ycforge/orm-security-providers/hmac-bi | KmsBlindIndexProvider | Deterministic HMAC-SHA256 blind indexes |
Import from the specific subpath:
import { KmsEncryptionProvider } from '@ycforge/orm-security-providers/yandex-kms';
import { KmsBlindIndexProvider } from '@ycforge/orm-security-providers/hmac-bi';Or from the root (re-exports everything):
import { KmsEncryptionProvider, KmsBlindIndexProvider } from '@ycforge/orm-security-providers';Quick Start (NestJS)
import { Module } from '@nestjs/common';
import { YdbCoreModule, YdbModule } from '@ycforge/ydb-orm';
import { KmsEncryptionProvider } from '@ycforge/orm-security-providers/yandex-kms';
import { KmsBlindIndexProvider } from '@ycforge/orm-security-providers/hmac-bi';
@Module({
imports: [
YdbCoreModule.forRootAsync({
useFactory: () => ({
endpoint: process.env.YDB_ENDPOINT!,
auth_type: 'auth_key',
authOptions: {
authorized_key_path: './authorized_key.json',
},
encryptionProvider: new KmsEncryptionProvider({
keyId: process.env.KMS_KEY_ID!,
auth_type: 'auth_key',
authOptions: { authorized_key_path: './authorized_key.json' },
}),
blindIndexProvider: new KmsBlindIndexProvider({
blindIndexKey: process.env.KMS_BLIND_INDEX_KEY!,
}),
}),
}),
YdbModule.forFeature([UserEntity]),
],
})
export class AppModule {}Quick Start (standalone)
import { configureEntities, createDriver, createExecutor } from '@ycforge/ydb-orm';
const driver = await createDriver({ endpoint: '...', auth_type: 'meta', authOptions: {} });
const executor = createExecutor(driver, { endpoint: '...', auth_type: 'meta', authOptions: {} });
configureEntities([UserEntity], {
executor,
encryptionProvider: new KmsEncryptionProvider({ /* ... */ }),
blindIndexProvider: new KmsBlindIndexProvider({ /* ... */ }),
});Contract (ydb-orm v0.2+)
interface YdbEncryptionProvider {
encrypt(plaintext: string, aad: string, context: YdbEncryptionContext): Promise<Uint8Array>;
decrypt(ciphertext: Uint8Array, aad: string, context: YdbEncryptionContext): Promise<string>;
}
interface YdbBlindIndexProvider {
hash(plaintext: string, context: YdbEncryptionContext): Promise<string>;
}KmsEncryptionProvider возвращает и принимает raw ciphertext (Uint8Array) —
ORM кладёт его в колонку Bytes без перекодирований. Преобразование base64
выполняется только внутри адаптера при обмене с KMS REST API.
@ycforge/orm-security-providers/yandex-kms
KmsEncryptionProvider
new KmsEncryptionProvider(options: KmsEncryptionProviderOptions)| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| keyId | string | yes | — | KMS symmetric key ID |
| auth_type | 'meta' \| 'auth_key' \| 'iam_token' | yes | — | Auth method |
| authOptions | KmsAuthOptions | yes | — | Auth parameters |
| apiEndpoint | string | no | https://kms.yandex | KMS API base URL |
Methods:
encrypt(plaintext: string, aad: string, context: YdbEncryptionContext): Promise<Uint8Array>
decrypt(ciphertext: Uint8Array, aad: string, context: YdbEncryptionContext): Promise<string>Authentication
| Mode | Description | Required options |
|------|-------------|-----------------|
| meta | VM metadata service (works only inside Yandex Cloud VMs) | none |
| auth_key | Service-account JSON key → JWT → IAM token exchange | authorized_key_path |
| iam_token | Static IAM token (non-refreshing: served as-is, server responses decide validity) | iam_token; optional iam_token_expires_at |
auth_key (recommended for local / CI)
yc iam service-account create --name kms-encrypter
yc iam key create --service-account-id <sa_id> --output authorized_key.json
yc kms symmetric-key add-access-binding \
--id <key_id> \
--service-account-id <sa_id> \
--role kms.keys.encrypterDecrypternew KmsEncryptionProvider({
keyId: 'aby...',
auth_type: 'auth_key',
authOptions: { authorized_key_path: './authorized_key.json' },
});meta (production on VM)
new KmsEncryptionProvider({
keyId: 'aby...',
auth_type: 'meta',
authOptions: {},
});iam_token (quick test)
yc iam create-tokennew KmsEncryptionProvider({
keyId: 'aby...',
auth_type: 'iam_token',
authOptions: { iam_token: '<token>' },
});The manager does not invent an expiry for a static token: by default it is
returned as-is on every call (no refresh), and a 401 from the server surfaces
as-is. If you know the expiry, pass iam_token_expires_at (Date, ISO string
or unix ms) — after that moment getToken() throws instead of sending a
dead token:
authOptions: {
iam_token: '<token>',
iam_token_expires_at: '2026-09-01T00:00:00Z',
}IamTokenManager
import { IamTokenManager } from '@ycforge/orm-security-providers/yandex-kms';
const manager = new IamTokenManager('auth_key', {
authorized_key_path: './authorized_key.json',
});
const token = await manager.getToken();@ycforge/orm-security-providers/hmac-bi
KmsBlindIndexProvider
new KmsBlindIndexProvider(options: KmsBlindIndexProviderOptions)| Option | Type | Required | Description |
|--------|------|----------|-------------|
| blindIndexKey | string | yes | Canonical padded Base64 key (≥ 32 bytes decoded); invalid alphabet, bad padding or non-canonical trailing bits are rejected |
Method:
hash(plaintext: string, context: YdbEncryptionContext): Promise<string>Returns deterministic base64 HMAC-SHA256 (44 characters). Context is accepted but ignored — the hash depends only on the key and value.
Generate a key:
openssl rand -base64 32Usage with ydb-orm entities
import {
YdbBaseEntity,
YdbEntity,
YdbColumn,
YdbPrimaryColumn,
YdbEncrypted,
YdbSecurityAAD,
} from '@ycforge/ydb-orm';
@YdbEntity('users')
class User extends YdbBaseEntity {
@YdbPrimaryColumn('Uuid')
uuid!: string;
// PK участвует в AAD: шифротекст привязан к значению колонки
@YdbSecurityAAD()
@YdbPrimaryColumn('Utf8')
tenant!: string;
// blind index включён по умолчанию → колонка email_bi (Utf8)
@YdbEncrypted()
email!: string;
// без blind index
@YdbEncrypted({ blindIndex: false })
note!: string;
@YdbColumn('Utf8')
name!: string;
}Шифротекст хранится в колонке Bytes; тип из @YdbColumn для
@YdbEncrypted-полей указывать не нужно.
CRUD через Active Record:
// save() шифрует @YdbEncrypted-поля и считает _bi
await User.save(user);
// find() дешифрует обратно
const found = await User.find({ uuid: user.uuid });
// поиск по зашифрованному полю — через blind index
const byEmail = await User.find({ tenant: user.tenant, email: '[email protected]' });
// зашифрованный updateBy(): перешифровка + обновление _bi
await User.updateBy(
{ uuid: user.uuid, tenant: user.tenant },
{ email: '[email protected]' },
);Больше примеров — в examples/:
nest-module.ts, orm-entity.ts, basic-encryption.ts, blind-index.ts,
aad-binding.ts, auth-modes.ts.
Testing
yarn test # unit + ORM-pipeline tests (моки KMS/YDB)
yarn test:integration # real KMS API (requires .env)
yarn test:cov # coverageORM-pipeline тесты (orm-pipeline.spec.ts) прогоняют реальный интерфейс
@ycforge/ydb-orm (configureEntities, декораторы, репозиторий) поверх
in-memory эмуляции YDB и мока KMS API: encrypt/decrypt, blind index поиск,
AAD-привязка, зашифрованный updateBy(), хранение Uint8Array.
Project Structure
src/
index.ts # root barrel — re-exports all providers
yandex-kms/
index.ts # barrel for KMS + IAM
kms-encryption-provider.ts # encrypt / decrypt via KMS REST API (Uint8Array contract)
iam-token-manager.ts # IAM token management (3 auth modes)
hmac-bi/
index.ts # barrel for HMAC blind index
hmac-blind-index-provider.ts # HMAC-SHA256 blind index
__tests__/
setup.ts # dotenv loader for Jest
helpers.ts # test config from env
kms-encryption-provider.spec.ts
kms-blind-index-provider.spec.ts
iam-token-manager.spec.ts
e2e.spec.ts # mocked KMS roundtrips
orm-pipeline.spec.ts # real ydb-orm interface × providers
integration.spec.ts # real KMS API (env-gated)
examples/
nest-module.ts # YdbCoreModule.forRootAsync() + forFeature
orm-entity.ts # standalone pipeline: save/find/updateBy
basic-encryption.ts
blind-index.ts
aad-binding.ts
auth-modes.tsLicense
MIT © ycforge
