npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

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.js skript 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-client

Talab: 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#7

v6 da apiKeys opsiyasi 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().$eimzo typed
  • nuxt.config.ts.eimzo typed (TypeScript completion)

SSR: Plugin .client mode'da, ya'ni faqat brauzerda ishlaydi (E-IMZO daemon localhost'da). Server tomonida useEimzo() ni chaqirish xavfsiz, lekin install() va boshqa metodlarni onMounted() ichida (yoki client-only block'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 oladi

Imzolash 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, u selected state'idan olinadi (yuqoridagi "Adapter API qoidalari" bo'limiga qarang).

sign() quyidagi turdagi ma'lumotlarni qabul qiladi:

type BinaryInput = string | ArrayBuffer | ArrayBufferView | Blob

1. 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 kerak

Qurilma 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 inline mode'da qoldiring. external faqat eski integrasiyalar uchun.


E-IMZO v6 yangiliklari

Paket avtomatik aniqlaydi va moslashadi:

  • ✅ Avtomatik API-KEY o'rnatish (apikey chaqiruv 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 aud field)
  • Sertifikat zanjiri tekshiruvi — Root CA gacha (E-IMZO PKI'siga ishonch)
  • Sertifikat statusivalidFrom <= 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
  • 🔴 localStorage ni 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

Savol, taklif yoki xavfsizlik muammolari uchun yuqoridagi kanallar orqali bog'laning.


Litsenziya

MIT © Qobiljon Jumaboyev