eimzo-client
v0.1.8
Published
Modern, typed, framework-agnostic E-IMZO (O'zbekiston ERI) client for the web. Vue 3 + React adapters, inline WebSocket transport, mobile detection.
Downloads
1,149
Maintainers
Readme
eimzo-client
Modern, typed, framework-agnostik E-IMZO (O'zbekiston ERI) klienti web ilovalar uchun. Vue 3 va React adapterlari ham ichida.
- ✅ 100% self-contained — tashqi
e-imzo.jsskript yuklash kerak emas (inline WebSocket transport) - ✅ Promise-based API (callback-hell yo'q)
- ✅ TypeScript bilan to'liq typed
- ✅ E-IMZO v5 va v6 mos (v6 da auto API key qo'llab-quvvatlanadi)
- ✅ PFX, FTJC token, ID-card, UZGUARD
- ✅ PKCS#7 imzolash + timestamp ulash + SHA-256 hash signing
- ✅ Vue 3 plugin + composable
- ✅ React Provider + hook
- ✅ ESM + CJS, treeshake-mos
- ✅ 32 MB hujjatgacha imzolash (v6 limit)
- ✅ Xavfsizlik: SRI, prototype pollution himoyasi, AbortSignal cancellation
- ✅ Mobil platforma aniqlash + deeplink builder (E-IMZO mobile app uchun)
O'rnatish
npm install eimzo-client
# yoki
pnpm add eimzo-client
yarn add eimzo-clientTalab: Foydalanuvchi kompyuterida E-IMZO dasturi o'rnatilgan va ishga tushirilgan bo'lishi kerak.
Tezkor boshlash (vanilla)
import { createEimzo } from 'eimzo-client'
const eimzo = createEimzo({
apiKeys: {
'localhost': '96D0C1491615C82B...',
'test.uz': 'C691454C3DBAC565...',
},
})
await eimzo.install() // skript yuklash + version + apikey
const certs = await eimzo.listKeys() // PFX + token sertifikatlari
const result = await eimzo.sign(certs[0], 'Imzolanadigan matn')
console.log(result.pkcs7) // base64 PKCS#7v6 da
apiKeysopsiyasi e'tiborga olinmaydi — E-IMZO o'zi avtomatik o'rnatadi.
Vue 3
// main.ts
import { createApp } from 'vue'
import { EimzoVuePlugin } from 'eimzo-client/vue'
import App from './App.vue'
createApp(App)
.use(EimzoVuePlugin, {
apiKeys: { 'localhost': '96D0C1491615C82B...' },
})
.mount('#app')<!-- KeySelector.vue -->
<script setup lang="ts">
import { onMounted } from 'vue'
import { useEimzo } from 'eimzo-client/vue'
const { certificates, selected, loading, error, loadCertificates, select, sign } = useEimzo()
onMounted(loadCertificates)
async function onSign() {
const result = await sign('Hujjat matni')
console.log(result.pkcs7)
}
</script>
<template>
<div v-if="loading">Yuklanmoqda...</div>
<div v-else-if="error">{{ error.message }}</div>
<ul>
<li v-for="c in certificates" :key="c.serialNumber" @click="select(c)">
{{ c.CN }} ({{ c.TIN || c.PINFL }})
</li>
</ul>
<button :disabled="!selected" @click="onSign">Imzolash</button>
</template>Nuxt 3
Avto-import bilan ishlatish uchun, Nuxt module sifatida ulashing:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['eimzo-client/nuxt'],
eimzo: {
apiKeys: {
'mysite.uz': process.env.NUXT_PUBLIC_EIMZO_KEY ?? '',
},
// 'inline' | 'external' (default: 'inline')
transport: 'inline',
// 'throw' | 'warn' | 'ignore' (default: 'throw')
onMobile: 'throw',
},
runtimeConfig: {
public: {
eimzo: {
// .env'dan o'qish — yuqoridagi konfig bilan birlashtiriladi
},
},
},
}).env:
NUXT_PUBLIC_EIMZO_KEY=31E2228B7A...Component'da useEimzo() avto-import bilan mavjud:
<!-- pages/sign.vue -->
<script setup lang="ts">
const { certificates, selected, loading, error, install, loadCertificates, select, sign } = useEimzo()
onMounted(async () => {
await install()
await loadCertificates()
})
async function onSign() {
if (!selected.value) return
const result = await sign('Imzolanadigan matn')
await $fetch('/api/sign', {
method: 'POST',
body: { pkcs7: result.pkcs7 },
})
}
</script>
<template>
<div v-if="loading">Yuklanmoqda...</div>
<div v-else-if="error">{{ error.message }}</div>
<ul>
<li v-for="c in certificates" :key="c.serialNumber" @click="select(c)">
{{ c.CN }} — {{ c.TIN || c.PINFL }}
</li>
</ul>
<button :disabled="!selected" @click="onSign">Imzolash</button>
</template>Nimasi avtomatik:
- ✅ Plugin (
vueApp.use(EimzoVuePlugin, options)) — client-side only (SSR-safe) - ✅
useEimzo()composable — auto-import, manual import kerak emas - ✅ Type augmentation —
useNuxtApp().$eimzotyped - ✅
nuxt.config.ts.eimzotyped (TypeScript completion)
SSR: Plugin
.clientmode'da, ya'ni faqat brauzerda ishlaydi (E-IMZO daemon localhost'da). Server tomonidauseEimzo()ni chaqirish xavfsiz, lekininstall()va boshqa metodlarnionMounted()ichida (yokiclient-onlyblock'ida) chaqiring.
React
// main.tsx
import { EimzoProvider } from 'eimzo-client/react'
ReactDOM.createRoot(root).render(
<EimzoProvider options={{ apiKeys: { 'localhost': '96D0C1...' } }} autoInstall>
<App />
</EimzoProvider>
)// KeySelector.tsx
import { useEffect } from 'react'
import { useEimzo } from 'eimzo-client/react'
export function KeySelector() {
const { certificates, selected, loading, error, loadCertificates, select, sign } = useEimzo()
useEffect(() => { loadCertificates() }, [loadCertificates])
async function onSign() {
const result = await sign('Hujjat matni')
console.log(result.pkcs7)
}
if (loading) return <div>Yuklanmoqda...</div>
if (error) return <div>{error.message}</div>
return (
<>
<ul>
{certificates.map(c => (
<li key={c.serialNumber} onClick={() => select(c)}>
{c.CN} ({c.TIN || c.PINFL})
</li>
))}
</ul>
<button disabled={!selected} onClick={onSign}>Imzolash</button>
</>
)
}Angular (≥16)
Standalone API + Signal'lar bilan ishlatiladi. provideEimzo() orqali sozlanadi.
// app.config.ts
import { ApplicationConfig } from '@angular/core'
import { provideEimzo } from 'eimzo-client/angular'
export const appConfig: ApplicationConfig = {
providers: [
provideEimzo({
apiKeys: { 'mysite.uz': '...' },
}),
],
}// key-selector.component.ts
import { Component, inject, OnInit } from '@angular/core'
import { CommonModule } from '@angular/common'
import { EimzoService } from 'eimzo-client/angular'
@Component({
standalone: true,
selector: 'app-key-selector',
imports: [CommonModule],
template: `
<div *ngIf="eimzo.loading()">Yuklanmoqda...</div>
<div *ngIf="eimzo.error() as err">{{ err.message }}</div>
<ul>
<li
*ngFor="let c of eimzo.certificates()"
(click)="eimzo.select(c)"
>
{{ c.CN }} ({{ c.TIN || c.PINFL }})
</li>
</ul>
<button [disabled]="!eimzo.selected()" (click)="onSign()">
Imzolash
</button>
`,
})
export class KeySelectorComponent implements OnInit {
readonly eimzo = inject(EimzoService)
async ngOnInit() {
await this.eimzo.install()
await this.eimzo.loadCertificates()
}
async onSign() {
const result = await this.eimzo.sign('Hujjat matni')
console.log(result.pkcs7)
}
}EimzoService ichidagi reaktiv field'lar Angular Signal'lar:
| Signal | Tip |
|---|---|
| certificates() | Certificate[] |
| selected() | Certificate \| null |
| loading() | boolean |
| error() | Error \| null |
| version() | VersionInfo \| undefined |
Tayyor EimzoClient instansiyasini ham berish mumkin (test yoki shared client uchun):
import { provideEimzoClient, EimzoService } from 'eimzo-client/angular'
import { createEimzo } from 'eimzo-client'
const client = createEimzo({ apiKeys: { ... } })
providers: [provideEimzoClient(client)]Angular ≥16 talab qilinadi (Signal API sababli). Eski versiyalar uchun EimzoClient'ni to'g'ridan-to'g'ri ishlating.
Adapter API qoidalari (Vue / React / Angular)
Adapter'larda (useEimzo() composable/hook va EimzoService) sertifikat ichki state orqali boshqariladi. Imzolash metodlarini chaqirishdan oldin selected ga sertifikat qo'yilishi shart, aks holda Error("Sertifikat tanlanmagan") qaytariladi.
Sertifikatni tanlash usullari
Vue — uchta ekvivalent yo'l:
const { selected, select } = useEimzo()
select(cert) // 1. select() metodi (React/Angular bilan symmetri)
selected.value = cert // 2. To'g'ridan-to'g'ri ref'ga yozish<!-- 3. v-model orqali (template'da) -->
<select v-model="selected">
<option v-for="c in certificates" :value="c">{{ c.CN }}</option>
</select>React — faqat select():
const { select } = useEimzo()
select(cert)Angular — faqat select() (signal readonly):
this.eimzo.select(cert)selected talab qiladigan / qilmaydigan metodlar
| Metod | selected kerakmi? | Sabab |
|---|:-:|---|
| install() | ❌ | Skript yuklash, sertifikat'siz |
| loadCertificates() | ❌ | Ro'yxat olish |
| select(cert) | ❌ | State'ga yozish |
| sign(data, opts?) | ✅ | selected dan cert oladi |
| signHash(file, opts?) | ✅ | selected dan cert oladi |
| authenticate(challenge) | ✅ | selected dan cert oladi |
| signWithSerial(serial, data) | ❌ | Cert serial bo'yicha topiladi |
💡 Agar siz cert'ni state'da saqlamasdan ishlatmoqchi bo'lsangiz — core API (
createEimzo()) ishlating, u har bir metodga cert'ni argument sifatida qabul qiladi.
Misol — to'g'ri tartib
const { install, loadCertificates, select, authenticate } = useEimzo()
await install() // 1. Skript + version
const certs = await loadCertificates() // 2. Ro'yxat
select(certs[0]) // 3. ⚠️ MAJBURIY — bu qadam o'tkazib yuborilsa, keyingi authenticate xato beradi
const pkcs7 = await authenticate(challenge) // 4. selected'dan cert oladiImzolash holatlari (signing flows)
Quyidagi misollar core API (
createEimzo()) uchun — har bir metodga cert argument sifatida beriladi. Adapter'larda (useEimzo()/EimzoService) cert'ni argument sifatida bermaysiz, uselectedstate'idan olinadi (yuqoridagi "Adapter API qoidalari" bo'limiga qarang).
sign() quyidagi turdagi ma'lumotlarni qabul qiladi:
type BinaryInput = string | ArrayBuffer | ArrayBufferView | Blob1. Auth challenge (matn)
const challenge = await fetch('/api/auth/challenge').then(r => r.text())
const pkcs7 = await eimzo.authenticate(challenge, selectedCert)2. JSON hujjat
const doc = { orderId: 12345, amount: 1_000_000, note: "to'lov" }
const result = await eimzo.sign(cert, JSON.stringify(doc))3. PDF/DOC fayl (Blob/File)
const file = inputElement.files[0] // <input type="file">
const result = await eimzo.sign(cert, file)
console.log(result.pkcs7)4. Katta fayl (SHA-256 hash imzolash — tavsiya etiladi)
Katta hujjatlar (10+ MB) uchun fayl bayt-baytlarini emas, hashni imzolash tezroq va xavfsizroq:
const file = inputElement.files[0]
const { pkcs7, hash } = await eimzo.signHash(cert, file)
// ↑ avtomatik SHA-256 + detached PKCS#7
// Backend'ga yuborganda hashni ham yuborasiz:
await fetch('/api/sign', {
method: 'POST',
body: JSON.stringify({
pkcs7,
hashHex: bytesToHex(hash),
}),
})5. Binary baytlar (ArrayBuffer / Uint8Array)
const bytes = new Uint8Array([0x01, 0x02, 0x03])
const result = await eimzo.sign(cert, bytes)6. Allaqachon base64 satr
const result = await eimzo.sign(cert, alreadyBase64, { isBase64: true })7. Detached signature (asl hujjatsiz imzo)
const result = await eimzo.sign(cert, file, { detached: true })
// PKCS#7 ichida fayl yo'q — alohida saqlash kerakQurilma orqali kirish (ID-card / BAIK / CKC)
Bu uchta qurilma sertifikat ro'yxatiga kirmaydi — alohida tugma orqali ishlatiladi. PIN'ni E-IMZO daemon o'zi so'raydi.
if (await eimzo.isBaikConnected()) {
const challenge = await fetch('/api/challenge').then(r => r.text())
const pkcs7 = await eimzo.authenticateWithBaik(challenge)
await fetch('/api/login', { method: 'POST', body: pkcs7 })
}| Metod | Tavsif |
|---|---|
| isIdCardConnected() / isBaikConnected() / isCkcConnected() | Qurilma ulanganmi? |
| signWithIdCard(data, opts?) / signWithBaik(...) / signWithCkc(...) | Imzolash |
| authenticateWithIdCard(challenge) / authenticateWithBaik(...) / authenticateWithCkc(...) | Auth challenge imzolash |
Talab: BAIK/CKC uchun E-IMZO 4.86+, ID-card uchun 4.12+.
Timestamp (TST) qo'shish
const result = await eimzo.sign(cert, data, {
async timestamp(signatureHex) {
const tst = await fetch('/api/timestamp', {
method: 'POST',
body: signatureHex,
}).then(r => r.text())
return tst
},
})API ma'lumotnomasi
createEimzo(options): EimzoClient
| Opsiya | Tip | Default | Tavsif |
|---|---|---|---|
| apiKeys | Record<string, string> | — | domain → key xaritasi (faqat v5) |
| scriptUrl | string | https://e-imzo.uz/e-imzo-cm/e-imzo.js | CAPIWS skript URL |
| skipScriptLoad | boolean | false | Skript allaqachon mavjud bo'lsa |
| timeoutMs | number | 30000 | CAPIWS chaqiruv timeout |
| maxDataBytes | number | 32 MB | Imzolanadigan ma'lumot maks hajmi |
EimzoClient metodlari
| Metod | Tavsif |
|---|---|
| install() | Skript yuklash + version + apikey o'rnatish |
| listKeys() | Mavjud sertifikatlar (PFX + token) |
| loadKey(cert, { verify }) | Kalitni yuklash, ixtiyoriy parol/PIN tekshiruvi |
| sign(cert, data, opts?) | PKCS#7 imzolash |
| signWithSerial(serial, data, opts?) | Serial bo'yicha topib imzolash |
| authenticate(challenge, cert) | Auth challenge ni imzolash |
| getCertificateChain(handle) | x509 zanjir |
| isIdCardConnected() / isBaikConnected() / isCkcConnected() | Qurilma ulanganmi |
| signWithBaik(data, opts?) / signWithCkc(...) / signWithIdCard(...) | Qurilma orqali imzolash |
| authenticateWithBaik(challenge) / authenticateWithCkc(...) / authenticateWithIdCard(...) | Auth challenge imzolash |
| version | Versiya ma'lumoti (install() dan keyin) |
Xato turlari
Hammasi EimzoError dan meros oladi va code xususiyatiga ega.
| Xato | Qachon |
|---|---|
| EimzoNotInstalledError | E-IMZO dasturi ishlamayapti |
| EimzoTransportError | WebSocket / tarmoq xatosi |
| EimzoVersionError | E-IMZO versiyasi mos kelmadi |
| EimzoApiKeyError | API key noto'g'ri yoki yo'q |
| EimzoTimeoutError | Chaqiruv kutish vaqti tugadi |
| EimzoWrongPasswordError | Noto'g'ri PFX parol / token PIN |
| EimzoSignError | Imzolashda xatolik |
| EimzoKeyNotFoundError | Sertifikat topilmadi |
| EimzoDataTooLargeError | Ma'lumot maxDataBytes dan oshdi |
| EimzoResponseTooLargeError | Daemon javobi juda katta |
| EimzoMobileUnsupportedError | Mobil brauzerda install() chaqirilgan |
| EimzoDeeplinkHostNotAllowedError | callbackUrl host whitelist'da yo'q |
import { EimzoError, EimzoNotInstalledError, EimzoWrongPasswordError } from 'eimzo-client'
try {
await eimzo.signWithBaik(data)
} catch (e) {
if (e instanceof EimzoNotInstalledError) {
alert('E-IMZO dasturini ishga tushiring')
} else if (e instanceof EimzoWrongPasswordError) {
alert("Noto'g'ri PIN")
} else if (e instanceof EimzoError) {
console.error(e.code, e.message)
}
}Sertifikat yordamchilari
Har bir Certificate obyekti listKeys() chaqirilganda avtomatik rol flag'lari bilan boyitiladi:
const certs = await eimzo.listKeys()
const cert = certs[0]
cert.isLegalEntity // boolean — yuridik shaxsmi?
cert.isPhysicalPerson // boolean — jismoniy shaxs (YATT ham bunga kiradi)
cert.isYATT // boolean — yakka tartibdagi tadbirkor?Free-funksiyalar — Certificate ga muqobil tarzda Pick'lar bilan ham ishlaydi:
import {
isLegalEntity,
isPhysicalPerson,
isYATT,
getCertificateRole,
getCertificateRoleLabel,
getCertificateStatus,
} from 'eimzo-client'
isLegalEntity({ TIN, UID }) // INN bor → true
isPhysicalPerson({ TIN, UID }) // INN yo'q → true
isYATT({ TIN, UID, O }) // INN yo'q VA O bor → true
getCertificateRole(cert) // 'legal' | 'yatt' | 'physical'
getCertificateRoleLabel(cert) // "Yuridik shaxs" | "Yakka tartibdagi tadbirkor" | "Jismoniy shaxs"
getCertificateRoleLabel(cert, 'en') // English variant
getCertificateStatus(cert) // { status: 'active' | 'expiring' | 'expired' | 'pending', daysLeft }UI'da tipni ko'rsatish (Vue/Nuxt misol):
<template>
<div v-for="c in certificates" :key="c.serialNumber" class="cert-card">
<span class="badge" :class="`role-${getCertificateRole(c)}`">
{{ getCertificateRoleLabel(c) }}
</span>
<h4>{{ c.CN }}</h4>
<p v-if="c.isLegalEntity">TIN: {{ c.TIN }}</p>
<p v-else>PINFL: {{ c.PINFL }}</p>
</div>
</template>Encoding yordamchilari
eimzo-client ichida UTF-8, Base64, hex, SHA-256 utility funksiyalari mavjud:
import {
encodeBase64, encodeBase64Async, encodeBase64Url,
decodeBase64, decodeBase64Text,
bytesToHex, hexToBytes,
sha256, sha256Hex, sha256Base64,
toUint8Array, byteLength,
} from 'eimzo-client'
encodeBase64('Salom dunyo') // UTF-8 → base64
encodeBase64(new Uint8Array([1, 2, 3])) // bayt → base64
await encodeBase64Async(file) // Blob/File → base64 (async)
decodeBase64('SGVsbG8=') // base64 → Uint8Array
decodeBase64Text('SGVsbG8=') // base64 → string
await sha256Hex(file) // SHA-256 hex string
await sha256Base64('text') // SHA-256 base64
bytesToHex(new Uint8Array([0xab, 0xcd])) // 'abcd'
hexToBytes('abcd') // Uint8Array([0xab, 0xcd])Native fast path: Uint8Array.prototype.toBase64() mavjud bo'lgan brauzerlarda (Chrome 130+, Safari 18+) avtomatik undan foydalanadi. Eski brauzerlarda chunked btoa (32K bo'lakli) — 32 MB gacha stack overflow yo'q.
Mobil brauzerda ishlatish
E-IMZO desktop dasturi mobil telefonda mavjud emas — uning o'rniga E-IMZO mobile app ishlatiladi va u butunlay boshqa flow orqali ishlaydi (deeplink + backend callback).
Platformani aniqlash
import { isMobilePlatform, getPlatform } from 'eimzo-client'
if (isMobilePlatform()) {
// E-IMZO mobile flow
} else {
// Desktop: createEimzo(...).install()
}
getPlatform() // 'ios' | 'android' | 'desktop' | 'unknown'Mobile flow (deeplink + polling)
import { buildEimzoMobileDeeplink, pollMobileResult } from 'eimzo-client'
// 1. Backend session yaratadi
const { sessionId } = await fetch('/api/eimzo/start', { method: 'POST' }).then(r => r.json())
// 2. Deeplink yarataylik va ochaylik
const deeplink = buildEimzoMobileDeeplink({
sessionId,
callbackUrl: 'https://api.mysite.uz/eimzo/callback',
baseUrl: 'https://e-imzo.uz/mobile-app/sign', // yoki sizning custom URL
params: { mode: 'auth' },
})
window.location.href = deeplink
// 3. Foydalanuvchi qaytib kelganda — natijani polling bilan kutamiz
const result = await pollMobileResult(
async () => {
const r = await fetch(`/api/eimzo/status/${sessionId}`).then(r => r.json())
return r.completed
? { ready: true, data: r.pkcs7 }
: { ready: false }
},
{ intervalMs: 2000, timeoutMs: 5 * 60 * 1000 },
)Default — install() mobilda exception tashlaydi
const eimzo = createEimzo({
// 'throw' (default) | 'warn' | 'ignore'
onMobile: 'throw',
})
if (isMobilePlatform()) {
// Mobile flow'ga o'ting
} else {
await eimzo.install()
}Transport rejimi
Default — inline mode: paket o'zining WebSocket transportini ishlatadi, hech qanday tashqi skript yuklamaydi.
const eimzo = createEimzo({
apiKeys: { 'mysite.uz': '...' },
transport: 'inline', // DEFAULT
})Xohlasangiz eski window.CAPIWS skriptidan foydalanish mumkin (legacy compat):
const eimzo = createEimzo({
transport: 'external',
scriptUrl: 'https://e-imzo.uz/e-imzo-cm/e-imzo.js',
scriptIntegrity: 'sha384-...', // SRI tavsiya etiladi
})| Xususiyat | inline (default) | external |
|---|---|---|
| Tashqi skript yuklash | ❌ Yo'q | ✅ e-imzo.uz dan ≈50KB |
| Supply-chain xavfi | ✅ Yo'q | ⚠️ CDN compromise xavfi (SRI bilan kamayadi) |
| CSP script-src | Faqat 'self' | 'self' https://e-imzo.uz |
| CSP connect-src | wss://127.0.0.1:* ws://127.0.0.1:* | Bir xil |
| WebSocket cancellation | ✅ AbortSignal bilan | ❌ Yo'q |
| Birinchi imzo tezligi | ⚡ Tezroq | Skript yuklash kutiladi |
| Offline (intranet) | ✅ Ishlaydi | ❌ e-imzo.uz kerak |
Tavsiya: production'da
inlinemode'da qoldiring.externalfaqat eski integrasiyalar uchun.
E-IMZO v6 yangiliklari
Paket avtomatik aniqlaydi va moslashadi:
- ✅ Avtomatik API-KEY o'rnatish (
apikeychaqiruv kerak emas) - ✅ 32 MB gacha hujjat imzolash
- ✅ ID-card emulyatori (test rejim)
- ✅ UZGUARD token qo'llab-quvvatlash
- ✅ JRE 17 → JRE 1.8 PFX moslik
Xavfsizlik
E-IMZO bilan ishlashda frontend imzo hosil qiladi, lekin uni tekshirmaydi. Backend tomonida quyidagilar majburiy:
Auth (login) flow uchun checklist
- ✅ Challenge tasodifiyligi — kamida 32 bayt CSPRNG (
crypto.randomBytes(32)) - ✅ TTL ≤ 5 daqiqa — challenge cache'da muddati cheklangan bo'lsin (Redis TTL)
- ✅ Bir martalik foydalanish — verify dan keyin challenge'ni darhol o'chiring
- ✅ Origin/audience binding — challenge ichida saytingizning host'i bo'lsin (yoki
audfield) - ✅ Sertifikat zanjiri tekshiruvi — Root CA gacha (E-IMZO PKI'siga ishonch)
- ✅ Sertifikat statusi —
validFrom <= now <= validTo, mumkin bo'lsa CRL/OCSP - ✅ PKCS#7 imzosi GOST/RSA bilan tekshirilgan — challenge baytlariga mos kelishi
Token saqlash
localStorage JWT uchun zaif — XSS hujumda butun token o'g'irlanadi. Ishlatish:
- 🟢 HttpOnly + Secure + SameSite=Strict cookie — XSS o'qiy olmaydi
- 🟡 Sessionga qisqa TTL (15-30 min) + refresh token rotation
- 🔴
localStorageni faqat development'da yoki tokendan boshqa narsa uchun
Mobile flow xavfsizligi
buildEimzoMobileDeeplink() da production'da allowedHosts doim bering:
buildEimzoMobileDeeplink({
sessionId,
callbackUrl,
allowedHosts: ['api.mysite.uz'], // ← MAJBURIY production'da
})Aks holda hujumchi sizning sahifangizdan deeplink yasab, foydalanuvchi PKCS#7'sini o'z serveriga yo'naltirishi mumkin (callbackUrl hijacking).
data parametri deeplink ichida plain text — uning yaxlitligi backend tomonidan session ichidagi hash bilan tekshirilishi shart (parametr tampering himoyasi).
Localhost daemon TLS
E-IMZO daemon wss://127.0.0.1:64443 da self-signed sertifikat ishlatadi. Brauzer faqat foydalanuvchi qabul qilgan-qabul qilmaganini tekshiradi, certificate pinning yo'q. Bu E-IMZO arxitekturasining cheklovi:
- Lokal mashinada zararli proses 64443 portga "o'tirib" foydalanuvchi imzolarini tutib qolishi mumkin
- Bu paket buni oldini olmaydi — tahdid model OS-level malware'dir
Mitigatsiyalar: ishonchli mashinalarda ishlash, antivirus, host-based IDS.
CSP tavsiyalari
script-src 'self'; # inline mode
connect-src 'self' wss://127.0.0.1:* ws://127.0.0.1:*;External mode'da script-src ga https://e-imzo.uz qo'shing va scriptIntegrity (SRI) bilan hash'ni qattiq belgilang.
Bog'lanish
Muallif: Qobiljon Jumaboyev
- LinkedIn: qobiljon-jumaboyev
- GitHub: @qobiljonDev
Savol, taklif yoki xavfsizlik muammolari uchun yuqoridagi kanallar orqali bog'laning.
Litsenziya
MIT © Qobiljon Jumaboyev
