ما يكشفه بحث Forever Security
في 25 أكتوبر 2026 نقل موقع BleepingComputer أعمال Forever Security. وجد الباحثون في مجلد ~/Downloads لدى أحد العملاء ملف AGENTS.md ملغّمًا يطلب من وكيل برمجة تثبيت حزمة npm اسمها «function-color». وكان ذلك المجلد هو نفسه دليل عمل الوكيل، والوكلاء يحمّلون تلقائيًا ملفات التعليمات الموجودة في دليل عملهم. والسيناريو المعاد بناؤه كالتالي: صفحة ويب تضع الملف بصمت في مجلد التنزيلات، ثم يشغّل المستخدم وكيله في ذلك المجلد، فيتبع الوكيل التعليمات بالأدوات والصلاحيات التي يملكها أصلًا.
مع Codex من OpenAI نجحت السلسلة الكاملة، بحمولة غير ضارة (فتح الحاسبة)، في 90 % من الاختبارات. ومع Claude Code كان ملف CLAUDE.md ملغّم يعيد توجيه حركة واجهة البرمجة إلى خادم يملكه الباحثون، فينكشف رمز OAuth ويمكن تنفيذ شيفرة في نحو 50 % من الاختبارات. ويرى الباحثون أنه لا شيء يستدعي الإصلاح لدى OpenAI أو Anthropic: فالوكيل يعمل كما صُمّم.
ما تثبته الدراسة وما لا تقوله
- المثبت — يكفي ملف تعليمات في دليل العمل لتوجيه الوكيل، دون استغلال أي ثغرة برمجية.
- الشرط — يتطلب الهجوم أن يشغّل المستخدم الوكيل في المجلد المعني. ووفق BleepingComputer توصي OpenAI وAnthropic أصلًا بتشغيل الوكيل في دليل المشروع.
- حمولات غير ضارة — نسبتا 90 % ونحو 50 % مصدرهما اختبارات بحمولة غير خطرة؛ فهما لا تقيسان هجمات حقيقية ولا تنطبقان على كل الأدوات.
- طريقة التسليم مجهولة — لم يتمكن الباحثون من تحديد كيف وصل ملف العميل إلى تنزيلاته: وتبقى صفحة الويب مجرد فرضية.
- Cursor CLI — ذُكرت أداة شُغّلت من ذلك المجلد، لكنها لم تُوصف بأنها اختُبرت.
- قراءتنا — قد يحتوي مستودع مستنسخ على ملف تعليماته الخاص. هذا استنتاج منا وليس نتيجة من الدراسة.
تشغيل الوكيل في المجلد الصحيح والموافقة على الأوامر: فلماذا خادم منفصل؟
الموافقة البشرية وصندوق الحماية في الأداة ضمانتان حقيقيتان ويجب إبقاؤهما مفعّلتين، وهذا ما يوصي به المقال المصدر أيضًا. لكنهما تقومان على انتباه دائم وأداة مضبوطة جيدًا وتشغيل في المجلد الصحيح. وحين يختل أحد الثلاثة يصبح السؤال: ما الذي يستطيع الوكيل المخترَق الوصول إليه؟
على جهاز العمل الجواب طويل: مفاتيح SSH ورموز السحابة ومستودعات عملاء آخرين ومتصفح مسجَّل الدخول. وعلى VPS مخصص الجواب قصير وأنت من يحدده. لا يحوي الخادم إلا المشروع ورمزًا بأقل نطاق ممكن ومستخدمًا بلا صلاحيات. ويمكن حذفه وإعادة إنشائه دون المساس بجهازك. لا يجعل VPS الوكيل آمنًا: إنه يحدّ من نطاق ضرره، وهذا هو الوعد الوحيد لهذا الدليل.
المتطلبات: ما تحتاجه لبناء الـ VPS
تستدعي وكلاء البرمجة واجهة برمجة لدى مزوّد: لا يجري حساب النموذج على خادمك، ولذلك يبقى الـ VPS متواضعًا. خصّص نواتين افتراضيتين و4 غيغابايت من الذاكرة و40 غيغابايت من القرص لمشروع Node أو PHP عادي، وأكثر إن كنت تبني أو تشغّل حاويات. تحتاج إلى وصول root وإلى Debian أو Ubuntu LTS وعنوان IPv4 مخصص ومفتاح SSH للاتصال.
من جهة الشبكة يُفتح منفذ واحد للدخول: المنفذ 22 لـ SSH. وفي الخروج لا يصل مستخدم الوكيل إلا إلى المنفذ 8888 على الواجهة المحلية، حيث يستمع وكيل وسيط يمرّر الطلبات إلى قائمة قصيرة من المضيفين. دوّن إذن قبل البدء المضيفين الذين تحتاجهم أداتك: سجل npm وواجهة المزوّد ومستودع Git للمشروع. الأوامر أدناه مكتوبة لـ Debian وUbuntu باستخدام apt وufw وnftables، وتفترض حساب إدارة يملك sudo، منفصلًا عن مستخدم الوكيل.
بناء الـ VPS في تسع خطوات
إنشاء مستخدم بلا صلاحيات
sudo adduser --disabled-password --gecos "" agentid agentsudo -l -U agent
يجب ألا يعرض الأمر الثاني المجموعةsudo، ويجب أن يجيب الثالث بأن المستخدم غير مخوَّل بتنفيذ sudo. الحساب بلا كلمة مرور: تدخل إليه بالأمرsudo -iu agentمن حساب الإدارة. خصّص VPS لكل عميل حتى لا يرى المستخدمagentفي خادم إلا مشاريع ذلك العميل.تخصيص دليل لكل مشروع
sudo install -d -o agent -g agent -m 700 /srv/agents/projet-a
شغّل الوكيل دائمًا من هذا الجذر، وليس من/أو~أو مجلد تنزيلات: فالدليل الأب الواسع يحمّل ملفات تعليمات لم تراجعها. ولجلب الشيفرة ادخل جلسة الوكيل ثم استنسخ:sudo -iu agentgit clone <url-du-dépôt> /srv/agents/projet-a
استخدم مفتاح نشر للقراءة فقط، يُولَّد على هذه الآلة ويخص مستودع المشروع.قطع الوصول إلى مفاتيحك
sudo -iu agent printenv SSH_AUTH_SOCKecho 'AllowAgentForwarding no' | sudo tee /etc/ssh/sshd_config.d/10-agents.confsudo sshd -t && sudo systemctl reload ssh
يجب ألا يعرض الأمر الأول شيئًا: لا يُمرَّر أي وكيل SSH إلى جلسة وكيل البرمجة. اتصل بالآلة عبرssh -o ForwardAgent=no. لا تضع أي مفتاح نشر للإنتاج على هذا الخادم، ولا أي ملف إعدادات يخص عميلًا آخر.حصر متغيرات البيئة في المشروع
sudo -iu agentinstall -m 600 /dev/null ~/.agent-env
ثم حرّر~/.agent-envليحوي رمز المزوّد فقط، بأقل نطاق ممكن، مخصصًا لهذه الآلة وقابلًا للإلغاء. شغّل الوكيل ببيئة مفرَّغة حتى لا يرث أي متغير من جلستك:env -i HOME="$HOME" PATH="$PATH" TERM="$TERM" HTTPS_PROXY=http://127.0.0.1:8888 bash -c 'set -a; . ~/.agent-env; cd /srv/agents/projet-a && exec claude'
استبدلclaudeبأمر أداتك. وإن كانت الأداة تتصل بحساب (OAuth) بدل مفتاح واجهة برمجة، فإن الرمز يوجد في الدليل الشخصي للمستخدم: وهو الرمز الذي رأت الدراسة أنه انكشف مع Claude Code. فضّل مفتاح واجهة برمجة مخصصًا تلغيه عند حذف الخادم.إغلاق الدخول بـ ufw
sudo ufw default deny incomingsudo ufw limit 22/tcpsudo ufw enable
أبقِ جلسة SSH ثانية مفتوحة أثناء التفعيل. يرشّحufwبالعنوان والمنفذ، لا باسم المضيف ولا بالمستخدم: ولهذا يُضبط الخروج في الخطوتين التاليتين بوكيل وسيط وnftables.تثبيت وكيل خروج بقائمة سماح
sudo apt install tinyproxy
في/etc/tinyproxy/tinyproxy.confاضبطListen 127.0.0.1,Port 8888,FilterDefaultDeny Yes,Filter "/etc/tinyproxy/allowlist"ثم اكتب القائمة:printf '%s\n' '^registry\.npmjs\.org$' '^api\.anthropic\.com$' | sudo tee /etc/tinyproxy/allowlistsudo systemctl restart tinyproxy
كل سطر تعبير نمطي على اسم المضيف. استبدل واجهة المثال بواجهة مزوّدك المأخوذة من وثائقه، ولا تضف^github\.com$إلا إذا طلبها المشروع. تصف وثائق tinyproxy ترشيحًا بحسب النطاق، لكنها لا تذكر أنه يُطبَّق على مضيف أنفاق HTTPS (CONNECT): فهذا غير موثَّق ويجب اختباره. لا تعتبر هذا الترشيح مسلَّمًا به قبل اختبار الخطوة 8.منع الوكيل من أي خروج آخر بـ nftables
أضف في نهاية
/etc/nftables.conf، بعد السطرflush ruleset:table inet agent_egress {chain output {type filter hook output priority 0; policy accept;meta skuid "agent" oifname "lo" tcp dport 8888 acceptmeta skuid "agent" limit rate 5/minute log prefix "agent-egress-drop: "meta skuid "agent" counter drop}}
لم يعد المستخدمagentيصل إلا إلى الوكيل المحلي؛ وما عداه يُسجَّل ثم يُسقَط. افحص الصياغة قبل التفعيل:sudo nft -c -f /etc/nftables.confsudo systemctl enable --now nftablesاختبار صمود الترشيح
sudo -u agent curl -sS --max-time 5 https://example.comsudo -u agent curl -sS -x http://127.0.0.1:8888 https://example.comsudo -u agent curl -sS -x http://127.0.0.1:8888 -o /dev/null -w '%{http_code}\n' https://registry.npmjs.org/-/ping
يجب أن ينتهي الأمر الأول بانتهاء المهلة لأن جدار الحماية يُسقط الاتصال المباشر. ويجب أن يفشل الثاني برفض من الوكيل الوسيط (مع curl، رسالة من نوعCONNECT tunnel failed؛ والرمز الدقيق في سجل tinyproxy). ويجب أن يجيب الثالث بالرمز 200. هذه النتائج هي المتوقعة: عاينها بنفسك على آلتك. وإن نجح الأمر الثاني فالترشيح لا يغطي أنفاق HTTPS: فانتقل إلى وكيل وسيط تغطي وثائقه CONNECT، وصحّح ذلك قبل تشغيل الوكيل.التخطيط لحذف الخادم
احفظ هذه الأوامر في سكربت خاضع للإصدارات. يُحذف الـ VPS المشبوه ويُعاد إنشاؤه من الصورة الأصلية، وتلغي رمز المزوّد. أما وصولك أنت إلى الخادم فمرّره عبر مضيف بوابة (bastion) بدل التعريض المباشر: ويغطي دليل bastion host المرتبط أسفل الصفحة هذه النقطة.
كل ضمانة تحدّ من شيء محدد
مرّر الجدول أفقيًا
| الضمانة | ما تحدّ منه | ما تتركه يمر |
|---|---|---|
| الموافقة البشرية على الأوامر | كل أمر يُنفَّذ | أمر يبدو عاديًا تُوافَق عليه بحكم العادة |
| صندوق حماية الأداة | إجراءات الوكيل بحسب إعداده | ما يتوقف على إعداد منسيّ أو يخص الأداة |
| مستخدم بلا صلاحيات | صلاحيات root وملفات النظام والحسابات الأخرى | ملفات المستخدم نفسه، ومنها رمزه |
| دليل مشروع ضيق | ملفات التعليمات المحمّلة بالخطأ | محتوى المشروع نفسه |
| خروج شبكي مرشَّح | تثبيت من مضيف غير متوقَّع، أو إرسال إلى مضيف مجهول (بعد التحقق من الترشيح) | كل ما يمر عبر مضيف مسموح، سجلًا كان أو واجهة برمجة |
| لا مفتاح إنتاج على الآلة | الوصول إلى خوادم الإنتاج وإلى عملاء آخرين | ما يكشفه رمز المزوّد ومستودع المشروع |
| VPS مخصص وقابل لإعادة الإنشاء | بقاء الاختراق واستمراره، وجهاز العمل | بيانات العميل الموجودة على الآلة |
مراجعة ملفات التعليمات قبل أن يتبعها الوكيل
يعمل ملف التعليمات مثل الشيفرة، عبر الوكيل: فراجعه على هذا الأساس. لإعداد الجرد نفّذ find /srv/agents -type f \( -name AGENTS.md -o -name CLAUDE.md \) -not -path '*/node_modules/*' . وللكشف عن أي تغيير احتفظ ببصمة:find /srv/agents -type f \( -name AGENTS.md -o -name CLAUDE.md \) -not -path '*/node_modules/*' -exec sha256sum {} + | sort -k2 > ~/instructions.sha256
ثم قارن قبل كل جلسة بالأمر find /srv/agents -type f \( -name AGENTS.md -o -name CLAUDE.md \) -not -path '*/node_modules/*' -exec sha256sum {} + | sort -k2 | diff ~/instructions.sha256 - . وفي مستودع Git يعرض git log -p -- AGENTS.md CLAUDE.md ما تغيّر منذ مراجعتك الأخيرة.
لا تبحث الأدوات كلها عن ملفاتها في المكان نفسه (الدليل الحالي، المجلدات الأم، المجلد الشخصي): اقرأ وثائق أداتك وأبقِ هذه المواضع فارغة أو معروفة. لا تشغّل وكيلًا أبدًا في ~/Downloads ولا في مجلد أب واسع: فهذا أول تخفيف توصي به الدراسة.
حالة الحزمة function-color: ما يغيّره الترشيح وما لا يغيّره
في الحالة الموثقة كان الملف الملغّم يطلب تثبيت حزمة npm باسم «function-color». ولا يذكر التقرير من أي سجل. قراءتنا، وهي تخفيف وليست نتيجة من البحث: خروج مقيّد بقائمة مضيفين ينبغي أن يمنع أو ينبّه (متى تحقّقتَ من الترشيح في الخطوة 8) إلى تثبيت من سجل أو خادم غير متوقَّع، ويُظهر ذلك سجلّ الوكيل الوسيط.
لكنه لا يمنع حزمة منشورة على السجل المسموح به، لأن registry.npmjs.org ضمن القائمة. وفي هذه الحالة اعتمد على مراجعة ملف التعليمات وعلى قائمة حزم معتمدة، وقبل ذلك كله على ألا يوجد ما يُسرَق من أسرار. ويفصّل دليل سلسلة التوريد في npm، المرتبط أدناه، هذا الجزء.
عدّ الرفض وقطع سكربتات التثبيت
اقرأ حالات الرفض كل يوم: sudo journalctl -k --since today | grep agent-egress-dropsudo grep -i filtered /var/log/tinyproxy/tinyproxy.log
الرفض المتكرر تجاه مضيف مجهول إشارة يجب فهمها قبل إضافة المضيف إلى القائمة: دوّن الاسم، وابحث عن سبب استدعاء الأداة له، ثم قرّر. وفي npm يمنع الأمر npm config set ignore-scripts true (يُنفَّذ بصفة agent) التشغيل التلقائي لسكربتات تثبيت الاعتماديات.
استكشاف الأخطاء: ما ستصادفه من أخطاء
الرسائل أدناه هي التي تنتجها مكونات هذا التركيب. ولكل منها علاج لا يمر أبدًا بإضعاف الترشيح.
- رفض sudo للمستخدم
agent— سلوك متوقَّع: المستخدم بلا أي صلاحية، ويتوقف نص الرفض الدقيق على إصدار sudo. نفّذ العملية بحساب الإدارة، ولا تضفagentإلى المجموعةsudoأبدًا. - فشل نفق
CONNECT(مع curl، رسالة من نوعCONNECT tunnel failed) — الأرجح أن الوكيل الوسيط رفض المضيف؛ والرمز الدقيق يتوقف على tinyproxy ويظهر في سجله. اقرأ اسمه في/var/log/tinyproxy/tinyproxy.logثم أضفه إلى القائمة فقط إذا احتاجته الأداة. - بحسب الأداة، انتهاء مهلة أو فشل في الشبكة (مثل
ETIMEDOUTأوfetch failed) — تحاول الأداة اتصالًا مباشرًا يُسقطهnftables. تحقق من أنHTTPS_PROXYمُصدَّر في جلسة الوكيل. بعض العملاء يتجاهلون هذا المتغير: فيفشلون فشلًا مغلقًا، وتبيّن وثائقهم إن كانوا يقبلون وكيلًا وسيطًا. Permission deniedعند قراءة مجلد مستخدم آخر — هذا هو العزل يؤدي عمله. وإن احتاج الوكيل إلى ملف فانسخه إلى دليل المشروع.- انقطاع اتصال SSH بعد
ufw enable— كانت قاعدة المنفذ 22 ناقصة، أو يستمع SSH على منفذ آخر. من وحدة تحكم الـ VPS نفّذsudo ufw statusثمsudo ufw allow <المنفذ>/tcp.
ما لا يحلّه هذا الخادم
يحدّ الـ VPS من نطاق الضرر ولا يزيل الخطر. رمز المزوّد موجود على الآلة: ويستطيع الوكيل المخترَق استخدامه ما لم تلغه. وشيفرة العميل موجودة أيضًا، وتبقى التسريبات ممكنة عبر مضيف مسموح، لأن السجل أو مستودع Git يقبل بيانات واردة كذلك. ويوصي الباحثون بإبقاء طلبات الموافقة وصندوق حماية الأداة: وهذا التركيب يضاف إليهما ولا يحل محلهما. وأخيرًا، يحتاج خادم التطوير الذي يقدّم حركة عامة إلى تقسية خاصة به، تعالجها قائمة التحقق المرتبطة أدناه.
للوكالة: خادم VPS لكل عميل
أسهل عزل يمكن شرحه لعميل هو العزل المادي: خادم VPS لكل عميل، بمستخدمه ورمزه وجدار حمايته. لا يوجد فيه مفتاح إنتاج ولا عميل آخر، فلا يتجاوز الوكيل المخترَق ذلك النطاق. احتفظ بسكربت إعداد واحد لكل الخوادم: فأي قاعدة خروج تتغير تُعاد في كل مكان. وفي نهاية المهمة تحذف الـ VPS وتلغي الرمز وتنتقل إلى ما بعده دون أي أثر على جهازك. يوفر VPS من ServOrbit.com وصول root وعنوان IPv4 مخصصًا واختيار نظام التشغيل: وهذا أساس هذا التصميم.
الوثائق الرسمية
تقرير BleepingComputer عن بحث Forever Security: https://www.bleepingcomputer.com/news/security/ai-coding-agents-hijacked-using-agentsmd-file-in-downloads-folder/
وثائق nftables: https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes
وثائق tinyproxy: https://tinyproxy.github.io/
صفحة دليل adduser (Debian): https://manpages.debian.org/stable/adduser/adduser.8.en.html
إعدادات npm: https://docs.npmjs.com/cli/using-npm/config