في هذه المقالة ، سوف نتعلم كيفية الاتصال بشكل صحيح أوامر النظام باستخدام node.js لتجنب نقاط الضعف في خط الأوامر المشتركة.
الطريقة التي نستخدمها في كثير من الأحيان للاتصال أوامر هي أبسط child_process.exec. له نمط استخدام بسيط للغاية. يمر في أمر سلسلة ويمرر نتيجة معالجة خطأ أو أمر إلى وظيفة رد الاتصال.
فيما يلي مثال نموذجي لأوامر نظام الاتصال الخاصة بك من خلال child_process.exec.
نسخة الكود كما يلي:
child_process.exec ('ls' ، function (err ، data) {
console.log (البيانات) ؛
}) ؛
ومع ذلك ، ماذا يحدث عندما تحتاج إلى إضافة بعض المعلمات التي يديرها المستخدم إلى الأمر الذي تتصل به؟ الحل الواضح هو ربط إدخال المستخدم مباشرة باستخدام الأمر الخاص بك. ومع ذلك ، تخبرني سنوات خبرتي: عندما ترسل سلاسل متصلة من نظام إلى آخر ، ستكون هناك مشاكل في يوم من الأيام.
نسخة الكود كما يلي:
var path = "إدخال المستخدم" ؛
child_process.exec ('ls -l' + path ، function (err ، data) {
console.log (البيانات) ؛
}) ؛
لماذا تواجه سلسلة الاتصال مشاكل؟
حسنًا ، لأنه بموجب محرك child_process.exec ، سيتم استدعاء تنفيذ "/bin/sh". ليس البرنامج المستهدف. يتم تمرير الأمر المرسلة للتو إلى عملية جديدة "/bin/sh 'لتنفيذ القشرة. اسم child_process.exec مضللة إلى حد ما - هذا هو مترجم باش ، وليس برنامجًا. وهذا يعني أن جميع أحرف الصدفة يمكن أن يكون لها عواقب مدمرة إذا تم تنفيذ المعلمات التي يتم إدخالها مباشرة.
نسخة الكود كما يلي:
[PID 25170] execve ("/bin/sh" ، ["/bin/sh" ، "-C" ، "ls -l user input"] ، [/ * 16 vars */]
على سبيل المثال ، يمكن للمهاجم استخدام فاصلة فاصلة "؛" لإنهاء الأمر وبدء مكالمة جديدة ، ويمكنهم استخدام backticks أو $ () لتشغيل المفوض الفرعي. هناك أيضا العديد من الانتهاكات المحتملة.
إذن ما هي الطريقة الصحيحة للاتصال بها؟
execfile / تفرخ
يأخذ Spawn و Execfile معلمة صفيف إضافية ، وهي ليست بيئة قذيفة يمكنها تنفيذ أوامر أخرى ولن تدير أوامر إضافية.
دعنا نستخدم execfile والتفرخ لتعديل المثال السابق لمعرفة كيف تختلف مكالمات النظام ولماذا ليس عرضة لحقن الأوامر.
child_process.execfile
نسخة الكود كما يلي:
var child_process = require ('child_process') ؛
var path = "."
child_process.execfile ('/bin/ls' ، ['-l' ، path] ، function (err ، result) {
console.log (نتيجة)
}) ؛
تشغيل مكالمات النظام
نسخة الكود كما يلي:
[PID 25565] execve ("/bin/ls" ، ["/bin/ls" ، "-l" ، "."] ، [/ * 16 vars */]
child_process.spawn
أمثلة على استخدام استبدال تفرخ متشابهة للغاية.
نسخة الكود كما يلي:
var child_process = require ('child_process') ؛
var path = "."
var ls = child_process.spawn ('/bin/ls' ، ['-l' ، path])
ls.stdout.on ('Data' ، function (data) {
console.log (data.toString ()) ؛
}) ؛
تشغيل مكالمات النظام
نسخة الكود كما يلي:
[PID 26883] execve ("/bin/ls" ، ["/bin/ls" ، "-l" ، "."] ، [/ * 16 vars */
عند استخدام تفرخ أو execfile ، فإن هدفنا هو تنفيذ أمر واحد فقط (المعلمة). هذا يعني أنه لا يمكن للمستخدم تشغيل الأمر الذي تم حقنه لأن /bin /ls لا يعرف كيفية التعامل مع backticks أو الأنابيب أو ؛. ما يفسره /bin /bash هو معلمات تلك الأوامر. يشبه استخدام معلمة لتمرير المعلمات إلى استعلام SQL ، إذا كنت على دراية به.
ولكن هناك أيضًا تحذير: استخدام تفرخ أو execfile ليس آمنًا دائمًا. على سبيل المثال ، قد لا يزال تشغيل /bin /find ومرر معلمات إدخال المستخدم محاصراً للنظام. يحتوي أمر Find على بعض الخيارات التي تسمح بقراءة/كتابة الملفات التعسفية.
لذلك ، إليك بعض التوجيهات على أوامر نظام التشغيل node.js:
تجنب استخدام child_process.exec ، خاصة عندما تحتاج إلى تضمين المعلمات التي أدخلها المستخدم ، يرجى تذكر ذلك.
حاول تجنب السماح للمستخدمين بتمرير المعلمات. يعد استخدام التحديدات أفضل بكثير من إدخال المستخدمين مباشرة.
إذا كان عليك السماح للمستخدم بإدخال المعلمات ، راجع معلمات الأمر على نطاق واسع ، وتحديد الخيارات الآمنة ، وإنشاء قائمة بيضاء.