🔐 امضای دیجیتال؛ وقتی یک قابلیت فنی میتواند به ریسک محصول تبدیل شود
در جریان پیادهسازی API مربوط به Digital Signature یکی از سرویسدهندهها، به نکتهای برخوردم که واقعاً برایم عجیب و البته نگرانکننده بود.
سرویس، Digital Certificate را برای کاربر صادر میکرد و فرآیند دریافت امضا هم انجام میشد؛ اما یک ارتباط مهم در این فرآیند وجود نداشت:
❌ ارتباط مشخص بین Certificate / Signature و Documentای که قرار است امضا شود.
یعنی در فرآیند دریافت امضا، عملاً مشخص نمیکردیم:
«این امضا دقیقاً برای کدام سند صادر میشود؟»
و این سؤال ساده، از دید Security و حتی Product بسیار مهم است.
🧩 مسئله دقیقاً چیست؟
فرض کنید کاربر برای امضای یک قرارداد خاص وارد سیستم شده است.
مثلاً: 📄 Contract #1024
کاربر تصور میکند قرار است همین قرارداد را امضا کند.
اما اگر فرآیند صدور امضا هیچ ارتباطی با Document نداشته باشد، ممکن است همان Certificate یا Signature در شرایط دیگری برای یک Document متفاوت مورد استفاده قرار بگیرد.
در این حالت سیستم ممکن است از نظر فنی بگوید:
✅ ءCertificate معتبر است
✅ ءSignature معتبر است
✅ امضا متعلق به این User است
اما یک سؤال مهم همچنان بیپاسخ میماند:
❓ آیا این کاربر واقعاً همین Document را برای امضا تأیید کرده است؟
و این دو مفهوم کاملاً متفاوت هستند.
⚠️ مشکل فقط Technical نیست
اینجا دقیقاً همان جایی است که باید از زاویه Product + Security به سیستم نگاه کنیم.
فرض کنید در یک سیستم مالی، دولتی یا حقوقی، کاربر به تصور اینکه دارد سند A را امضا میکند، فرآیند را تأیید میکند.
اما سیستم در لایه دیگری امکان استفاده از همان اعتبار برای سند B را فراهم میکند.
حتی اگر تمام APIها درست کار کنند و هیچ Exception یا خطای فنی وجود نداشته باشد، رفتار محصول میتواند مشکلدار باشد.
چون: Technical Validity ≠ User Intent
اینکه یک Signature از نظر Cryptography معتبر باشد، الزاماً به این معنی نیست که کاربر قصد داشته همان عملیات خاص را تأیید کند.
🔍 اینجا باید یک سؤال مهمتر بپرسیم
وقتی کاربر روی
Sign کلیک میکند، دقیقاً چه چیزی را تأیید کرده است؟آیا فقط هویت خودش را تأیید کرده؟
یا مشخصاً تأیید کرده که:
«من، User X، این Document مشخص با شناسه Y و نسخه Z را در این لحظه برای امضا تأیید میکنم.»
این تفاوت کوچک در طراحی، میتواند از نظر امنیتی بسیار مهم باشد.
🛡 راهکاری که در طراحی خودمان در نظر گرفتیم
برای اینکه حداقل در سمت خودمان، User Consent و آگاهی کاربر نسبت به سندی که قرار است امضا کند واضحتر باشد، یک مرحله OTP به فرآیند اضافه کردیم. Flow به این شکل شد:
User
│
▼
Select Document
│
▼
Show Document Details
│
▼
Request Signature
│
▼
Generate OTP
│
▼
Send OTP to User
│
▼
Verify OTP
│
▼
Create Signature Request
│
▼
Sign Specific Document
اینجا OTP صرفاً یک Authentication Factor نیست.
در طراحی محصول ما، بخشی از فرآیند Explicit User Consent هم محسوب میشود.
یعنی قبل از اینکه عملیات حساس انجام شود، کاربر باید دوباره در همان Flow تأیید کند که قصد ادامه فرآیند را دارد.
🔗 اما فقط OTP کافی نیست
یک نکته مهم این است که اضافه کردن OTP بهتنهایی نباید به ما حس امنیت کاذب بدهد.
اگر بخواهیم این سیستم را اصولیتر طراحی کنیم، باید Signature Request را به یک Document مشخص Bind کنیم.
مثلاً:
SignatureRequest
----------------
Id
UserId
DocumentId
DocumentVersion
DocumentHash
CreatedAt
ExpiresAt
Status
در این حالت بهجای اینکه صرفاً بگوییم:
User X signed something
میتوانیم بگوییم:
User X
approved
Document Y
Version Z
with Hash H
و این دقیقاً همان چیزی است که در یک سیستم حساس ارزش دارد:
🎯 Binding the user's intent to a specific artifact
🔐 حتی Document Hash هم میتواند مهم باشد
فرض کنید کاربر Document را در لحظه
T1 مشاهده کرده است.بعد از آن، Document تغییر میکند.
اگر Signature فقط به
DocumentId وابسته باشد، ممکن است این سؤال ایجاد شود:کاربر دقیقاً کدام نسخه از Document را امضا کرده است؟
به همین دلیل در سیستمهای حساس، نگهداشتن چیزی مثل:
DocumentId
DocumentVersion
DocumentHash
میتواند بسیار مهم باشد.
در این صورت میتوانیم مشخص کنیم Signature مربوط به همان محتوایی بوده که کاربر تأیید کرده است، نه صرفاً یک رکورد با یک شناسه.