SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-13

آیا Vaultwarden امن است؟ بررسی امنیت و روش‌های مقاوم‌سازی

Vaultwarden تمام داده‌ها را در سمت کلاینت رمزنگاری می‌کند. امنیت واقعی شما به محافظت از admin token، مدیریت صحیح فایل‌های پشتیبان و بستن پورت‌های غیرضروری وابسته است.

آیا Vaultwarden امن است؟ پاسخ کوتاه

Vaultwarden در تنها بخشی که اهمیت حیاتی دارد امن است، زیرا هر آیتم موجود در vault پیش از رسیدن به سرور، روی دستگاه شما رمزنگاری می‌شود. سرور تنها داده‌های رمزنگاری‌شده‌ای (blob) را ذخیره می‌کند که قادر به خواندن آن‌ها نیست. کسی که کل دیتابیس را کپی کند، همچنان برای استخراج هرگونه اطلاعات مفید، به master password نیاز دارد.

این پاسخ کوتاه، بار معنایی زیادی را به دوش می‌کشد و بخش‌هایی که ممکن است دچار نقص شوند، همان قسمت‌هایی هستند که شما پیکربندی می‌کنید. پنل مدیریتی که پشت یک توکن قابل حدس قرار دارد، پورت کانتینری که برای کل اینترنت باز شده است، یک config.json که به صورت متن ساده (plaintext) رها شده، یا یک فایل پشتیبان tarball که در دایرکتوری home روی همان سرور قرار دارد. هیچ‌کدام از این‌ها مشکلات رمزنگاری نیستند. تمام این موارد دلایلی هستند که باعث می‌شوند vaultهای self-hosted خالی شوند.

تمام موارد زیر فرض را بر این می‌گذارند که یک نصب فعال دارید. اگر هنوز نصبی ندارید، ابتدا آن را با استفاده از راهنمای نصب Vaultwarden برای VPS انجام دهید، سپس بازگردید و این فهرست را به ترتیب دنبال کنید.

سرور دقیقاً چه چیزی را ذخیره می‌کند

Vaultwarden مدل داده‌ای Bitwarden را پیاده‌سازی می‌کند. نام، نام کاربری، رمز عبور، یادداشت‌ها و URIهای یک آیتم در vault، پیش از ارسال هرگونه درخواست و در سمت کلاینت، با کلیدی که از رمز عبور اصلی (master password) شما مشتق شده، رمزنگاری می‌شوند. محتوای فایل‌های پیوست نیز به همین شیوه رمزنگاری می‌شوند. سرور داده‌های مبهمی را دریافت می‌کند که یک UUID (شناسه منحصربه‌فرد جهانی) به آن‌ها متصل است.

برخی موارد به صورت متن ساده (plaintext) هستند و باید دقیقاً بدانید کدام‌ها هستند:

  • آدرس ایمیل حساب کاربری شما، به صورت متن ساده.
  • تنظیمات KDF (تابع مشتق‌سازی کلید) و salt، زیرا کلاینت برای بازسازی کلید در ورود بعدی به آن‌ها نیاز دارد.
  • یک هش سمت سرور از هش رمز عبور اصلی که کلاینت ارسال می‌کند؛ این مورد برای احراز هویت خودِ ورود استفاده می‌شود.
  • متادیتا: عضویت در سازمان، نام دستگاه‌ها، زمان‌های آخرین ورود.
  • رمز (secret) روش احراز هویت دو مرحله‌ای که از ورود به Vaultwarden محافظت می‌کند. این مورد در جدول twofactor به صورت رمزنگاری‌نشده قرار دارد، زیرا سرور باید کد مورد انتظار را محاسبه کند تا با کد شما مقایسه نماید. این مورد با رمز TOTP (رمز یک‌بارمصرف زمانی) که داخل یک آیتم vault ذخیره می‌کنید متفاوت است؛ آن رمز مانند سایر فیلدها رمزنگاری می‌شود.

پوشه داده‌ها کوچک است. در نصب Docker، این پوشه همان مسیری است که در /data mount کرده‌اید.

sudo ls -l /vw-data/

db.sqlite3 تقریباً تمام وضعیت (state) را در خود نگه می‌دارد. attachments/ فایل‌های آپلود شده را به ازای هر UUID ذخیره می‌کند و تنها دسته مهم از داده‌هاست که در جداول دیتابیس قرار ندارد. sends/ پیوست‌های Send را نگه می‌دارد و ماهیت آن موقتی است. icon_cache/ قابل حذف است. rsa_key.pem و فایل‌های همراه آن، JWTهای (توکن‌های وب JSON) کاربران واردشده را امضا می‌کنند؛ بنابراین یک کپی از آن کلید خصوصی می‌تواند برای جعل نشست ورود به vault استفاده شود. config.json تنها زمانی ایجاد می‌شود که صفحه مدیریت (admin page) را فعال کنید و پروژه در مورد آن صریح است: این فایل توکن مدیریت و اعتبارنامه‌های SMTP شما را به صورت متن ساده نگه می‌دارد.

بنابراین، مدل تهدید عملی، دسترسی به سیستم فایل است، نه رمزنگاری شبکه. دسترسی خواندن به آن دایرکتوری، آدرس ایمیل تمام کاربران، رمزهای 2FA ورود آن‌ها، کلیدی برای جعل نشست‌ها و یک کپی آفلاین از تمام vaultها برای حمله در فرصت مناسب را در اختیار مهاجم قرار می‌دهد. تمام مراحل زیر برای دور نگه داشتن افراد از آن دایرکتوری است.

ابتدا توکن مدیریت را اصلاح کنید

/admin یک پنل کنترل کامل است: فهرست کاربران، دعوت‌نامه‌ها، حذف و تمامی تنظیمات زمان اجرا. این پنل تنها با یک رمز مشترک محافظت می‌شود و هیچ چیز دیگری ندارد. نه نام کاربری و نه احراز هویت دو مرحله‌ای برای هر کاربر.

راهنماهای قدیمی به شما می‌گویند که ADMIN_TOKEN را با openssl rand -base64 48 تولید کنید. این روش کار می‌کند و رمز را به‌صورت متن ساده در config.json و فایل compose شما می‌نویسد. Vaultwarden همچنین یک رشته Argon2 PHC (مسابقه هش رمز عبور) را می‌پذیرد، بنابراین مقدار ذخیره‌شده به‌جای متن ساده، یک هش خواهد بود. آن را با استفاده از یک کانتینر در حال اجرا تولید کنید:

docker exec -it vaultwarden /vaultwarden hash

یا بدون دست زدن به کانتینر در حال اجرا:

docker run --rm -it vaultwarden/server /vaultwarden hash

این دستور دو بار رمز عبور را می‌پرسد و سپس خطی را که با $argon2id$ شروع می‌شود چاپ می‌کند. در نصب bare-metal، دستور ./vaultwarden hash را اجرا کنید. اگر ترجیح می‌دهید مستقیماً از CLI مربوط به argon2 استفاده کنید، این پروژه حداقل پارامترهای OWASP را مستند کرده است:

echo -n 'MySecretPassword' | argon2 "$(openssl rand -base64 32)" -e -id -k 19456 -t 2 -p 1

حالا دامی که باعث اتلاف وقت کاربران می‌شود: یک رشته PHC پر از کاراکترهای $ است و Docker Compose کاراکتر $ را به عنوان جای‌گذاری متغیر (variable interpolation) در نظر می‌گیرد. اگر آن را بدون escape کردن در یک بلوک environment: قرار دهید، مقداری که به کانتینر می‌رسد تغییر می‌کند و در نتیجه /admin توکنی را که می‌دانید صحیح است، رد می‌کند. دو روش ایمن وجود دارد. در docker-compose.yml، هر $ را دو برابر کنید:

environment:
  ADMIN_TOKEN: $$argon2id$$v=19$$m=19456,t=2,p=1$$UUZxK1FZMkZoRHFQRlVrTXZvS0E3bHpNQW55c2dBN2NORzdsa0Nxd1JhND0$$cUoId+JBUsJutlG4rfDZayExfjq4TCt48aBc9qsc3UI

در یک فایل .env، نیازی به escape کردن نیست، اما از کوتیشن تکی استفاده کنید:

ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$MmeKRnGK5RW5mJS7h3TOL89GrpLPXJPAtTK8FTqj9HM$DqsstvoSAETl9YhnsXbf43WeaUwJC6JhViIvuPoig78'

سپس برای پنل محدودیت نرخ (rate limit) اعمال کنید و نشست آن را کوتاه‌تر کنید:

ADMIN_RATELIMIT_SECONDS=300
ADMIN_RATELIMIT_MAX_BURST=3
ADMIN_SESSION_LIFETIME=20

پس از سه تلاش ناموفق در عرض پنج دقیقه، پنل دیگر به آن کلاینت پاسخ نمی‌دهد. نشست مدیریت پس از 20 دقیقه عدم فعالیت منقضی می‌شود.

بهتر از همه این‌ها: صفحه را خاموش کنید. اکثر نمونه‌ها فقط یک‌بار به آن نیاز دارند، برای پیکربندی SMTP و دعوت اولین کاربران، و پس از آن دیگر نیازی نیست. برای غیرفعال کردن آن، نه ADMIN_TOKEN و نه DISABLE_ADMIN_TOKEN را تنظیم نکنید، هر کلید "admin_token" را از config.json حذف کنید و سپس کانتینر را دوباره ایجاد کنید. حذف کلید از فایل مهم است زیرا صفحه مدیریت تنظیمات را در آنجا می‌نویسد و آنچه در config.json است بر محیط (environment) اولویت دارد. حذف کردن متغیر به‌تنهایی باعث باز ماندن صفحه می‌شود.

بستن ثبت‌نام پیش از آنکه کسی دامنه شما را پیدا کند

SIGNUPS_ALLOWED=false
SIGNUPS_VERIFY=true
SHOW_PASSWORD_HINT=false

SIGNUPS_ALLOWED به‌صورت پیش‌فرض روی true تنظیم شده است. اگر آن را به همین حالت رها کنید، هر کسی که به دامنه شما برسد می‌تواند یک حساب کاربری بسازد و داده‌هایش در همان db.sqlite3 متعلق به شما ذخیره خواهد شد. آن را روی false تنظیم کنید و کاربران را از طریق دعوت‌نامه در صفحه مدیریت اضافه کنید؛ این کار مستلزم فعال بودن SMTP است. INVITATIONS_ALLOWED نیز به‌صورت پیش‌فرض true است و به مالکان سازمان اجازه می‌دهد دیگران را دعوت کنند. این حالت زمانی که به کاربران خود اعتماد دارید مناسب است، اما در یک نمونه تک‌کاربره باید false باشد. اگر فقط دامنه‌های خاصی مجاز به ثبت‌نام هستند، SIGNUPS_DOMAINS_WHITELIST=example.com محدودتر از ثبت‌نام آزاد است اما امنیت آن بسیار کمتر از استفاده از دعوت‌نامه است.

SHOW_PASSWORD_HINT به‌صورت پیش‌فرض false است و باید در همین حالت باقی بماند. با فعال بودن این گزینه، وارد کردن یک آدرس ایمیل معتبر در فرم ورود، راهنمای رمز عبور اصلی (master password hint) آن حساب را نمایش می‌دهد که هم باعث افشای راهنما می‌شود و هم وجود آن آدرس ایمیل را تأیید می‌کند.

اگر نمونه شما برای مدتی با ثبت‌نام آزاد اجرا شده است، پیش از آنکه فرض کنید تنها کاربر آن هستید، صفحه مدیریت را باز کرده و لیست کاربران را بررسی کنید.

پورتی که قصد انتشار آن را نداشتید

ایمیج Docker داخل کانتینر روی پورت 80 گوش می‌دهد. نصب روی bare-metal به‌صورت پیش‌فرض از ROCKET_PORT=8000 استفاده می‌کند. دستور اجرای مستندشده، آن را به این صورت منتشر می‌کند:

--publish 127.0.0.1:8000:80

پیشوند 127.0.0.1: تمام هدف این دستور است. اگر به‌جای آن -p 8000:80 بنویسید، Docker پورت را به 0.0.0.0 متصل می‌کند و این کار را با نوشتن قوانین DNAT (ترجمه آدرس شبکه مقصد) در جدول nat انجام می‌دهد. این قوانین پیش از زنجیره‌های filter که توسط ufw مدیریت می‌شوند ارزیابی می‌گردند؛ بنابراین ufw status وضعیت پورت را مسدود (denied) گزارش می‌دهد، در حالی که پورت همچنان به‌راحتی به اینترنت پاسخ می‌دهد. مطالعه مکانیزم کامل این موضوع در راهنمای دور زدن ufw توسط پورت‌های Docker ارزشمند است.

بررسی کنید چه چیزی واقعاً در حال گوش دادن است:

sudo ss -tlnp | grep 8000

یک نتیجه سالم، تنها یک خط متصل به 127.0.0.1:8000 است. خطی که به 0.0.0.0:8000 متصل باشد به این معنی است که vault مستقیماً در معرض دید قرار دارد. نگاشت (mapping) را اصلاح کنید و سپس کانتینر را دوباره بسازید، زیرا اتصال پورت هنگام ایجاد کانتینر تعیین می‌شود و docker compose restart آن را تغییر نخواهد داد:

docker compose up -d --force-recreate

یک پورت دیگر در راهنماهای قدیمی باقی مانده است: 3012، که پورت جداگانه WebSocket است. پشتیبانی از آن در Vaultwarden 1.31.0 حذف شد، زیرا ترافیک اعلان‌ها به پورت اصلی HTTP منتقل شده است. WEBSOCKET_ENABLED و WEBSOCKET_PORT از نسخه 1.29.0 نادیده گرفته می‌شوند. سوئیچ فعلی ENABLE_WEBSOCKET است که به‌صورت پیش‌فرض true می‌باشد. اگر فایروال یا فایل compose شما هنوز پورت 3012 را باز نگه داشته است، آن را ببندید.

پایان‌دهی TLS در reverse proxy، نه در Rocket

سرویس Vaultwarden می‌تواند TLS (امنیت لایه انتقال) را مستقیماً از طریق Rocket، که فریم‌ورک وب آن است، ارائه دهد؛ اما مستندات پروژه توصیه می‌کنند که در محیط عملیاتی (production) این کار را انجام ندهید. قابلیت TLS داخلی Rocket فاقد پشتیبانی دقیق از SNI (نشان‌گر نام سرور) است. به همین دلیل است که توصیه امنیتی اکید بر این است که همیشه از طریق نام دامنه (hostname) به نمونه خود متصل شوید و هرگز از آدرس IP خام استفاده نکنید. محدوده‌های IP عمومی دائماً اسکن می‌شوند و هر vault که به یک آدرس IP پاسخ دهد، به‌راحتی شناسایی می‌شود.

بخش‌های مهم در یک server block مربوط به nginx:

client_max_body_size 525M;

location / {
  proxy_pass http://127.0.0.1:8000;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection $connection_upgrade;
}

مقدار پیش‌فرض nginx برای client_max_body_size برابر با 1 MB است؛ بنابراین بدون این خط، آپلود فایل‌های پیوست با خطای 413 Request Entity Too Large در لاگ خطای nginx مواجه می‌شود، در حالی که Vaultwarden هیچ موردی را ثبت نمی‌کند. هدرهای Upgrade و Connection وظیفه انتقال handshake مربوط به WebSocket به /notifications/hub را بر عهده دارند. اگر آن‌ها را حذف کنید، vault همچنان کار می‌کند، اما تغییرات تا زمانی که صفحه را به‌صورت دستی رفرش نکنید، در سایر دستگاه‌های شما ظاهر نخواهند شد.

پیکربندی Caddy کوتاه‌تر است و گواهی را به‌صورت خودکار دریافت می‌کند:

vw.example.com {
  reverse_proxy 127.0.0.1:8000 {
    header_up X-Real-IP {remote_host}
  }
}

سپس این موضوع را به Vaultwarden اطلاع دهید:

DOMAIN=https://vw.example.com
IP_HEADER=X-Real-IP

مقدار IP_HEADER به‌صورت پیش‌فرض روی X-Real-IP تنظیم شده است، بنابراین وظیفه شما اطمینان از این است که پروکسی حتماً این هدر را تنظیم کند. اگر این کار انجام نشود، تمام خطوط لاگ و محدودیت‌های نرخ ورود (login rate limit) آدرس 127.0.0.1، یعنی خودِ پروکسی را مشاهده می‌کنند. این بدان معناست که شکست‌های یک مهاجم به پای تمام کاربران نمونه شما نوشته می‌شود. مقدار DOMAIN را نیز روی URL واقعی https تنظیم کنید، زیرا Vaultwarden لینک‌های دعوت و بازنشانی رمز عبور را بر اساس آن می‌سازد و کلیدهای امنیتی WebAuthn نیز به همان مبدأ (origin) وابسته هستند.

نکته‌ای که اغلب نادیده گرفته می‌شود: اتصال WebSocket توکن نشست (session token) را در query string و به عنوان /notifications/hub?access_token=[JWT] ارسال می‌کند. این مقدار به‌صورت متن آشکار در لاگ دسترسی پروکسی شما ثبت می‌شود. پارامتر access_token را در فرمت لاگ حذف (redact) کنید یا اطمینان حاصل کنید که این لاگ‌ها به هیچ مکانی که تحت کنترل شما نیست، ارسال نمی‌شوند.

مسدودسازی حملات brute force در endpoint ورود

محدودیت‌های نرخ (Rate limits) به‌صورت پیش‌فرض فعال هستند (LOGIN_RATELIMIT_SECONDS=60، LOGIN_RATELIMIT_MAX_BURST=10). این محدودیت‌ها سرعت مهاجم را کاهش می‌دهند، اما جلوی او را نمی‌گیرند. ابزار fail2ban این کار را انجام می‌دهد، اما Vaultwarden باید ابتدا یک فایل لاگ بنویسد که در حالت پیش‌فرض این کار را انجام نمی‌دهد:

LOG_FILE=/data/vaultwarden.log
LOG_LEVEL=info
EXTENDED_LOGGING=true

یک ورود ناموفق دقیقاً یک خط تولید می‌کند و این همان رشته‌ای است که فیلتر شما باید با آن مطابقت داشته باشد:

[2026-08-07 09:14:22][vaultwarden::api::identity][ERROR] Username or password is incorrect. Try again. IP: 203.0.113.10. Username: user@example.com.

فیلتر را در /etc/fail2ban/filter.d/vaultwarden.local بنویسید:

[INCLUDES]
before = common.conf

[Definition]
failregex = ^.*?Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =

و jail را در /etc/fail2ban/jail.d/vaultwarden.local تنظیم کنید:

[vaultwarden]
enabled = true
port = 80,443
filter = vaultwarden
banaction = %(banaction_allports)s
logpath = /vw-data/vaultwarden.log
maxretry = 3
bantime = 14400
findtime = 14400

اگر صفحه مدیریت را نگه داشته‌اید، یک jail دوم اضافه کنید که failregex آن ^.*Invalid admin token\. IP: <ADDR>.*$ باشد، زیرا خطاهای ورود به بخش مدیریت با پیام متفاوتی ثبت می‌شوند و فیلتر ورود هرگز آن‌ها را مشاهده نخواهد کرد. سپس عملکرد خود را بررسی کنید:

sudo systemctl restart fail2ban
sudo fail2ban-client status vaultwarden

یک jail فعال، فایل لاگ شما را در زیر File list فهرست می‌کند و Currently failed: 0 را گزارش می‌دهد. سه بار رمز عبور اشتباه را از یک شبکه متفاوت وارد کنید تا شمارنده افزایش یابد، سپس آدرس در زیر Banned IP list ظاهر می‌شود. اگر شمارنده هرگز تغییر نمی‌کند، علت معمول logpath است: این مسیر باید مسیر فایل روی میزبان (host) باشد، نه مسیر /data/... داخل کانتینر. دومین علت معمول، نبود X-Real-IP است که باعث می‌شود هر مسدودسازی، پروکسی شما را هدف قرار دهد. باقی تنظیمات، از جمله jail مربوط به SSH که باید از قبل در حال اجرا باشد، در راهنمای fail2ban برای Ubuntu 24.04 موجود است.

رمز عبور اصلی همچنان کل سیستم است

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

PASSWORD_ITERATIONS=600000 تعداد تکرارهای KDF است که هنگام ایجاد حساب کاربری جدید به کلاینت‌ها ارائه می‌شود. حساب‌های موجود، مقداری را که با آن ایجاد شده‌اند حفظ می‌کنند؛ بنابراین افزایش این مقدار برای کاربرانی که سال گذشته ثبت‌نام کرده‌اند، تغییری ایجاد نمی‌کند. آن‌ها باید خودشان این مقدار را در تنظیمات امنیتی web vault تغییر دهند که باعث رمزنگاری مجدد کلیدشان می‌شود. این موضوع را به آن‌ها اطلاع دهید، زیرا هیچ بخشی در رابط کاربری این کار را انجام نمی‌دهد.

سپس احراز هویت دو مرحله‌ای را برای هر حساب فعال کنید. این کار از متن رمزنگاری‌شده محافظت نمی‌کند، زیرا کلید vault تنها از رمز عبور اصلی به دست می‌آید. اما این کار باعث می‌شود که سرقت رمز عبور به تنهایی برای ورود و همگام‌سازی یک نسخه کافی نباشد. REQUIRE_DEVICE_EMAIL=true یک مرحله تأیید ایمیل را برای اولین باری که یک حساب از دستگاهی ناشناس وارد می‌شود، اضافه می‌کند.

پشتیبان‌گیری؛ جایی که مخازن self-hosted با شکست مواجه می‌شوند

یک tar czf از پوشه داده‌ها که در دایرکتوری home روی همان VPS باقی بماند، تمام مراحل قبلی را بی‌اثر می‌کند. آن آرشیو حاوی db.sqlite3 با متن رمزنگاری‌شده (ciphertext) تمام کاربران، rsa_key.pem که نشست‌های ورود را جعل می‌کند، و config.json با توکن مدیر و رمز عبور SMTP به صورت متن ساده (plaintext) است. دسترسی خواندن به آن یک فایل، به معنای دسترسی خواندن به کل مخزن است.

دو قانون این موضوع را پوشش می‌دهند. آرشیو را از سرور خارج کنید. پیش از خروج، آن را رمزنگاری کنید.

همچنین یک مشکل در صحت داده‌ها وجود دارد. کپی کردن db.sqlite3 با cp در حالی که سرویس در حال اجرا است، می‌تواند فایلی ایجاد کند که در میانه نوشتن بوده و باز نخواهد شد؛ و شما تا زمان بازیابی (restore) متوجه این موضوع نخواهید شد. به جای آن از قابلیت snapshot خود SQLite استفاده کنید:

sqlite3 /vw-data/db.sqlite3 ".backup '/tmp/vw-db-backup.sqlite3'"

بخش بازیابی، که نیمه‌ای است که هیچ‌کس آن را تست نمی‌کند، در راهنمای پشتیبان‌گیری و بازیابی Vaultwarden پوشش داده شده است.

آنچه در مقایسه با سرویس میزبانی‌شده Bitwarden از دست می‌دهید

حساب‌وکتاب صادقانه. سرویس میزبانی‌شده Bitwarden توسط افرادی اداره می‌شود که شغل تمام‌وقتشان مدیریت آن است؛ این سرویس دارای ممیزی‌های شخص ثالث منتشرشده است و در ساعت 3 بامداد نیز فردی برای رسیدگی به مشکلات در دسترس است. در حالت self-hosting، شما این مزایا را با برنامه زمانی به‌روزرسانی خودتان جایگزین می‌کنید.

Vaultwarden اصلاحات امنیتی را در قالب نسخه‌های معمولی (releases) ارائه می‌دهد. نسخه 1.37.0 که در تاریخ 24 ژوئیه 2026 منتشر شد، تا اوت 2026 نسخه جاری محسوب می‌شود و یادداشت‌های آن از کاربران می‌خواهد که در اسرع وقت آن را به‌روزرسانی کنند. نمونه‌ای که یک سال پیش راه‌اندازی کرده و فراموش کرده‌اید، در حال اجرای کدی است که یک سال از عمر آن می‌گذرد. تگ latest به‌تنهایی کمکی نمی‌کند: کانتینری که در حال اجراست، همان ایمیجی را نگه می‌دارد که با آن شروع شده است، مگر اینکه دستور docker compose pull را اجرا کرده و آن را دوباره ایجاد کنید. برای بسته‌های میزبان، ارتقاهای خودکار در Ubuntu را فعال کنید و به‌روزرسانی کانتینر را در یادآور تقویمی قرار دهید که واقعاً آن را می‌خوانید.

نتیجه‌گیری صادقانه‌ای که یک خواننده باید بگیرد این است: رمزنگاری در اینجا بر اساس طراحی Bitwarden است و کاملاً قابل‌اتکا است، اما ریسک عملیاتی به‌طور کامل به شما منتقل می‌شود. اگر آن را به‌روز نگه دارید و در جای دیگری از آن نسخه پشتیبان تهیه کنید، یک نمونه Vaultwarden روی یک VPS که کنترل آن را در دست دارید، مکان مناسبی برای نگهداری رمزهای عبور شماست. اگر قرار نیست این دو عادت را در خود ایجاد کنید، هزینه سرویس میزبانی‌شده را بپردازید و توجه خود را صرف کارهای دیگر کنید. مقایسه ویژگی‌به‌ویژگی در مقایسه Vaultwarden با Bitwarden خودمیزبان موجود است.

ایمن‌سازی میزبان زیرین کانتینر

Vaultwarden تنها یک پردازش روی سیستم‌عامل Linux است و کاربر root در آن سیستم، صرف‌نظر از پیکربندی برنامه، می‌تواند /vw-data را بخواند. کانتینر را با استفاده از user: "1000:1000" در فایل compose خود با یک کاربر فاقد دسترسی‌های ویژه اجرا کنید، مالکیت پوشه داده‌ها را با آن کاربر هماهنگ سازید و هر فایلی را که کانتینر در آن نمی‌نویسد، با استفاده از :ro به‌صورت فقط‌خواندنی (read-only) mount کنید. سپس در ورودی اصلی را ببندید: ایمن‌سازی SSH روی VPS به موضوع ورود فقط با کلید و غیرفعال‌سازی احراز هویت با رمز عبور می‌پردازد؛ این همان اقدامی است که حملات معمول و ساده‌ای را که از تمام لایه‌های امنیتی فوق عبور می‌کنند، متوقف می‌سازد.

FAQ

آیا اگر کسی دیتابیس Vaultwarden را سرقت کند، می‌تواند رمزهای عبور مرا بخواند؟

نه مستقیماً. هر آیتم موجود در vault در سمت کلاینت با کلیدی که از رمز عبور اصلی (master password) مشتق شده، رمزنگاری می‌شود؛ بنابراین db.sqlite3 فقط حاوی متن رمزنگاری‌شده (ciphertext) است. آنچه مهاجم بلافاصله به دست می‌آورد، آدرس ایمیل هر حساب، تنظیمات KDF، متادیتای ورود و دستگاه، و همچنین کدهای مخفی احراز هویت دو مرحله‌ای در جدول twofactor است که به‌صورت رمزنگاری‌نشده ذخیره می‌شوند، زیرا سرور باید کد مورد انتظار را محاسبه کند. آن‌ها همچنین می‌توانند متن رمزنگاری‌شدهٔ vault را به‌صورت آفلاین و برای هر مدت زمانی که بخواهند مورد حمله قرار دهند؛ به همین دلیل است که طول رمز عبور اصلی، عاملی است که نتیجه را تعیین می‌کند.

آیا باید از ADMIN_TOKEN استفاده کنم یا صفحه مدیریت را کاملاً غیرفعال کنم؟

اگر می‌توانید آن را غیرفعال کنید، چرا که اکثر نمونه‌ها (instances) فقط یک‌بار برای پیکربندی SMTP و دعوت از کاربران به آن نیاز دارند و پس از آن دیگر نیازی به آن نیست. برای غیرفعال کردن آن، نه ADMIN_TOKEN و نه DISABLE_ADMIN_TOKEN را تنظیم نکنید، هر کلید "admin_token" را از config.json حذف کنید و سپس container را دوباره بسازید. حذف کردن صرفِ متغیر محیطی (environment variable) کافی نیست، زیرا تنظیماتی که توسط صفحه مدیریت نوشته شده‌اند در config.json قرار دارند و اولویت بالاتری دارند. اگر صفحه را نگه می‌دارید، توکن را به‌جای یک رشته تصادفی ساده، به‌صورت هش Argon2 تولیدشده توسط vaultwarden hash ذخیره کنید و ADMIN_RATELIMIT_MAX_BURST=3 را تنظیم نمایید.

توکن ADMIN_TOKEN من درست است اما مسیر /admin آن را رد می‌کند. مشکل چیست؟

تقریباً همیشه مشکل از جای‌گذاری (interpolation) در $ است. یک رشته Argon2 PHC حاوی چندین کاراکتر $ است و Docker Compose آن‌ها را به‌عنوان متغیر در داخل بلوک docker-compose.yml environment: بسط می‌دهد؛ بنابراین container یک مقدار ناقص دریافت می‌کند، در حالی که فایل شما درست به نظر می‌رسد. در فایل compose، هر $ را به $$ تبدیل کنید (دوبرابر کنید)، یا مقدار را به یک فایل .env منتقل کرده و داخل تک‌کوتیشن قرار دهید که در آن نیازی به escape کردن نیست. پس از آن container را دوباره بسازید، زیرا تغییرات محیطی با یک restart ساده اعمال نمی‌شوند.

آیا هنوز برای اعلان‌ها (notifications) نیاز به باز کردن پورت 3012 دارم؟

خیر. پشتیبانی از ترافیک WebSocket روی پورت 3012 در Vaultwarden نسخه 1.31.0 حذف شد، زیرا اعلان‌ها به پورت اصلی HTTP منتقل شدند و WEBSOCKET_ENABLED و WEBSOCKET_PORT از نسخه 1.29.0 نادیده گرفته می‌شوند. تنظیم فعلی ENABLE_WEBSOCKET است که به‌صورت پیش‌فرض true می‌باشد. پورت 3012 را در فایروال ببندید و آن را از فایل compose خود حذف کنید؛ سپس مطمئن شوید که reverse proxy شما هدرهای Upgrade و Connection را فوروارد می‌کند، زیرا همگام‌سازی بلادرنگ (real-time sync) اکنون دقیقاً به همین موارد وابسته است.