Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
keeper — حافظ بسيط وآمن للأسرار | Kitploit
أدوات/GitHubGitHub/agberohq/keeper
المصادقة والترخيصأدوات التشفير/فك التشفيرالتشفيركشف الأسرار
GitHubagberohq/keeper

keeper

حافظ بسيط وآمن للأسرار

عرض المستودع
1204منذ 4 أشهرتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

keeper

Keeper هو مخزن تشفير سري للغة Go. يقوم بتشفير أي حمولات بايت تعسفية عند التخزين باستخدام اشتقاق المفتاح Argon2id والتشفير الموثّق XChaCha20-Poly1305 (الافتراضي)، ويخزّنها في قاعدة بيانات bbolt مضمنة.

يأتي على ثلاثة أشكال يمكن استخدامها بشكل مستقل:

  • مكتبة Go — قم بتضمين مخزن سري محسّن مباشرةً في عمليتك، مع أربعة مستويات أمان، وعزل DEK لكل مجموعة، وسلسلة تدقيق مقاومة للعبث.
  • مُعالج HTTP (x/keephandler) — قم بتركيب نقاط نهاية Keeper على أي موزع net/http في استدعاء واحد، مع خطافات قابلة للتوصيل، وحراس، ومشفِّرات استجابة للتحكم في الوصول وتسجيل التدقيق.
  • واجهة CLI (cmd/keeper) — واجهة طرفية مع جلسة REPL دائمة، إدخال سري بدون صدى، وعدم تعريض سجل الصدفة.

تم تصميم Keeper كطبقة إدارة أسرار أساسية لموازن التحميل Agbero لكنه لا يعتمد على Agbero ويعمل في أي مشروع Go.


المحتويات

  • نموذج الأمان
  • التصميم التشفيري
  • تسلسل المفاتيح
  • مخطط التخزين
  • سلسلة التدقيق
  • تكامل Jack
  • x/keepcmd — عمليات CLI قابلة لإعادة الاستخدام
  • x/keephandler — معالج HTTP
  • مرجع API
  • كتالوج الأخطاء
  • قرارات الأمان
  • التبعيات

نموذج الأمان

يقوم Keeper بتقسيم الأسرار إلى مجموعات. لكل مجموعة سياسة أمان مجموعة (BucketSecurityPolicy) غير قابلة للتغيير تحكم كيفية حماية مفتاح تشفير البيانات (DEK) الخاص بها. تتوفر أربعة مستويات.

المخططات مقابل مستويات الأمان

المخطط هو بادئة URI تجمع المجموعات ذات الصلة (vault://، certs://، space://، أو أي اسم تسجّله). مستوى الأمان هو خاصية لسياسة المجموعة تُعيّن عند الإنشاء وتظل غير قابلة للتغيير بعد ذلك.

يمكنك مزج مستويات الأمان بحرية داخل نفس المخطط. على سبيل المثال، vault://system قد يكون LevelPasswordOnly (يفتح تلقائيًا عند بدء التشغيل)، بينما vault://admin هو LevelAdminWrapped (يتطلب بيانات اعتماد صريحة).

LevelPasswordOnly

يتم اشتقاق DEK للمجموعة من المفتاح الرئيسي باستخدام HKDF-SHA256 مع سلسلة معلومات مفصولة بالمجال لكل مجموعة (keeper-bucket-dek-v1:scheme:namespace). جميع مجموعات LevelPasswordOnly تُفتح تلقائيًا عند استدعاء UnlockDatabase بكلمة المرور الرئيسية الصحيحة. لا حاجة لبيانات اعتماد لكل مجموعة في وقت التشغيل. هذا المستوى مناسب للأسرار التي تحتاجها العملية عند بدء التشغيل دون تدخل بشري.

LevelAdminWrapped

للمجموعة DEK مولد عشوائيًا بحجم 32 بايت وفريد لتلك المجموعة. لا يُخزَّن DEK أبدًا بنص عادي. لكل مسؤول مصرح له، يتم اشتقاق مفتاح تشفير المفاتيح (KEK) من HKDF(masterKey‖adminCred, dekSalt) ويستخدم لتغليف DEK عبر XChaCha20-Poly1305. لا يمكن الوصول إلى المجموعة حتى يقوم مسؤول باستدعاء UnlockBucket ببيانات اعتماده. لا يمكن لكلمة المرور الرئيسية وحدها فك تشفير المجموعة. إلغاء صلاحية مسؤول واحد لا يؤثر على النسخة المغلفة لأي مسؤول آخر.

LevelHSM

يتم إنشاء DEK للمجموعة في وقت CreateBucket ويُغلّف فورًا بواسطة HSMProvider يوفره المتصل. يقوم المزوّد بعمليات التغليف وفك التغليف — لا يتعامل Keeper مع DEK الخام بعد تسليمه للمزوّد. يستدعي UnlockDatabase تلقائيًا المزوّد لفك التغليف وتغذية الغلاف (Envelope) لجميع مجموعات HSM المسجلة. تدوير المفتاح الرئيسي لا يُعيد تشفير هذه المجموعات؛ DEK يتحكم فيه المزوّد.

تتوفر في pkg/hsm تطبيق SoftHSM مدمج يعتمد على مفتاح تغليف محمي بـ memguard للاختبار وبيئات CI. لا تستخدمه في الإنتاج.

LevelRemote

مطابق لـ LevelHSM في سلوك إدارة المفاتيح، لكن HSMProvider يتم تنفيذه بواسطة pkg/remote.Provider — وهو محول HTTPS قابل للتكوين يفوض التغليف وفك التغليف إلى أي خدمة KMS عن بُعد عبر TLS. توجد تكوينات مسبقة لـ HashiCorp Vault Transit و AWS KMS و GCP Cloud KMS في pkg/remote. للاستخدام في الإنتاج، قم بتكوين TLSClientCert و TLSClientKey لتفعيل مصادقة TLS المتبادلة.


التصميم التشفيري

اشتقاق المفتاح الرئيسي```

salt ← random 32 bytes, generated once, stored as a versioned SaltStore (unencrypted) masterKey ← Argon2id(passphrase, salt, t=3, m=64 MiB, p=4) → 32 bytes

root@kitploit:~
يتم تخزين تجزئة التحقق عند الاشتقاق الأول:```
verifyHash ← Argon2id(masterKey, "verification", t=1, m=64 MiB, p=4) → 32 bytes

بعد ذلك، تقوم استدعاءات DeriveMaster بإعادة حساب هذا التجزئة ومقارنته مع crypto/subtle.ConstantTimeCompare. يؤدي عدم التطابق إلى إرجاع ErrInvalidPassphrase.

يتم تخزين ملح KDF غير مشفر حسب التصميم. يجب أن يكون قابلاً للقراءة قبل UnlockDatabase لاشتقاق المفتاح الرئيسي — تشفيره بمفتاح مشتق من المفتاح الرئيسي سيكون دائريًا. ملح KDF ليس سرًا؛ الغرض منه هو التفرد، وليس السرية.

تشفير البيانات السرية

يتم تشفير كل قيمة نصية واضحة باستخدام XChaCha20-Poly1305 باستخدام مفتاح تشفير البيانات للمجموعة (bucket DEK):``` nonce ← random 24 bytes ciphertext ← XChaCha20-Poly1305.Seal(nonce, DEK, plaintext)

root@kitploit:~
السجل المخزّن هو بنية `Secret` مشفّرة بـ msgpack تحتوي على النص المشفر، والبيانات الوصفية المشفّرة، وإصدار المخطط. المصادقة ضمنية: أي نص مشفر يتم فك تشفيره بمفتاح خاطئ ينتج عنه فشل في مصادقة AEAD قبل إرجاع أي نص واضح.

### KEK derivation — LevelAdminWrapped```
salt ← random 32 bytes, generated at bucket creation, stored in policy
ikm ← masterKey ‖ adminCredential
KEK ← HKDF-SHA256(ikm, salt, info="keeper-kek-v1") → 32 bytes
wrappedDEK ← XChaCha20-Poly1305.Seal(nonce, KEK, DEK)

يتم اشتقاق KEK باستخدام HKDF بدلاً من تمريرة Argon2 ثانية. المفتاح الرئيسي تم إنتاجه بالفعل بواسطة KDF عالي التكلفة؛ استدعاء ثانٍ لـ Argon2 من شأنه أن يضيف مئات المللي ثانية من زمن الانتظار لكل استدعاء UnlockBucket دون أي فائدة أمنية. تعمل HKDF-SHA256 في حوالي ميكروثانية واحدة.

دفاع متعمق: المهاجم الذي يخترق قاعدة البيانات فقط يحصل على DEK المغلف وملح HKDF لكنه لا يستطيع اشتقاق KEK بدون المفتاح الرئيسي. المهاجم الذي يخترق المفتاح الرئيسي فقط لا يستطيع فك تغليف أي DEK من نوع LevelAdminWrapped دون معرفة بيانات اعتماد المشرف أيضًا.

تشفير البيانات الوصفية — الأسرار

يتم تشفير البيانات الوصفية السرية (وقت الإنشاء، وقت التحديث، عدد مرات الوصول، الإصدار) بشكل منفصل عن النص المشفر:``` metaKey ← HKDF-SHA256(bucketDEK, nil, info="keeper-metadata-v1") → 32 bytes encryptedMeta ← XChaCha20-Poly1305.Seal(nonce, metaKey, msgpack(metadata))

root@kitploit:~
بالنسبة للحاويات `LevelAdminWrapped` و `LevelHSM` و `LevelRemote`، هذا يعني أن البيانات الوصفية غير قابلة للوصول بدون بيانات اعتماد الحاوية، مما يمنع المهاجم الذي لديه حق الوصول للقراءة إلى ملف قاعدة البيانات من معرفة أنماط الوصول أو الطوابع الزمنية.

**ملاحظة حول القنوات الجانبية الزمنية:** يقوم XChaCha20-Poly1305 بمعالجة النص المشفر بالكامل قبل إرجاع خطأ في المصادقة. مسار فك التشفير الاحتياطي (مفتاح تشفير البيانات المستمد الجديد → مفتاح رئيسي قديم كمفتاح تشفير بيانات) يستغرق نفس الوقت الفعلي بغض النظر عن أي مفتاح ينجح. لا يوجد قناة جانبية زمنية تسرب حالة ترحيل السجل.

### تشفير البيانات الوصفية — السياسات، سجل الكتابة المسبقة (WAL)، والتدقيق

جميع البيانات الوصفية الهيكلية مشفرة أيضًا أثناء التخزين. يتم اشتقاق مفتاحين من المفتاح الرئيسي عند وقت `UnlockDatabase`:```
policyEncKey ← HKDF-SHA256(masterKey, nil, info="keeper-policy-enc-v1") → 32 bytes
auditEncKey  ← HKDF-SHA256(masterKey, nil, info="keeper-audit-enc-v1")  → 32 bytes

policyEncKey يُشفر: قيم BucketSecurityPolicy وWAL التدوير.

auditEncKey يُشفر: حقول Scheme وNamespace وDetails لكل حدث تدقيق.

يتم مسح كلا المفتاحين من الذاكرة عند Lock(). التشفير المستخدم لتشفير البيانات الوصفية هو نفس واجهة crypt.Cipher القابلة للتكوين المستخدمة للأسرار — اختيار المستخدم للتشفير (AES-256-GCM لـ FIPS، XChaCha20-Poly1305 افتراضيًا) يتم تطبيقه تلقائيًا.

تنسيق الإرسال لجميع كتل البيانات الوصفية المشفرة:``` nonce (cipher.NonceSize() bytes) || AEAD-ciphertext

root@kitploit:~
### تجزئة مفتاح دلو السياسة

مفاتيح السياسة على القرص هي تجزئات غير شفافة بدلاً من سلاسل `scheme:namespace` بنص عادي، مما يمنع تعداد أسماء الدلائل دون اتصال:```
base ← hex(SHA-256("scheme:namespace"))[:32]   // 32 hex chars = 128-bit key space
_policies/<base>          → encrypted BucketSecurityPolicy
_policies/<base>__hash__  → SHA-256(encrypted policy bytes)
_policies/<base>__hmac__  → HMAC-SHA256(policyKey, encrypted policy bytes)

لا يزال schemeRegistry في الذاكرة يستخدم "scheme:namespace" كمفتاح له — فقط تمثيل التخزين على القرص هو الذي يتغير.

مصادقة السياسة

يحمل كل سجل سياسة علامتي تكامل تُكتبان بشكل ذري في معاملة bbolt واحدة:``` hash ← SHA-256(encryptedPolicyBytes) — unauthenticated, pre-unlock integrity policyKey ← HKDF-SHA256(masterKey, nil, info="keeper-policy-hmac-v1") → 32 bytes hmac ← HMAC-SHA256(policyKey, encryptedPolicyBytes) — authenticated, post-unlock integrity

root@kitploit:~
قبل `UnlockDatabase`، يكون هاش SHA-256 فقط متاحًا. بعد فتح القفل، يتحقق `loadPolicy` من علامة HMAC. يستدعي `UnlockDatabase` دالة `upgradePolicyHMACs` لملء علامات HMAC للسياسات التي تم إنشاؤها قبل وجود هذه الميزة.

### تدقيق HMAC signing```
auditKey ← HKDF-SHA256(masterKey, nil, info="keeper-audit-hmac-v1") → 32 bytes
HMAC ← HMAC-SHA256(auditKey, event fields including Seq)

يتم تنشيط مفتاح التوقيع في UnlockDatabase ويتم مسحه في Lock. وعندما يتم تدوير المفتاح الرئيسي، يقوم Rotate بإلحاق حدث تدقيق بنقطة تفتيش تدوير المفتاح بكل سلسلة تدقيق نشطة، موقعة بالمفتاح القديم للتدقيق كحدث أخير للحقبة القديمة. لا تتم إعادة كتابة التاريخ أبدًا؛ نقطة التفتيش هي جسر الثقة بين الحقب.


التسلسل الهرمي للمفاتيح```

passphrase │ └─ Argon2id(salt) ──→ masterKey (32 bytes, memguard Enclave) │ ├─ HKDF("keeper-audit-hmac-v1") ──→ auditKey (HMAC signing) ├─ HKDF("keeper-audit-enc-v1") ──→ auditEncKey (audit field encryption) ├─ HKDF("keeper-policy-hmac-v1") ──→ policyKey (policy HMAC) ├─ HKDF("keeper-policy-enc-v1") ──→ policyEncKey (policy/WAL encryption) │ ├─ [LevelPasswordOnly] │ └─ HKDF("keeper-bucket-dek-v1:scheme:ns") ──→ DEK │ └─ HKDF("keeper-metadata-v1") ──→ metaKey │ ├─ [LevelAdminWrapped] │ ├─ random 32 bytes ──→ DEK │ │ └─ HKDF("keeper-metadata-v1") ──→ metaKey │ │ │ └─ HKDF("keeper-kek-v1", masterKey‖adminCred, dekSalt) │ └─ KEK │ └─ XChaCha20-Poly1305(KEK, DEK) ──→ wrappedDEK │ └─ [LevelHSM / LevelRemote] ├─ random 32 bytes ──→ DEK │ └─ HKDF("keeper-metadata-v1") ──→ metaKey │ └─ HSMProvider.WrapDEK(DEK) ──→ wrappedDEK (stored; provider controls the wrapping key)

root@kitploit:~
تُصفّر جميع المفاتيح الوسيطة فور استخدامها. ولا يُكتب المفتاح الرئيسي على القرص بأي شكل.

---

## مخطط التخزين

قاعدة البيانات الأساسية هي bbolt. جميع الحاويات (buckets) ومحتوياتها:

| حاوية bbolt | المفتاح (Key) | القيمة (Value) |
|---|---|---|
| `__meta__` | `salt` | msgpack — SaltStore (غير مشفرة؛ اعتماد دائري إذا كانت مشفرة) |
| `__meta__` | `verify` | بايتات خام — تجزئة التحقق Argon2id |
| `__meta__` | `rotation_wal` | `nonce‖AEAD(msgpack(RotationWAL))` |
| `__meta__` | `bucket_dek_done` | `"1"` — علامة إتمام ترحيل DEK |
| `__policies__` | `hex(SHA-256(scheme:ns))[:32]` | `nonce‖AEAD(msgpack(BucketSecurityPolicy))` |
| `__policies__` | `<base>__hash__` | سداسي عشري SHA-256 لبايتات السياسة المشفرة |
| `__policies__` | `<base>__hmac__` | سداسي عشري HMAC-SHA256(policyKey, بايتات السياسة المشفرة) |
| `__audit__/scheme/namespace` | UUID الحدث | JSON — حدث المراجعة (Event) |
| `__audit__/scheme/namespace` | `__chain_index__` | JSON — chainIndex |
| `scheme/namespace` | سلسلة المفتاح | msgpack — بنية Secret |

### بنية Secret (msgpack)```go
type Secret struct {
    Ciphertext    []byte `msgpack:"ct"`
    EncryptedMeta []byte `msgpack:"em,omitempty"`
    SchemaVersion int    `msgpack:"sv"`  // always 1
}

حقول حدث التدقيق

يستخدم هيكل Event حقول توجيه بنص عادي منفصلة (Scheme، Namespace) بجانب حقول الحمولة المشفرة (EncScheme، EncNamespace، EncDetails). يتم حساب المجاميع الاختبارية (Checksums) على حقول التوجيه بنص عادي وبايتات EncDetails المشفرة، بحيث يمكن التحقق من سلامة السلسلة على ثلاثة مستويات بدون أي مفتاح:

المستوىيملكيمكنه التحقق
عاملا شيءسلسلة المجموع الاختباري SHA-256 (يكشف العبث والإدراج)
حامل مفتاح التدقيقauditEncKeyالسلسلة الكاملة + فك تشفير Scheme/Namespace/Details

مثال: يتلقى مدقق الامتثال (compliance auditor) فقط auditEncKey. يمكنه التحقق من سلسلة HMAC الكاملة عبر تدوير المفاتيح وقراءة جميع تفاصيل الحدث، لكنه لا يستطيع فك تشفير أي قيم سرية. يمكن للمراقب العام الذي لديه ملف قاعدة البيانات فقط أن يكتشف ما إذا تم تعديل أي حدث أو إدراجه بعد وقوعه.

مخزن الملح ذو الإصدارات

يتم تخزين ملح KDF كـ SaltStore مشفر بـ msgpack تحت مفتاح البيانات الوصفية salt. كل تدوير للملح يضيف إدخال SaltEntry جديد ويقدم CurrentVersion. يتم الاحتفاظ بالإدخالات القديمة كمسار تدقيق. يتم تخزين SaltStore بدون تشفير — انظر قرارات الأمان.

سجل WAL للتدوير الآمن ضد الأعطال

يكتب Rotate سجل WAL قبل لمس أي سجل. يحمل WAL WrappedOldKey: المفتاح الرئيسي قبل التدوير المشفر بالمفتاح الرئيسي الجديد. بعد الانهيار، تختفي عبارة المرور القديمة؛ WrappedOldKey هو الطريقة الصحيحة الوحيدة لحمل المفتاح القديم عبر الحدود. في UnlockDatabase، عند وجود WAL، يقوم المفتاح الرئيسي الجديد بفك تشفير WrappedOldKey ويستأنف التدوير من مؤشر WAL. يتم تشفير WAL نفسه باستخدام policyEncKey.


سلسلة التدقيق

كل عملية مهمة تُلحق حدثًا واضح التلاعب (tamper-evident) بسلسلة تدقيق الدلو. تعتمد سلامة السلسلة على آليتين.

المجموع الاختباري. SHA-256 على prevChecksum، ID، BucketID، Scheme، Namespace، EncDetails، EventType، وTimestamp. استخدام Scheme/Namespace كنص عادي (محفوظ دائمًا بجانب النماذج المشفرة) يضمن أن المجموع الاختباري مستقر عبر مسارات التحميل. يوفر EncDetails سلامة على الحمولة المشفرة.

HMAC. HMAC-SHA256 على جميع الحقول بما في ذلك Seq. المهاجم الذي يمكنه الكتابة إلى قاعدة البيانات لكنه لا يعرف مفتاح التدقيق لا يمكنه إنتاج HMAC صالح. يتحقق VerifyIntegrity من كلا الطبقتين لكل حدث.

حدود حقبة تدوير المفاتيح. في Rotate، يتم إلحاق حدث نقطة تفتيش (checkpoint event) بكل سلسلة نشطة تحمل بصمات كل من مفتاح التدقيق الصادر والوارد. يتم توقيع نقطة التفتيش بالمفتاح الصادر. يمكن لمدققي التدقيق الذين يحملون أي مفتاح حقبة استعادة مفاتيح الحقبة اللاحقة من حقل wrapped_new_key والتحقق من استمرارية HMAC عبر السلسلة الكاملة.

التقليم التلقائي. عند تعيين AuditPruneInterval في Config، يعمل jack.Scheduler بشكل دوري ويستدعي PruneEvents على كل دلو مسجل. لا يتم تقليم دلاء LevelHSM و LevelRemote أبدًا بغض النظر عن هذا الإعداد.


تكامل Jack

Jack هي مكتبة إشراف على العمليات اختيارية. عند توفير JackConfig عبر WithJack، يقوم keeper بتنشيط المكونات الخلفية تلقائيًا:

  • حلقة القفل التلقائي: تتحقق بشكل دوري من طابع النشاط الأخير وتتخلص من مفاتيح DEK لدلو LevelAdminWrapped بعد AutoLockInterval. تبقى دلاء LevelPasswordOnly مفتوحة حتى تستمر المهام الخلفية دون انقطاع. نمط قفل الكتابة الواحد داخل مهمة الحلقة يلغي حالة السباق RUnlock→Lock الموجودة في التصاميم السابقة.
  • جامع DEK لكل دلو: انتهاء الصلاحية استنادًا إلى TTL لمفاتيح DEK الخاصة بـ LevelAdminWrapped.
  • مرضى مراقبة الصحة: فحص زمن استجابة القراءة لـ bbolt والتحقق من إرسال واستقبال التشفير/فك التشفير، كلاهما مسجلان مع jack.Doctor.
  • جدولة تقليم التدقيق: PruneEvents دوري على جميع الدلاء غير HSM.
  • تجمع الأحداث غير المتزامن: يتم إرسال أحداث التدقيق دون حظر العملية الرئيسية.

إذا لم يتم توفير JackConfig، يعمل keeper بدون هذه المهام الخلفية. لا يستدعي Keeper أبدًا pool.Shutdown — دورة حياة التجمع تعود للمتصل.


x/keepcmd

توفر x/keepcmd عمليات keeper قابلة لإعادة الاستخدام منفصلة عن أي إطار CLI. قم بتضمينه في تطبيقك الخاص للحصول على إدارة أسرار مكتوبة وقابلة للاختبار دون سحب ثنائي CLI.```go import "github.com/agberohq/keeper/x/keepcmd"

cmds := &keepcmd.Commands{ Store: func() (*keeper.Keeper, error) { return security.KeeperOpen(cfg) // your own config }, Out: keepcmd.PlainOutput{}, NoClose: false, // true in REPL / session contexts }

cmds.List() // all keys: scheme://namespace/key cmds.List("vault") // all keys in scheme vault cmds.List("vault", "system") // all keys in vault://system cmds.Get("vault://system/jwt_secret") cmds.Set("vault://system/jwt_secret", "newsecret", keepcmd.SetOptions{}) cmds.Rotate(newPassphraseBytes) // caller resolved the passphrase — no prompter dependency cmds.RotateSalt(currentPassBytes) // same

root@kitploit:~
`keepcmd` never calls `prompter` or reads from stdin. Passphrase resolution
is entirely the caller's responsibility — this keeps the package safe in
headless server contexts.

`NoClose: true` prevents `Commands` from calling `store.Close()` after each
operation. Use this in REPL / session contexts where one store is shared
across many calls.

---

## x/keephandler

`x/keephandler` mounts keeper HTTP endpoints on any `net/http` mux. No
external router dependency — it uses Go 1.22+ method+pattern routing with
stdlib `http.ServeMux`.```go
import "github.com/agberohq/keeper/x/keephandler"

keephandler.Mount(mux, store,
    keephandler.WithPrefix("/api/keeper"),
    keephandler.WithGuard(func(w http.ResponseWriter, r *http.Request, route string) bool {
        if !acl.Allow(r.Header.Get("X-Principal"), route) {
            http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
            return false
        }
        return true
    }),
    keephandler.WithHooks(
        keephandler.Hook{
            Route:       keephandler.RouteGet,
            CaptureBody: false,
            After: func(r *http.Request, status int, _ []byte) {
                audit.Log(r.Context(), route, status)
            },
        },
    ),
    keephandler.WithEncoder(func(w http.ResponseWriter, route string, status int, data any) {
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(status)
        json.NewEncoder(w).Encode(map[string]any{
            "ok":    status < 400,
            "route": route,
            "data":  data,
        })
    }),
    keephandler.WithRoutes(func(m *http.ServeMux) {
        m.HandleFunc("POST /api/keeper/totp/{user}", myTOTPHandler)
    }),
)

نقاط النهاية

اتفاقية الخطاف

BeforeFunc تُرجع (allow bool, err error).

  • (true, nil) — السماح بمواصلة الطلب.
  • (false, nil) — الإحباط؛ كتب الخطاف بالفعل استجابة كاملة.
  • (false, err) — الإحباط؛ يكتب الإطار استجابة 500 باستخدام err.Error(). يجب ألا يكون الخطاف قد كتب أي شيء إلى w.

Hook.CaptureBody bool يتحكم فيما إذا كانت AfterFunc تتلقى نص الاستجابة. false (القيمة الافتراضية) تكلف غلاف statusWriter خفيف واحد؛ true يخزن النص الكامل في bytes.Buffer لـ AfterFunc — تخصيص واحد لكل طلب.

يتم تنفيذ الخطافات بترتيب التسجيل. استدعاءات WithHooks المتعددة تراكمية. فقط أول خطاف مسجل لاسم مسار معين يُستخدم—يتم تجاهل التسجيلات اللاحقة لنفس المسار.


مرجع API

البناء وفتح القفل```go

store, err := keeper.New(keeper.Config{ DBPath: "/var/lib/agbero/keeper.db", AutoLockInterval: 30 * time.Minute, EnableAudit: true, AuditPruneInterval: 24 * time.Hour, AuditPruneKeepLastN: 10_000, AuditPruneOlderThan: 90 * 24 * time.Hour, DBLatencyThreshold: 200 * time.Millisecond, Logger: logger, }, keeper.WithJack(keeper.JackConfig{ Pool: jackPool, Shutdown: jackShutdown, })) defer store.Close()

// Shorthand (wraps DeriveMaster + UnlockDatabase): if err := store.Unlock([]byte(os.Getenv("KEEPER_PASSPHRASE"))); err != nil { log.Fatal(err) // ErrInvalidPassphrase on wrong passphrase }

root@kitploit:~
`UnlockDatabase` يقوم بما يلي بالترتيب:

1. اشتقاق وتفعيل مفتاح توقيع HMAC للتدقيق (audit HMAC signing key)
2. اشتقاق وتفعيل مفتاح HMAC للسياسة (policy HMAC key)
3. اشتقاق وتفعيل `policyEncKey` و `auditEncKey`
4. مسح وإعادة تحميل `schemeRegistry` (فك تشفير جميع كتل السياسة)
5. استئناف أي سجل WAL للتدوير المتقطع
6. ترقية علامات HMAC للسياسة
7. تغذية جميع مفاتيح DEK لحاوية `LevelPasswordOnly` في المغلف (Envelope)
8. بدء المهام الخلفية (حلقة الترحيل، القفل التلقائي، مرضى الصحة)

### حاوية `LevelPasswordOnly` — دورة الحياة الكاملة```go
err := store.CreateBucket("vault", "system", keeper.LevelPasswordOnly, "init")

store.Set("vault://system/jwt_secret", []byte("supersecret"))
val, err := store.Get("vault://system/jwt_secret")

// Namespaced convenience wrappers
store.SetNamespaced("admin", "jwt_secret", secretBytes)
val, err = store.GetNamespaced("admin", "jwt_secret")

LevelAdminWrapped bucket — دورة حياة كاملة```go

err := store.CreateBucket("finance", "payroll", keeper.LevelAdminWrapped, "ops-team") err = store.AddAdminToPolicy("finance", "payroll", "alice", []byte("alicepass"))

store.SetNamespacedFull("finance", "payroll", "salary_key", []byte("AES256..."))

store.LockBucket("finance", "payroll") err = store.UnlockBucket("finance", "payroll", "bob", []byte("bobpass")) // ErrAuthFailed — does not distinguish wrong password from unknown admin (CWE-204)

err = store.RevokeAdmin("finance", "payroll", "alice") err = store.RotateAdminWrappedDEK("finance", "payroll", "bob", []byte("bobpass"))

needs, err := store.NeedsAdminRekey("finance", "payroll")

root@kitploit:~
### LevelHSM / LevelRemote الدلاء```go
import (
    "github.com/agberohq/keeper/pkg/hsm"
    "github.com/agberohq/keeper/pkg/remote"
)

// SoftHSM — testing only
provider, _ := hsm.NewSoftHSM()
store.RegisterHSMProvider("secure", "keys", provider)
store.CreateBucket("secure", "keys", keeper.LevelHSM, "ops")

// Vault Transit
cfg := remote.VaultTransit("https://vault.corp:8200", vaultToken, "my-key")
cfg.TLSClientCert = "/etc/keeper/client.crt"
cfg.TLSClientKey  = "/etc/keeper/client.key"
provider, _ = remote.New(cfg)
store.RegisterHSMProvider("tenant", "secrets", provider)
store.CreateBucket("tenant", "secrets", keeper.LevelRemote, "ops")

تصدير مفتاح التدقيق```go

// Export the audit encryption key to allow a third-party auditor to decrypt // event details without access to the master passphrase. auditKey, err := store.ExportAuditKey() defer zero.Bytes(auditKey)

events, err := auditStore.LoadChain("vault", "system", auditKey)

root@kitploit:~
### تدوير المفاتيح```go
// Rotate passphrase — crash-safe WAL, resumes on next Unlock if interrupted
store.Rotate([]byte("new-passphrase"))

// Rotate KDF salt — re-derives master key, re-encrypts LevelPasswordOnly
store.RotateSalt([]byte("current-passphrase"))

Compare-and-swap```go

err := store.CompareAndSwapNamespacedFull("vault", "system", "counter", []byte("old"), []byte("new")) // ErrCASConflict if current value does not match old

root@kitploit:~
### نسخ احتياطي```go
f, _ := os.Create("keeper.db.bak")
info, err := store.Backup(f)
// info.Bytes, info.Timestamp, info.DBPath

كتالوج الأخطاء

جميع الأخطاء الحارسة تعمل مع errors.Is و errors.As. يتم التقاط تتبعات المكدس عند نقطة الإنشاء عبر github.com/olekukonko/errors.


قرارات الأمان

ErrAuthFailed يوحد جميع حالات فشل UnlockBucket (CWE-204 / CVSS 5.3). سواء كان معرف مدير غير معروف أو كلمة مرور خاطئة، يتم إرجاع ErrAuthFailed. هذا يمنع تعداد معرفات المديرين عن طريق التوقيت أو مقارنة سلسلة الخطأ. يحتفظ RevokeAdmin بـ ErrAdminNotFound لأنه عملية إدارية على مخزن تم فتح قفله بالفعل. تم حذف المقارنة ذات الوقت الثابت لوجود معرف المدير عمدًا. المهاجم الذي يمكنه قياس اختلافات أقل من ميكروثانية في عمليات البحث عن جرافات bbolt سيحتاج إلى وصول إلى نظام الملفات المحلي — وعندها يمكنه قراءة جرافة السياسة مباشرة. يفترض نموذج التهديد أن ملف قاعدة البيانات قد يكون مخترقًا؛ الدفاع عن التوقيت ضد التعداد عن بُعد هو الاهتمام الأساسي.

Argon2id يهيمن على التوقيت. يستغرق Argon2id من 200 إلى 500 مللي ثانية على الأجهزة النموذجية. اختلافات المقارنة بعد الاشتقاق أصغر بأربعة مراتب أو أكثر ولا يمكن قياسها عن بُعد. لا يتم تطبيق أي معادلة مصطنعة.

استرجاع DEK داخل حدود معاملة CAS. يقوم CompareAndSwapNamespacedFull باسترجاع DEK للجرافة داخل معاملة الكتابة في bbolt، مما يلغي النافذة الزمنية التي يمكن أن يغير فيها Rotate المتزامن DEK بين الاسترجاع والاستخدام.

لا يتم تخزين عبارة المرور أبدًا كسلسلة Go في معالج HTTP. يتم فك ترميز جميع حقول عبارة المرور الثلاثة (passphrase، new_passphrase) من JSON مباشرة إلى []byte عبر استخراج الخريطة الخام، مما يبقي مصفوفة السلاسل الخلفية بعيدًا عن الكومة طويلة العمر. يتم مسخ نسخة []byte باستخدام wipeBytes بعد الاستخدام.

لا يوجد علم --passphrase في واجهة سطر الأوامر. تظهر الأعلام في مخرجات ps وسجل القشرة. تقبل واجهة سطر الأوامر عبارة المرور فقط من متغير البيئة KEEPER_PASSPHRASE أو موجه تفاعلي بدون صدى.

قيم REPL السرية غير مرئية أبدًا. يستخدم set <key> في REPL بدون قيمة مضمنة term.ReadPassword — لا تظهر في التمرير الطرفي، أو سجل القشرة، أو ps. يمكن توفير قيمة مضمنة (set key value) للبيانات غير الحساسة عند الحاجة.

SaltStore غير مشفرة عمدًا. يجب أن يكون ملح KDF قابلاً للقراءة قبل UnlockDatabase لاشتقاق المفتاح الرئيسي. policyEncKey (المستخدمة لتشفير جميع البيانات الوصفية الأخرى) مشتقة بدورها من المفتاح الرئيسي — تشفير الملح بـ policyEncKey سيكون دائريًا. يوفر ملح KDF التفرد، وليس السرية؛ لا توجد قيمة أمنية في تشفيره.

مفاتيح جرافة السياسة مشفرة، وليست نصًا عاديًا. مفاتيح السياسة على القرص هي hex(SHA-256("scheme:namespace"))[:32] — 128 بت من مساحة المفاتيح — بدلاً من سلاسل قابلة للقراءة. المهاجم غير المتصل الذي يقرأ ملف bbolt لا يمكنه تعداد أسماء الجرافات دون فك تشفير كتل السياسة.

يستخدم تشفير البيانات الوصفية نفس واجهة التشفير للأسرار. تمر جميع عمليات policyEncKey و auditEncKey عبر s.config.NewCipher(key) — نفس واجهة crypt.Cipher التي تم تكوينها لقيم الأسرار. ينتقل اختيار المستخدم للشيفرة (AES-256-GCM لـ FIPS 140، XChaCha20-Poly1305 افتراضيًا) تلقائيًا إلى تشفير السياسة، WAL، والتدقيق. لا يوجد مسار كود يحدد خوارزمية معينة بشكل ثابت.

يتم تخطي جرافات LevelHSM و LevelRemote أثناء تدوير المفتاح الرئيسي. يتخطى reencryptAllWithKey و RotateSalt هذه الجرافات بشكل صريح. DEK يتحكم فيه المزود؛ تدوير ملح المفتاح الرئيسي لا يؤثر عليه.

تدوير آمن للتعطل مع WrappedOldKey. يكتب Rotate WAL قبل لمس أي سجل. يحمل WAL WrappedOldKey: المفتاح الرئيسي قبل التدوير مشفرًا بالمفتاح الرئيسي الجديد. بعد تعطل، يقوم UnlockDatabase بفك تشفير WrappedOldKey باستخدام المفتاح الجديد الموثوق ويستأنف التدوير من المؤشر.


التبعيات

تنزيل الأداة
المشغلعبارة مرور رئيسيةكل شيء
الطريقةالمسارالوصف
POST{prefix}/unlockفتح المتجر باستخدام عبارة مرور
POST{prefix}/lockقفل المتجر
GET{prefix}/statusحالة القفل — آمن للاستعلام دون مصادقة
GET{prefix}/keysسرد جميع المفاتيح السرية
GET{prefix}/keys/{key}استرجاع قيمة سرية
POST{prefix}/keysتخزين سر (JSON أو متعدد الأجزاء)
DELETE{prefix}/keys/{key}حذف سر
POST{prefix}/rotateتدوير عبارة المرور الرئيسية
POST{prefix}/rotate/saltتدوير ملح KDF
GET{prefix}/backupبث لقطة من قاعدة البيانات
الخطأالمعنى
ErrStoreLockedتمت محاولة عملية بينما المخزن مقفل
ErrInvalidPassphraseعبارة مرور رئيسية خاطئة
ErrAuthFailedأي فشل في UnlockBucket — لا يميز بين كلمة مرور خاطئة ومعرف مدير غير معروف (CWE-204)
ErrKeyNotFoundمفتاح سري غير موجود
ErrBucketLockedلم يتم فتح قفل الجرافة
ErrPolicyImmutableسياسة ثانية لجرافة موجودة
ErrPolicyNotFoundلا توجد سياسة للمخطط/مساحة الأسماء المحددة
ErrAdminNotFoundمعرف المدير غير موجود في السياسة — فقط RevokeAdmin
ErrHSMProviderNilتم إنشاء جرافة HSM/عن بعد بدون مزود مسجل
ErrCheckLatencyتجاوز زمن قراءة قاعدة البيانات DBLatencyThreshold
ErrCASConflictالقيمة الحالية لا تطابق المتوقعة في CompareAndSwap
ErrSecurityDowngradeنقل عبر الجرافات من مستوى أمان أعلى إلى أدنى
ErrAlreadyUnlockedتم استدعاء UnlockDatabase على مخزن تم فتح قفله بالفعل
ErrMasterRequiredتم استدعاء UnlockDatabase مع Master فارغ أو مدمر
ErrChainBrokenفشل التحقق من سلامة سلسلة التدقيق
ErrMetadataDecryptتعذر فك تشفير البيانات الوصفية المشفرة
ErrPolicySignatureفشل التحقق من HMAC للسياسة — تم العبث بالسجل
الحزمةالغرض
go.etcd.io/bboltمخزن قيم-مفاتيح مدمج
golang.org/x/cryptoArgon2id, XChaCha20-Poly1305, HKDF, scrypt
github.com/awnumar/memguardحاوية مفاتيح آمنة في الذاكرة (مفتاح رئيسي، مفاتيح تشفير البيانات)
github.com/vmihailenco/msgpack/v5تسلسل ثنائي للأسرار والسياسات
github.com/olekukonko/jackإشراف على العمليات (دمج Jack اختياري)
github.com/olekukonko/llتسجيل منظم
github.com/olekukonko/errorsأخطاء حارسة مع تتبعات المكدس
github.com/olekukonko/zeroتصفير آمن لشرائح البايت
github.com/olekukonko/prompterمطالبات طرفية بدون صدى (سطر الأوامر فقط)
github.com/integrii/flaggyتحليل أعلام سطر الأوامر (cmd/keeper فقط)
golang.org/x/termكشف TTY وقراءة كلمة المرور الخام (سطر الأوامر فقط)