هل Vaultwarden آمن؟ خطوات تقوية الخادم
يشفّر Vaultwarden محتوى الخزنة على جهازك، لكنه يخزّن بيانات الحساب بنص واضح. تعرّف إلى مخاطر رمز الإدارة وملف النسخ الاحتياطي وكيفية تقويتهما.
هل Vaultwarden آمن؟ الإجابة المختصرة
يكون Vaultwarden آمناً في أهم موضع، لأن كل عنصر في الخزنة يُشفَّر على جهازك قبل وصوله إلى الخادم. يخزّن الخادم محتويات الخزنة المشفَّرة إلى جانب بيانات الحساب الوصفية بنص واضح. وحتى من ينسخ قاعدة البيانات بالكامل، يظل بحاجة إلى مفاتيح تشفير الخزنة لقراءة محتوياتها، لكنه يحصل فوراً على عناوين البريد الإلكتروني للحسابات وتلميحات كلمات المرور.
تتطلب هذه الإجابة عدة شروط، ونقاط الضعف فيها هي ما تهيّئه بنفسك. لوحة إدارة محمية برمز يمكن تخمينه. منفذ حاوية منشور على الإنترنت بالكامل. config.json بنص واضح. أرشيف tar احتياطي موجود في مجلد المنزل على الخادم نفسه. لا تتعلق أي من هذه الحالات بمشكلات التشفير. لكنها جميعاً أسباب تفريغ الخزائن المستضافة ذاتياً.
يفترض كل ما يلي أن التثبيت يعمل. إذا لم يكن لديك تثبيت بعد، فأعد إعداده أولاً باستخدام دليل تثبيت Vaultwarden على VPS، ثم ارجع ونفّذ عناصر هذه القائمة بالترتيب.
ما يخزّنه الخادم فعلياً
يطبّق Vaultwarden نموذج بيانات Bitwarden. يُشفَّر اسم عنصر الخزنة واسم المستخدم وكلمة المرور والملاحظات وعناوين URI على العميل باستخدام مفتاح عنصر يُولَّد عشوائياً، قبل إرسال أي طلب. يحمي مفتاح المستخدم أو المؤسسة المتماثل ذلك المفتاح، بينما يحمي المفتاح المشتق من كلمة المرور المفتاح المتماثل للمستخدم. تستخدم محتويات ملفات المرفقات مفتاح مرفق منفصلاً يحميه مفتاح العنصر. يستقبل الخادم بيانات مبهمة مرفقة بمعرّف UUID (معرّف فريد عالمياً).
بعض البيانات ليست نصاً مشفراً، ويجب أن تعرفها بدقة:
- عنوان البريد الإلكتروني لحسابك وتلميح كلمة المرور الرئيسية، بنص واضح.
- إعدادات KDF (اشتقاق المفتاح) وsalt، لأن العميل يحتاج إليها لإعادة بناء المفتاح عند تسجيل الدخول التالي.
- تجزئة على الخادم لتجزئة كلمة المرور الرئيسية التي يرسلها العميل، وتُستخدم لمصادقة تسجيل الدخول نفسه.
- البيانات الوصفية: عضوية المؤسسة، وأسماء الأجهزة، وأوقات تسجيل الدخول الأخيرة.
- سر TOTP لتسجيل الدخول بالمصادقة الثنائية المستندة إلى تطبيق المصادقة. يُخزَّن في جدول
twofactorدون تشفير، لأن الخادم يجب أن يحسب الرمز المتوقع لمقارنته بالرمز الذي تقدمه. وهذا يختلف عن سر TOTP (كلمة مرور لمرة واحدة معتمدة على الوقت) الذي تخزّنه داخل عنصر خزنة، إذ يُشفَّر مثل أي حقل آخر.
مجلد البيانات صغير. في تثبيت Docker، يكون هذا المجلد هو المسار الذي ربطته بـ/data.
sudo ls -l /vw-data/يحتوي db.sqlite3 على معظم الحالة. ويحتوي attachments/ على الملفات المرفوعة داخل مجلدات تحمل معرّفات UUID، خارج جداول قاعدة البيانات. ويحتوي sends/ على مرفقات Send، وهو مخصص للبيانات المؤقتة. أما icon_cache/ فهو قابل للحذف. يوقّع rsa_key.pem والملفات المرافقة له JWTs (رموز الويب JSON) الخاصة بالمستخدمين الذين سجّلوا الدخول، ولذلك يمكن استخدام نسخة من ذلك المفتاح الخاص لتزوير جلسة تسجيل دخول إلى الخزنة. يخزّن config.json الإعدادات المحفوظة عبر صفحة الإدارة، بما في ذلك بيانات اعتماد SMTP بنص واضح وقيمة رمز الإدارة المُعدّة. يؤدي اتباع تعليمات Argon2 أدناه إلى تخزين تجزئة رمز الإدارة بدلاً من كلمة المرور بنص واضح.
لذلك، يتمثل نموذج التهديد العملي في الوصول إلى نظام الملفات، لا في تشفير الشبكة. يمنح الوصول للقراءة إلى ذلك المجلد وحده المهاجم عناوين البريد الإلكتروني لجميع المستخدمين، وأسرار 2FA الخاصة بتسجيل الدخول، ومفتاحاً لتزوير الجلسات، ونسخة غير متصلة من كل خزنة لمهاجمتها دون قيود زمنية. وتهدف كل خطوة أدناه إلى منع الوصول إلى ذلك المجلد.
صحّح رمز admin أولاً
/admin عبارة عن لوحة تحكم كاملة: قائمة المستخدمين، والدعوات، والحذف، وكل إعدادات التشغيل. لا يحميها سوى سر مشترك واحد. لا يوجد اسم مستخدم. ولا توجد مصادقة ثنائية لكل مستخدم.
تطلب منك الأدلة الأقدم إنشاء ADMIN_TOKEN باستخدام openssl rand -base64 48. ينجح ذلك، لكنه يترك السر بنص واضح في كل موضع تضبطه فيه. يقبل Vaultwarden أيضاً سلسلة Argon2 PHC (مسابقة تجزئة كلمات المرور)، لذلك تصبح القيمة المخزنة تجزئة بدلاً من السر نفسه. أنشئ واحدة مقابل حاوية قيد التشغيل:
docker exec -it vaultwarden /vaultwarden hashأو من دون لمس الحاوية قيد التشغيل مطلقاً:
docker run --rm -it vaultwarden/server /vaultwarden hashسيطلب كلمة المرور مرتين، ثم يطبع سطراً يبدأ بـ $argon2id$. في تثبيت bare-metal، شغّل ./vaultwarden hash. وإذا كنت تفضّل استخدام واجهة argon2 مباشرة، فإن المشروع يوثّق الحد الأدنى من المعلمات وفق OWASP:
echo -n 'MySecretPassword' | argon2 "$(openssl rand -base64 32)" -e -id -k 19456 -t 2 -p 1إليك المشكلة التي تكلّف الناس ساعة من العمل. تحتوي سلسلة PHC على كثير من محارف $، ويتعامل Docker Compose مع $ باعتبارها استبدالاً للمتغيرات. إذا لصقتها من دون تهريب داخل كتلة environment:، فستصل القيمة إلى الحاوية بعد تغييرها، ولذلك يرفض /admin رمزاً تعرف أنه صحيح. هناك صيغتان آمنتان. في docker-compose.yml، ضاعف كل $:
environment:
ADMIN_TOKEN: $$argon2id$$v=19$$m=19456,t=2,p=1$$UUZxK1FZMkZoRHFQRlVrTXZvS0E3bHpNQW55c2dBN2NORzdsa0Nxd1JhND0$$cUoId+JBUsJutlG4rfDZayExfjq4TCt48aBc9qsc3UIفي ملف .env، لا حاجة إلى التهريب، لكن استخدم علامات اقتباس مفردة:
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$MmeKRnGK5RW5mJS7h3TOL89GrpLPXJPAtTK8FTqj9HM$DqsstvoSAETl9YhnsXbf43WeaUwJC6JhViIvuPoig78'ثم حدّد معدل طلبات اللوحة وقلّص مدة جلستها:
ADMIN_RATELIMIT_SECONDS=300
ADMIN_RATELIMIT_MAX_BURST=3
ADMIN_SESSION_LIFETIME=20يسمح ذلك بدفعة من ثلاث محاولات تسجيل دخول إلى لوحة الإدارة لكل عنوان IP للعميل، ثم يعيد السماح بمعدل طلب واحد كل خمس دقائق. تُحتسب كلمات المرور الصحيحة وغير الصحيحة معاً، لأن محدِّد المعدل يعمل قبل التحقق من كلمة المرور. تتلقى الطلبات التي تتجاوز الحد استجابة HTTP 429 مع Too many requests, try again later.. تنتهي جلسة الإدارة بعد 20 دقيقة من تسجيل الدخول، سواء واصلت استخدام اللوحة أم لا، لأن النشاط لا يمدد مدتها.
والأفضل من كل ذلك: عطّل الصفحة. تحتاج معظم النسخ إليها مرة واحدة لإعداد SMTP ودعوة المستخدمين الأوائل، ثم لا تحتاج إليها مجدداً. لتعطيلها، لا تضبط ADMIN_TOKEN ولا DISABLE_ADMIN_TOKEN، وأزل أي مفتاح "admin_token" من config.json، ثم أعد إنشاء الحاوية. حذف المفتاح من الملف مهم لأن صفحة الإدارة تكتب الإعدادات فيه، وما يوجد في config.json تكون له الأولوية على متغيرات البيئة. إزالة المتغير وحدها تترك الصفحة مفتوحة.
أغلِق التسجيل قبل أن يعثر أي شخص على النطاق
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 افتراضياً، وينبغي أن تبقى كذلك. عند تفعيلها وعدم إعداد SMTP، تؤدي كتابة عنوان بريد إلكتروني صالح في نموذج تسجيل الدخول إلى عرض تلميح كلمة المرور الرئيسية للحساب، ما يؤدي إلى تسريب التلميح وتأكيد وجود العنوان.
إذا كان مثيلك يعمل مع فتح التسجيل لأي مدة، فافتح صفحة الإدارة واقرأ قائمة المستخدمين قبل افتراض أنك الحساب الوحيد فيه.
المنفذ الذي لم تقصد نشره
تستمع صورة Docker على المنفذ 80 داخل الحاوية. ويستخدم التثبيت مباشرة على الخادم القيمة الافتراضية 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 المنفذ على أنه محظور، بينما يستجيب المنفذ فعلياً للإنترنت. يجدر بك قراءة الآلية كاملة في الدليل الخاص بتجاوز منافذ Docker لجدار ufw.
تحقق مما يستمع فعلياً:
sudo ss -tlnp | grep 8000تكون النتيجة السليمة سطراً واحداً مرتبطاً بـ 127.0.0.1:8000. أما السطر المرتبط بـ 0.0.0.0:8000 فيعني أن vault مكشوف مباشرة. أصلح الربط، ثم أعد إنشاء الحاوية، لأن ربط المنفذ يُحدَّد عند إنشاء الحاوية، و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، وهو إطار الويب الخاص به، لكن المشروع ينصح بعدم فعل ذلك في بيئة الإنتاج. لا يدعم TLS المدمج في Rocket ميزة SNI (إشارة اسم الخادم) الصارمة، ولهذا تنص إرشادات التقوية أيضاً على الوصول إلى مثيلك باستخدام اسم مضيف، وعدم استخدام عنوان IP مجرداً على الإطلاق. تُفحص نطاقات IP العامة باستمرار، والخزنة التي تستجيب على عنوان IP هي خزنة يسهل العثور عليها.
الأجزاء المهمة من كتلة الخادم في 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 إلى 1m، ولذلك لا تفشل إلا أجسام الطلبات الأكبر من هذا الحد من دون تجاوز للإعداد. يعيد nginx إلى العميل استجابة HTTP 413 ويسجل client intended to send too large body: ... bytes؛ ولا يصل التحميل المرفوض إلى Vaultwarden. ينقل رأسا Upgrade وConnection مصافحة WebSocket إلى /notifications/hub. إذا حُذفا، فستظل الخزنة تعمل، لكن إشعارات WebSocket لن تصل إلى عملاء المتصفح وسطح المكتب؛ أما عملاء الهاتف المحمول فيستخدمون إشعارات دفع مُعدّة بشكل منفصل.
يكون إعداد 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 بالفعل، لذا تتمثل المهمة في التأكد من أن الـproxy يضبط هذا الرأس فعلياً. لا يقبل Vaultwarden هذا الرأس إلا من العناوين المدرجة في IP_HEADER_TRUSTED_PROXIES، وقيمته الافتراضية هي local: أي عنوان غير عام، ويشمل ذلك proxy يعمل على المضيف نفسه أو على شبكة الحاويات نفسها. يجب إضافة proxy الذي يتصل من عنوان عام إلى هذه القائمة. إذا كان الرأس مفقوداً أو جاء من عنوان غير موثوق، فستستخدم كل سطور السجل وكل حدود معدل تسجيل الدخول عنوان الـproxy نفسه، وهذا يعني أن إخفاقات مهاجم واحد تُحتسب على جميع المستخدمين في المثيل. إذا استخدمت IP_HEADER=X-Forwarded-For بدلاً من ذلك، فسيأخذ الإصدار 1.37.4 العنوان الموجود في أقصى اليمين والذي ليس proxy موثوقاً، لذلك أدرج كل proxy في السلسلة، بما في ذلك CDN الموجود أمام nginx. اضبط DOMAIN على عنوان https الحقيقي أيضاً، لأن Vaultwarden ينشئ منه روابط الدعوات واسترداد الحساب، كما ترتبط مفاتيح أمان WebAuthn بذلك الأصل.
هناك تفصيل يغفله بعض المستخدمين: يمرر اتصال WebSocket رمز الجلسة في سلسلة الاستعلام، على النحو التالي: /notifications/hub?access_token=[JWT]. ويظهر ذلك نصاً واضحاً في سجل وصول الـproxy. احجب المعامل access_token في تنسيق السجل، أو تأكد من عدم إرسال هذه السجلات إلى أي جهة لا تسيطر عليها.
حظر هجمات القوة الغاشمة على نقطة تسجيل الدخول
تكون حدود المعدل مفعّلة افتراضياً (LOGIN_RATELIMIT_SECONDS=60، LOGIN_RATELIMIT_MAX_BURST=10). وهي تبطئ المهاجم، لكنها لا توقفه. يفعل fail2ban ذلك، لكنه يحتاج إلى سجلات محاولات تسجيل الدخول الفاشلة في Vaultwarden في مكان يمكنه قراءتها منه. في Docker، الخيار الأبسط هو استخدام ملف سجل، ولا ينشئ Vaultwarden هذا الملف افتراضياً:
LOG_FILE=/data/vaultwarden.log
LOG_LEVEL=info
EXTENDED_LOGGING=trueتنتج محاولة تسجيل دخول بكلمة مرور خاطئة سطراً بهذا الشكل، وهذه هي السلسلة النصية التي يجب أن يطابقها المرشح:
[2026-08-07 09:14:22.000][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
backend = pyinotify
port = 80,443
filter = vaultwarden
banaction = %(banaction_allports)s
logpath = /vw-data/vaultwarden.log
maxretry = 3
bantime = 14400
findtime = 14400إذا كان Vaultwarden يعمل كخدمة systemd بدلاً من حاوية، يمكنك تخطي LOG_FILE: إذ يقرأ fail2ban في ويكي المشروع السجل عبر backend = systemd، ويستخدم journalmatch على وحدة Vaultwarden بدلاً من logpath.
إذا أبقيت صفحة الإدارة، فأضف jail ثانية تكون قيمة failregex فيها هي ^.*Invalid admin token\. IP: <ADDR>.*$، لأن محاولات الإدارة الفاشلة تُسجَّل برسالة مختلفة، ولن يرى مرشح تسجيل الدخول هذه المحاولات مطلقاً. ثم تحقّق من إعدادك:
sudo systemctl restart fail2ban
sudo fail2ban-client status vaultwardenتعرض jail العاملة ملف السجل ضمن File list، وتبلغ عن Currently failed: 0. أدخل كلمة مرور خاطئة 3 مرات من شبكة مختلفة، فيرتفع ذلك العداد، ثم يظهر العنوان ضمن Banned IP list. إذا لم يتحرك العداد مطلقاً، فالسبب المعتاد هو logpath: يجب أن يكون مسار الملف على المضيف، لا مسار /data/... داخل الحاوية. والسبب المعتاد الثاني هو وجود X-Real-IP مفقود أو غير موثوق، ما يجعل كل حظر يستهدف وكيلك العكسي نفسه. يرد باقي الإعداد، بما في ذلك jail الخاصة بـSSH التي يفترض أن تكون قيد التشغيل لديك، في دليل fail2ban الخاص بـUbuntu 24.04.
كلمة المرور الرئيسية هي النظام بأكمله
يعني التشفير من جهة العميل أن كلمة المرور الرئيسية تحمي المفتاح المتماثل للمستخدم. إذا كانت قاعدة بيانات النسخة قد نُسخت من قِبل مهاجم، فلا يحمي كلمة المرور الرئيسية القصيرة أي شيء مما يرد في هذا المنشور، لأن المهاجم يهاجم تلك النسخة دون اتصال وبالسرعة التي تسمح بها أجهزته. ولا يصل أي إعداد على الخادم إلى جهاز المهاجم نفسه.
من السهل إساءة فهم PASSWORD_ITERATIONS=600000. فهو يحدد عدد التكرارات للتجزئة التي ينفذها الخادم على تجزئة تسجيل الدخول التي يرسلها العميل، ويذكر قالب إعدادات المشروع أن القيمة المعدلة تُطبَّق على المستخدمين الحاليين عند تسجيل دخولهم التالي. ولا يؤثر ذلك في KDF من جهة العميل، وهو الإعداد الذي يحدد تكلفة تنفيذ هجوم دون اتصال على الخزنة. ويخص هذا الإعداد كل حساب، ويمكن تغييره من Settings، ثم Security، ثم Keys في خزنة الويب، ما يؤدي إلى إعادة تشفير المفتاح. تذكر وثائق Bitwarden أن عملاءها منذ 2026.2.1 يرفعون تلقائياً إعداد PBKDF2 إذا كان أقل من 600,000 تكرار، عند تسجيل المستخدم الدخول أو إلغاء قفل الخزنة باستخدام كلمة المرور الرئيسية. وحتى 10 October 2026، لم يُؤكَّد هذا السلوك مع خادم Vaultwarden، كما أنه لا يغيّر إعدادات Argon2id مطلقاً. لذلك قد يظل من سجّل حسابه قبل سنوات يستخدم إعداد KDF أقدم وأقل. تحقّق من ذلك وأبلغهم به، لأن الخادم لا يستطيع إجراء هذا التغيير نيابةً عنهم.
بعد ذلك، فعّل المصادقة الثنائية لكل حساب. فهي لا تحمي النص المشفّر، لأنها لا تشارك في تشفير الخزنة. لكنها تمنع كلمة مرور مسروقة من أن تكون كافية لتسجيل الدخول ومزامنة نسخة. عند إعداد البريد الإلكتروني، تجعل REQUIRE_DEVICE_EMAIL=true رسالة البريد الإلكتروني لتسجيل الدخول من جهاز جديد إلزامية: فإذا فشل الإرسال، يفشل تسجيل الدخول. وإذا كانت خدمة البريد معطّلة بالكامل، فلن يمنع هذا الإعداد عمليات تسجيل الدخول من أجهزة جديدة.
موضع فشل خزائن الاستضافة الذاتية هو النسخ الاحتياطية
إنشاء tar czf لمجلد البيانات وتركه في مجلد المنزل على VPS نفسه يلغي جميع الخطوات السابقة. يحتوي هذا الأرشيف على db.sqlite3 الذي يتضمن النص المشفّر لكل مستخدم، وعلى rsa_key.pem الذي يمكنه تزوير جلسات تسجيل الدخول، وعلى config.json الذي يتضمن قيمة رمز المسؤول المُعد وكلمة مرور SMTP. تخزّن تعليمات Argon2 تجزئة كلمة مرور المسؤول، لكن كلمة مرور SMTP تبقى بنص واضح. ويؤدي الوصول للقراءة إلى ذلك الملف الواحد إلى كشف بيانات الحسابات وأسرار الخادم وبيانات الخزائن المشفّرة.
قاعدتان تكفيان لذلك. انقل الأرشيف خارج الخادم. وشَفِّره قبل نقله.
هناك أيضاً مشكلة تتعلق بصحة البيانات. قد يؤدي نسخ db.sqlite3 باستخدام cp أثناء تشغيل الخدمة إلى إنشاء ملف قيد الكتابة، ولن يفتح هذا الملف. وقد لا تكتشف المشكلة إلا عند الاستعادة. استخدم لقطة SQLite الأصلية بدلاً من ذلك:
sqlite3 /vw-data/db.sqlite3 ".backup '/tmp/vw-db-backup.sqlite3'"أما جانب الاستعادة، وهو الجزء الذي لا يختبره أحد تقريباً، فتغطيه دليل النسخ الاحتياطي والاستعادة في Vaultwarden.
ما الذي تتنازل عنه مقارنةً بخدمة Bitwarden المستضافة
تقييم صريح. يدير خدمة Bitwarden المستضافة أشخاص تتمثل وظيفتهم الأساسية في تشغيلها، مع عمليات تدقيق منشورة تجريها جهات خارجية. عند الاستضافة الذاتية، تستبدل ذلك بجدول التحديثات الخاص بك.
يصدر Vaultwarden إصلاحات أمنية ضمن الإصدارات العادية، وتصدر هذه الإصدارات بوتيرة متقاربة. وفقاً للتحقق الذي أُجري في 2 November 2026 من موجز إصدارات المشروع، الإصدار الحالي هو 1.37.5، وقد نُشر في 1 November 2026. تتناول ملاحظاته تحذيرين أمنيين بدرجة Medium، وتوصي بشدة بالتحديث في أقرب وقت ممكن. كما يلزم هذا الإصدار لتفعيل طرق تسجيل الدخول بخطوتين أو تعطيلها في web vault 2026.7.1 والإصدارات الأحدث، وللاستيلاء على حساب عبر emergency access في 2026.9.0 والإصدارات الأحدث. أما الإصدار السابق، 1.37.4، الذي نُشر في 5 October 2026، فقد تناول سبعة تحذيرات أمنية، وكان أشدها بدرجة High. وكان ذلك الإصدار الرابع منذ 1.37.0 في 24 July 2026: صدر 1.37.1 في 29 July، ثم 1.37.2 في 22 August، وهو مطلوب للعملاء ذوي الإصدار 2026.8.0 والإصدارات الأحدث، ثم 1.37.3 في 13 September، الذي يلغي رموز تذكّر 2FA عند تغيير بيانات الاعتماد أو 2FA، ويحدّ معدل طلبات نقاط نهاية prelogin وauth. إذا كنت تدير مؤسسات مع مشرفين لا تثق بهم بالكامل، فتقترح ملاحظات 1.37.4 أيضاً تدوير مفتاح API لكل مؤسسة بعد التحديث. يغيّر 1.37.4 كذلك العنوان الذي تتم قراءته من X-Forwarded-For، كما هو موضح في قسم reverse proxy أعلاه. إذا أعددت مثيلاً قبل عام ثم نسيته، فهو يشغّل شيفرة عمرها عام. لا تساعد العلامة latest وحدها: تظل الحاوية قيد التشغيل تستخدم الصورة التي بدأت بها إلى أن تشغّل docker compose pull وتعيد إنشاءها. فعّل التحديثات غير التفاعلية على Ubuntu لحزم المضيف، وضع تحديث الحاوية في تذكير تقويمي ستقرأه فعلاً.
الاستنتاج الذي ينبغي للقارئ الصريح الوصول إليه هو أن التشفير هنا مبني على تصميم Bitwarden ويظل متيناً، بينما تنتقل المخاطر التشغيلية بالكامل إليك. إذا كنت ستحدّثه بانتظام وتحتفظ بنسخة احتياطية منه في مكان آخر، فإن مثيل Vaultwarden على VPS تتحكم فيه يمثل مكاناً معقولاً لحفظ كلمات مرورك. إذا لم تكن هاتان العادتان ممكنتين، فادفع تكلفة الخدمة المستضافة ووجّه اهتمامك إلى أمور أخرى. توجد المقارنة التفصيلية بين الميزات في Vaultwarden مقارنةً بـ Bitwarden المستضاف ذاتياً.
تحصين المضيف الذي يعمل تحته الحاوية
Vaultwarden عملية واحدة على نظام Linux، ويمكن للحساب root على ذلك النظام قراءة /vw-data بغض النظر عما تفعله إعدادات التطبيق. شغّل الحاوية بحساب غير مميّز باستخدام user: "1000:1000" في ملف compose، واضبط ملكية مجلد البيانات بما يتوافق مع ذلك، وثبّت أي مورد لا تكتب إليه الحاوية بوضع القراءة فقط باستخدام :ro. ثم أغلق نقطة الدخول العامة: يشرح تحصين SSH على VPS تسجيل الدخول باستخدام المفاتيح فقط وتعطيل المصادقة بكلمة المرور، وهذا ما يوقف الهجوم العادي الذي يتجاوز كل الإجراءات السابقة.
FAQ
هل يستطيع أحد قراءة كلمات المرور إذا سرق قاعدة بيانات Vaultwarden؟
ليس مباشرةً. يُشفَّر كل عنصر في الخزنة داخل العميل باستخدام مفتاح مُنشأ عشوائياً، وتتم حمايته عبر المفتاح المتماثل للمستخدم أو المؤسسة؛ ويحمي المفتاح المشتق من كلمة المرور المفتاح المتماثل للمستخدم. يحتوي db.sqlite3 على محتوى الخزنة المشفّر إلى جانب بيانات الحساب الوصفية بنص واضح. وما يحصل عليه المهاجم فوراً هو عنوان البريد الإلكتروني لكل حساب وتلميح كلمة المرور، وإعدادات KDF، وبيانات تسجيل الدخول والجهاز، وأسرار TOTP الخاصة بتسجيل الدخول في جدول twofactor، وهي مخزّنة من دون تشفير لأن الخادم يجب أن يحسب الرمز المتوقع. ويمكنه أيضاً مهاجمة النص المشفّر للخزنة دون اتصال بالإنترنت طوال المدة التي يريدها، ولذلك فإن طول كلمة المرور الرئيسية هو العامل الذي يحسم النتيجة.
هل ينبغي أن أستخدم ADMIN_TOKEN أم أعطّل صفحة الإدارة بالكامل؟
عطّلها إذا استطعت، لأن معظم المثيلات تحتاج إليها مرة واحدة لإعداد SMTP ودعوة المستخدمين، ثم لا تحتاج إليها مجدداً. لتعطيلها، لا تضبط ADMIN_TOKEN ولا DISABLE_ADMIN_TOKEN، وأزل أي مفتاح "admin_token" من config.json، ثم أعد إنشاء الحاوية. لا يكفي حذف متغير البيئة وحده، لأن الإعدادات التي كتبتها صفحة الإدارة تكون موجودة في config.json ولها أولوية. إذا أبقيت الصفحة مفعّلة، فخزّن الرمز على شكل تجزئة Argon2 تنتجها vaultwarden hash بدلاً من سلسلة عشوائية بنص واضح، واضبط ADMIN_RATELIMIT_MAX_BURST=3.
رمز ADMIN_TOKEN صحيح، لكن /admin يرفضه. ما الخطأ؟
في الغالب تكون المشكلة في استبدال $. تحتوي سلسلة Argon2 بصيغة PHC على عدة محارف $، ويستبدلها Docker Compose بمتغيرات داخل كتلة docker-compose.yml environment:، لذلك تتلقى الحاوية قيمة مشوّهة رغم أن الملف يبدو صحيحاً. ضاعف كل $ إلى $$ في ملف compose، أو انقل القيمة إلى ملف .env وضعها بين علامتي اقتباس مفردتين، حيث لا تكون هناك حاجة إلى الهروب. أعد إنشاء الحاوية بعد ذلك، لأن إعادة التشغيل لا تلتقط تغييرات البيئة.
هل ما زلت بحاجة إلى فتح المنفذ 3012 للإشعارات؟
لا. أزيل دعم حركة WebSocket على المنفذ 3012 في Vaultwarden 1.31.0 لأن الإشعارات نُقلت إلى منفذ HTTP الرئيسي، وأصبح WEBSOCKET_ENABLED وWEBSOCKET_PORT مهجورين منذ 1.29.0 ويتم تجاهلهما. الإعداد الحالي هو ENABLE_WEBSOCKET، وقيمته الافتراضية هي true. أغلق 3012 في جدار الحماية واحذفه من ملف compose، ثم تأكد من أن Reverse Proxy يمرّر الرأسيْن Upgrade وConnection، لأن إشعارات WebSocket في المتصفح وتطبيق سطح المكتب تعتمد عليهما. يستخدم عملاء الأجهزة المحمولة إشعارات دفع تُضبط بشكل منفصل.