لودسک · Security Whitepaper · نسخهٔ ۱

سند فنی امنیت و رمزنگاری سرتاسری

برای کارشناسان فنی، حقوقی و نهادهایی که می‌خواهند ادعاهای امنیتی لودسک را راستی‌آزمایی کنند.

فهرست

  1. خلاصهٔ مدیریتی
  2. مدل تهدید
  3. الگوریتم‌ها و پارامترها
  4. سلسله‌مراتب کلیدها
  5. روندها
  6. آنچه سرور می‌بیند و آنچه نمی‌بیند
  7. لایهٔ دوم روی سرور، انتقال و زیرساخت
  8. دستیار هوشمند
  9. پشتیبان‌گیری و حذف
  10. راستی‌آزمایی مستقل
  11. محدودیت‌ها و نقشهٔ راه

۱. خلاصهٔ مدیریتی

لودسک میز کار وکیل است: موکلان، پرونده‌ها، جلسه‌ها، مهلت‌ها، یادداشت‌ها، امور مالی و اسناد. این اطلاعات مشمول رازداری حرفه‌ای وکیل است. هدف طراحی این است که اپراتور سامانه — یعنی خود ما — نتواند این اطلاعات را بخواند، حتی اگر بخواهد یا مجبور شود.

  • همهٔ رکوردها و فایل‌ها روی دستگاه کاربر با AES-256-GCM رمز می‌شوند، پیش از آنکه چیزی به شبکه برسد.
  • کلید داده روی دستگاه ساخته می‌شود و هرگز به‌صورت باز از دستگاه خارج نمی‌شود.
  • سرور فقط «پاکت کلید» را نگه می‌دارد: کلید داده که با کلیدی برگرفته از رمز گاوصندوق کاربر و با کلید بازیابی او بسته‌بندی شده است. رمز گاوصندوق و کلید بازیابی هرگز به سرور فرستاده نمی‌شوند.
  • سرور پس از فعال‌شدن گاوصندوق هر نوشتنی را که متن رمزشده نباشد رد می‌کند؛ رکورد بدون رمز حتی به اشتباه هم پذیرفته نمی‌شود.

۲. مدل تهدید

در برابر این‌ها محافظت می‌شود

  • نفوذ به سرور یا پایگاه داده: مهاجم فقط متن رمزشده و پاکت‌های کلید به دست می‌آورد.
  • مدیر یا کارمند کنجکاو یا مخرب: هیچ ابزاری در پنل یا روی سرور، محتوای میز کار را باز نمی‌کند، چون کلید آن روی سرور نیست.
  • سرقت یا نشت نسخهٔ پشتیبان: پشتیبان‌ها همان متن رمزشده را دارند.
  • درخواست اجباری از اپراتور: اپراتور فقط متن رمزشده و فرادادهٔ فهرست‌شده در بخش ۶ را در اختیار دارد.
  • شنود شبکه: علاوه بر رمزنگاری سرتاسری، انتقال با HTTPS است.
  • جابه‌جایی یا دست‌کاری متن رمزشده: داده‌های همراه (AAD) شناسهٔ حساب، شناسهٔ رکورد و نوع آن را به هر متن رمزشده گره می‌زنند؛ متن جابه‌جاشده یا دست‌کاری‌شده باز نمی‌شود.

در برابر این‌ها محافظت نمی‌شود

  • دستگاه آلوده به بدافزار یا دستگاه باز در دست دیگری؛ آنچه روی دستگاه باز است دیده می‌شود. (قفل برنامه با رمز و اثر انگشت این خطر را کم می‌کند؛ فایل محلی برنامه هم رمزشده است، ولی کلید آن در Keystore همان گوشی است.)
  • رمز گاوصندوق ضعیف و قابل حدس. PBKDF2 با ۶۰۰٬۰۰۰ دور حدس‌زدن را کُند می‌کند، اما رمز کوتاه را امن نمی‌کند.
  • تحلیل فراداده (بخش ۶): مثلاً تعداد رکوردها و زمان ویرایش‌ها.
  • تغییر عمدی کد نسخهٔ وب توسط اپراتور (محدودیت ذاتی هر اپلیکیشن وبی). اپ اندروید با کلید امضای ثابت منتشر می‌شود و این خطر را ندارد.
  • آنچه کاربر خودش به دستیار هوشمند می‌فرستد (بخش ۸).

۳. الگوریتم‌ها و پارامترها

کاربردالگوریتمپارامتر
رمزنگاری رکورد و فایلAES-256-GCMکلید ۲۵۶ بیت، nonce تصادفی ۹۶ بیتی برای هر رمزنگاری، برچسب احراز ۱۲۸ بیتی
کلید از رمز گاوصندوقPBKDF2-HMAC-SHA256۶۰۰٬۰۰۰ دور (توصیهٔ OWASP، ۲۰۲۳)، نمک تصادفی ۱۲۸ بیتی، خروجی ۲۵۶ بیت؛ سرور کمتر از ۶۰۰٬۰۰۰ را نمی‌پذیرد
کلید از کلید بازیابیHKDF-SHA256ورودی ۱۶۰ بیت تصادفی، نمک ۱۲۸ بیتی، info: lawdesk/recovery/v1
بسته‌بندی کلید دادهAES-256-GCMAAD: lawdesk/vault/v1|userId|pass و …|recovery
شناسهٔ کلیدHMAC-SHA256HMAC کلید داده روی برچسب ثابت؛ برای تشخیص دستگاهی که کلید دیگری دارد، بدون افشای کلید
تولید عدد تصادفیCSPRNG سیستم‌عاملRandom.secure() دارت (روی اندروید/iOS از منبع امن سیستم‌عامل)
لایهٔ دوم سرورAES-256-GCMکلید جدا برای هر حساب، بسته‌بندی‌شده با کلید اصلی بیرون از پایگاه داده

پیاده‌سازی رمزنگاری در اپ با کتابخانهٔ متن‌باز و شناخته‌شدهٔ cryptography برای Dart انجام شده است؛ روی اندروید و iOS از رمزنگاری بومی سیستم‌عامل و در مرورگر از WebCrypto استفاده می‌کند. هیچ الگوریتم اختصاصی یا دست‌ساز به کار نرفته است.

۴. سلسله‌مراتب کلیدها

Vault passphrase (user's memory)  ──PBKDF2-SHA256 600k──▶ KEK_pass ─┐
Recovery key, 160-bit (user's paper) ──HKDF-SHA256────────▶ KEK_rec  ─┤ wrap (AES-256-GCM)
                                                                      ▼
                                   DEK, 256-bit random (device only, Keychain / Keystore)
                                                                      │ encrypt (AES-256-GCM, fresh nonce, AAD)
                                                                      ▼
                                   records  { "_e2e":1, "c": nonce‖ciphertext‖tag }
                                   files    "LDE2E1" ‖ nonce ‖ ciphertext ‖ tag

Server keeps:  envelope = { kdf:{alg,iter,salt}, pass: wrap(KEK_pass,DEK),
                            recovery:{salt, wrapped: wrap(KEK_rec,DEK)} },  key_id = HMAC(DEK,label)
Server never receives: passphrase, recovery key, KEK_*, DEK

۵. روندها

۵.۱ ساخت گاوصندوق

  1. دستگاه DEK تصادفی، کلید بازیابی تصادفی و دو نمک می‌سازد.
  2. DEK را با KEK_pass و KEK_rec بسته‌بندی می‌کند و پاکت را همراه key_id به PUT /desk/vault می‌فرستد. سرور فقط ساختار پاکت را بررسی می‌کند (و پاکتی را که فیلد ناشناخته داشته باشد، مثلاً خود رمز، رد می‌کند).
  3. کلید بازیابی یک بار نمایش داده می‌شود و کاربر تأیید می‌کند که آن را نوشته است.
  4. DEK در Keychain / Android Keystore دستگاه نگه داشته می‌شود؛ در مرورگر فقط در حافظه.
  5. داده‌های موجود (برای حساب‌های قدیمی) روی دستگاه رمز و دوباره ارسال می‌شوند؛ فایل‌ها رمزشده بارگذاری و نسخهٔ بدون رمز قبلی پاک می‌شود.

۵.۲ دستگاه تازه

دستگاه پاکت را دریافت می‌کند، از رمز وارد‌شده KEK_pass را می‌سازد و DEK را باز می‌کند. اگر رمز اشتباه باشد، برچسب احراز GCM رد می‌شود؛ سرور از تلاش‌ها و درستی رمز اطلاعی ندارد. key_id بازشده با key_id سرور مقایسه می‌شود.

۵.۳ تغییر رمز

همان DEK با KEK تازه دوباره بسته‌بندی می‌شود؛ داده‌ها دوباره رمز نمی‌شوند و کلید بازیابی معتبر می‌ماند. سرور فقط پاکتی را جایگزین می‌کند که key_id آن با کلید فعلی یکی باشد.

۵.۴ بازیابی

با کلید بازیابی، DEK باز و رمز تازه تعیین می‌شود. بدون رمز و کلید بازیابی، کاربر می‌تواند با تأیید صریح نسخهٔ قفل‌شدهٔ سرور را پاک کند و گاوصندوق تازه بسازد؛ آنچه روی دستگاهش هست دوباره همگام می‌شود. اپراتور هیچ راه بازیابی دیگری ندارد.

۵.۵ نسخهٔ روی دستگاه

نسخه‌ای که برای کار بدون اینترنت روی دستگاه می‌ماند هم با همان DEK و AES-256-GCM (AAD: lawdesk/local/v1|userId) رمز می‌شود؛ فایل محلی گوشی و حافظهٔ مرورگر فقط متن رمزشده دارند. روی گوشی DEK در Keychain / Keystore است و برنامه بی‌وقفه باز می‌شود؛ در مرورگر DEK نگه داشته نمی‌شود و نسخهٔ محلی تا وارد کردن رمز گاوصندوق قفل می‌ماند. تا وقتی نسخهٔ محلی با کلید موجود باز نشود، برنامه هرگز روی آن نمی‌نویسد. اگر گاوصندوق از دستگاه دیگری بازنشانی شود، کلید قبلی فقط برای خواندن همین نسخه کنار گذاشته و پس از ذخیرهٔ دوباره با کلید تازه پاک می‌شود.

۵.۶ همگام‌سازی و تعارض

هر تغییر پیش از ارسال رمز می‌شود. سرور فقط به شمارهٔ نسخه‌ها نگاه می‌کند: نوشتن روی نسخهٔ قدیمی را رد و نسخهٔ فعلی (رمزشده) را برمی‌گرداند؛ دستگاه آن را باز و فیلدبه‌فیلد ادغام می‌کند و دوباره رمزشده می‌فرستد. ادغام فقط روی دستگاه انجام می‌شود.

۶. آنچه سرور می‌بیند و آنچه نمی‌بیند

بدون رمز (فراداده)رمزشده
شمارهٔ موبایل و نام و نام خانوادگی صاحب حساب؛ وضعیت اشتراکنام و مشخصات موکلان و اشخاص
شناسهٔ تصادفی (UUID) و نوع هر رکورد؛ حذف‌شده یا نهعنوان، شماره و جزئیات پرونده‌ها؛ نام دادگاه و شعبه
زمان ایجاد و ویرایش؛ شمارهٔ نسخهجلسه‌ها، مهلت‌ها و وظایف با تاریخ و ساعت
حجم متن رمزشدهٔ هر رکورد و هر فایلیادداشت‌ها، صورتحساب‌ها، دریافتی‌ها و هزینه‌ها
شمار پیام‌های روزانهٔ دستیار (سهمیه)نام، نوع و محتوای فایل اسناد؛ قراردادها و لوایح
پیام‌هایی که کاربر به پشتیبانی می‌نویسد، و در صورت رضایت او نسخهٔ اپ، سیستم‌عامل، اندازهٔ صفحه و پلن—

یادآوری پیامکی (اختیاری، پیش‌فرض خاموش). وقتی کاربر آن را روشن کند، برای هر رویدادی که یادآوری می‌شود فقط این‌ها به سرور می‌رود: یک شناسهٔ مبهم (۱۲۸ بیت اول SHA-256 شناسهٔ رکورد)، نوع (جلسهٔ دادگاه، مهلت قانونی، جلسه با موکل، کارشناسی، وظیفه یا رویداد) و تاریخ و ساعت. متن پیامک از همین دو ساخته می‌شود («جلسهٔ دادگاه — ۱۵ مهر ۱۰:۳۰»). عنوان، موکل، دادگاه و هر متن دیگری فرستاده نمی‌شود؛ اما زمان و نوع رویدادهای یادآوری‌شده برای سرور و سرویس پیامک روشن است. با خاموش کردن، یادآورهای منتظر از سرور حذف می‌شوند.

اعلان‌های لودسک (پوش). برای رساندن اطلاعیه‌های تیم لودسک، گوشی (FCM) یا مرورگر (Web Push طبق RFC 8030/8291 با کلید VAPID خود سرور) یک توکن پوش می‌سازد و همراه نسخهٔ برنامه و برچسب «لودسک» روی سرور ثبت می‌شود؛ با خروج از حساب حذف می‌شود. متن اطلاعیه را تیم لودسک در پنل می‌نویسد و از هیچ داده‌ای از میز کار ساخته نمی‌شود؛ اعلان‌های یادآوری جلسه همچنان روی خود گوشی زمان‌بندی می‌شوند و از سرور نمی‌گذرند.

استعلامات رسمی (فقط وقتی کاربر استعلام می‌گیرد). سامانهٔ زحل فقط به IP ثبت‌شدهٔ سرور پاسخ می‌دهد و توکن آن نباید در اپ باشد، پس هر استعلام از سرور می‌گذرد: فیلدهای همان سرویس (مثلاً کد ملی، شناسهٔ ملی شرکت، شناسهٔ صیادی، شبا، کد پستی) بدون رمز سمت‌کاربر به سرور و از آنجا روی HTTPS به زحل می‌رود و پاسخ به گوشی برمی‌گردد. سرور ورودی و پاسخ را ذخیره و لاگ نمی‌کند؛ دفتر کیف پول استعلام فقط این‌ها را نگه می‌دارد: کاربر، نوع سرویس، مبلغ، وضعیت (موفق/برگشت‌خورده) و زمان. قیمت پیش از تماس با زحل از کیف پول کسر و در صورت ناموفق بودن برگردانده می‌شود. نتیجه روی گوشی با AES-256-GCM و کلیدی ۲۵۶ بیتی که فقط در Keychain/Keystore گوشی است رمز می‌شود و پس از ۱۵ روز حذف می‌شود؛ ورودی در سوابق نیمه‌پنهان ذخیره می‌شود. این کلید جدا از کلید گاوصندوق است: سوابق استعلام بخشی از میز کار رمزنگاری‌شدهٔ سرتاسری نیست و با نصب دوبارهٔ برنامه از دست می‌رود.

۷. لایهٔ دوم روی سرور، انتقال و زیرساخت

روی متن رمزشده‌ای که از دستگاه می‌رسد، سرور یک لایهٔ رمزنگاری دیگر (AES-256-GCM با کلید مخصوص هر حساب) هم اعمال می‌کند. کلید اصلی این لایه در فایلی جدا از پایگاه داده با دسترسی محدود نگه داشته می‌شود. این لایه امنیت اصلی نیست؛ دفاع در عمق است.

ارتباط اپ و سرور فقط از راه HTTPS است. فایل‌های اسناد بیرون از پوشهٔ عمومی وب و با مجوز فقط برای سرویس نگه‌داری می‌شوند. سرور روی زیرساخت داخل ایران میزبانی می‌شود.

احراز هویت حساب. رمز عبور با bcrypt ذخیره می‌شود. ورود دومرحله‌ای اختیاری با TOTP (RFC 6238؛ HMAC-SHA1، ۶ رقم، گام ۳۰ ثانیه، پنجرهٔ ±۱ گام؛ راز ۱۶۰ بیتی). راز با AES-256-GCM و کلیدی جدا از کلید تنظیمات رمز می‌شود؛ کد پذیرفته‌شده در همان گام دوباره پذیرفته نمی‌شود. ۸ کد بازیابی ۱۰ نویسه‌ای (حدود ۵۰ بیت) فقط به‌صورت SHA-256 نمک‌دار ذخیره و پس از مصرف باطل می‌شوند. پنج خطای پیاپی، ۱۵ دقیقه قفل. وقتی ورود دومرحله‌ای روشن است، مرحلهٔ اول (رمز یا پیامک) فقط یک چالش ۵ دقیقه‌ای یک‌بارمصرف می‌دهد و بازنشانی رمز با پیامک هم کد برنامه را می‌خواهد.

۸. دستیار هوشمند

دستیار تنها استثنای آگاهانه است. وقتی کاربر پرسشی می‌فرستد، متن پرسش و فقط بخش‌هایی از میز کار که کاربر در «دسترسی دستیار» روشن کرده است (پرونده، تقویم، مالی) روی دستگاه به متن تبدیل و از راه سرور به سرویس مدل زبانی فرستاده می‌شود تا پاسخ ساخته شود. این متن در لودسک ذخیره نمی‌شود. همهٔ دسترسی‌ها خاموش‌شدنی است و بدون پرسش، چیزی فرستاده نمی‌شود. با دسترسی «پرونده‌ها»، دستگاه فقط پرونده‌ای را می‌فرستد که دستیار از آن باز شده یا پرسش نام پرونده، موکل، طرف مقابل یا شمارهٔ آن را آورده (حداکثر سه)، و برای پرسش کلی دربارهٔ «پرونده‌هایم» فهرست پرونده‌های باز (عنوان، موکل، وضعیت، مرجع، تاریخ رویداد بعدی؛ حداکثر ۲۵). سه پرسش‌وپاسخ آخر همان گفتگو هم همراه پرسش می‌رود و ذخیره نمی‌شود. پرسشی که به میز کار مربوط است در «پرسش‌های بی‌پاسخ» ثبت نمی‌شود.

بازیابی منبع: سرور با جست‌وجوی متن کامل روی کتابخانهٔ قوانین و پاسخ‌های تأییدشده (پرسش‌وپاسخ‌هایی که یک حقوقدان نوشته و حقوقدان دیگری تأیید کرده) چند ماده را به پرسش ضمیمه می‌کند. پرسشی که منبعی برایش پیدا نشود، بدون شناسهٔ کاربر، با پوشاندن هر رشتهٔ پنج‌رقمی یا بلندتر و بدون متن میز کار، برای تیم حقوقی نگه داشته و پس از مدتی که پنل تعیین می‌کند (پیش‌فرض ۳۰ روز) حذف می‌شود؛ این ثبت در پنل خاموش‌شدنی است. برای هر درخواست فقط نتیجه، زمان پاسخ، تعداد منبع، نسخهٔ پرامپت و بازخورد «مفید بود / نبود» ثبت می‌شود، بدون کاربر و بدون متن.

تاریخچهٔ گفتگو و سکه: گفتگوهای دستیار روی دستگاه با AES-GCM و کلیدی تصادفی در Keychain یا Android Keystore نگه داشته می‌شود و به سرور نمی‌رود. «اعتبار هوش مصنوعی» یک کیف پول سکه روی سرور است: موجودی، و برای هر برداشت فقط نوع کار (پیام، لایحه، قرارداد)، تعداد سکه و زمان؛ هر خرید با شمارهٔ سفارش یک بار اعتبار می‌شود.

نگارش هوشمند: قالب‌ها (۱۷۱۰ قالب) و پرامپت‌ها داخل سرور است و مدل آموزش داده نمی‌شود. مسیریابی (انتخاب قالب) متن توضیح کاربر را به مدل می‌دهد؛ گفتگوی نگارش بی‌حالت است: دستگاه در هر نوبت کل گفتگو (و اگر کاربر پرونده‌ای انتخاب کرده، متن همان پرونده) را می‌فرستد و سرور آن را به مدل می‌دهد و چیزی از آن ذخیره نمی‌کند؛ در لاگ فقط شناسهٔ قالب، شمار نوبت، تعداد توکن و زمان پاسخ می‌آید. خروجی Word در حافظه ساخته و برگردانده می‌شود.

۹. پشتیبان‌گیری و حذف

از پایگاه داده هر شب نسخهٔ پشتیبان گرفته می‌شود. برای حساب‌هایی که گاوصندوق دارند، میز کار در پشتیبان هم فقط متن رمزشده است. با حذف حساب، رکوردها، فایل‌ها و پاکت کلید از سرور حذف می‌شوند؛ پشتیبان‌های قبلی تا پاک شدنشان می‌مانند و آن‌ها هم چیزی جز متن رمزشده ندارند.

۱۰. راستی‌آزمایی مستقل

  • ترافیک شبکه: با ابزارهایی مانند پراکسی HTTPS، درخواست‌های POST /desk/changes دیده می‌شوند: بدنهٔ هر رکورد فقط {"_e2e":1,"c":"…"} است.
  • اثر انگشت کلید: در تنظیمات اپ، اثر انگشت کلید (برگرفته از key_id) روی همهٔ دستگاه‌های یک حساب یکسان است.
  • رفتار سرور: پس از فعال‌سازی، ارسال رکورد بدون رمز با خطا رد می‌شود؛ این را با یک درخواست آزمایشی می‌توان دید.
  • بازبینی کد: کد بخش رمزنگاری اپ و سرور برای کارشناس امنیتی معرفی‌شده از سوی نهاد بررسی‌کننده، تحت توافق‌نامهٔ عدم افشا، قابل ارائه است.

۱۱. محدودیت‌ها و نقشهٔ راه

  • این طراحی هنوز توسط نهاد مستقل ممیزی نشده است و گواهی‌نامه‌ای مانند ISO 27001 ادعا نمی‌شود. ممیزی مستقل در نقشهٔ راه است.
  • فراداده (بخش ۶) بدون رمز است.
  • نسخهٔ وب از سرور بارگذاری می‌شود؛ اپ اندروید امضاشده سطح اطمینان بالاتری دارد.
  • حساب‌هایی که هنوز گاوصندوق نساخته‌اند، تا آن زمان فقط رمزنگاری سمت سرور دارند و همگام‌سازی آن‌ها متوقف است.

نسخهٔ سند: ۱ · تاریخ: ۲۰ مهر ۱۴۰۵ · تماس امنیتی: solicitor.saraee@gmail.com