الوحدة cgi
في بايثون
تقدّم هذه الوحدة عددًا من الأدوات التي تستخدم بواسطة سكربتات CGI المكتوبة في بايثون.
مقدمة
تنفّذ سكربتات CGI بواسطة مخدّم HTTP وتستخدم عادة لمعالجة مدخلات المستخدم المرسلة إلى المخدّم من خلال عنصر <FORM>
أو <ISINDEX>
في HTML.
تستقرّ سكربتات CGI معظم الأحيان في المجلد الخاص cgi-bin
في المخدّم، ويضع مخدّم HTTP جميع المعلومات المرتبطة بالطلب (مثل اسم المضيف لدى العميل، عنوان URL المطلوب، سلسلة الاستعلام النصية، وغير ذلك الكثير) في بيئة الصدفة الخاصة بالسكربت، وينفّذ السكربت ثم يرسل مخرجاته إلى العميل مرة أخرى.
مدخلات السكربت مرتبطة بالعميل أيضًا، وفي بعض الأحيان تُقرأ بيانات النموذج بهذه الطريقة، وفي أحيان أخرى تُمرّر بيانات النموذج عبر سلسلة الاستعلام النصية "query string" كجزء من عنوان URL. أعدّت هذه الوحدة لمعالجة الحالات المختلفة وتقديم واجهة أبسط للتعامل مع سكربتات بايثون. تقدّم هذه الوحدة كذلك عددًا من الأدوات التي تساعد في تعقّب أخطاء السكربتات وتصحيحها، إلى جانب دعمها لعملية رفع الملفّات من نماذج HTML (إن كان المتصفّح يدعم ذلك).
يجب أن تكون مخرجات سكربتات CGI مقسومة إلى قسمين، يفصل بينهما سطر فارغ. يتضمّن القسم الأول عددًا من الترويسات والتي تخبر العميل بطبيعة البيانات المرسلة إليه. يبين المثال التالي كيفية إنشاء قسم ترويسات بسيط:
print("Content-Type: text/html")
print() # السطر الفارغ يعني نهاية قسم الترويسات
أما القسم الثاني فيتضمّن عادة شيفرة HTML، والتي تسمح لبرنامج العميل بأن يعرض النصوص بطريقة منسّقة وجميلة باستخدام الترويسات والصور وغيرها. تطبع الشيفرة التالية شيفرة HTML بسيطة:
print("<TITLE>CGI script output</TITLE>")
print("<H1>This is my first CGI script</H1>")
print("Hello, world!")
استخدام وحدة cgi
للبدء باستخدام الوحدة يجب استيرادها بواسطة الشيفرة التالية:
import cgi
وعند كتابة سكربت جديد أضف السطرين التاليين إلى بداية السكربت:
import cgitb
cgitb.enable()
تفعّل الشيفرة السابقة أداة خاصّة للتعامل مع الاستثناءات تعرض تقريرًا مفصّلا في متصفح الويب عند حدوث الأخطاء. إن كنت تفضل إخفاء الأخطاء عن مستخدمي الشيفرة الخاصّة بك، يمكنك حفظ التقارير إلى ملفات، وذلك باستخدام الشيفرة التالية:
import cgitb
cgitb.enable(display=0, logdir="/path/to/logdir")
تقدّم التقارير التي تنتجها هذه الأداة معلومات مفيدة من شأنها توفير الكثير من الوقت في تعقّب الأخطاء وإصلاحها. يمكن الاستغناء عن هذه الأداة بعد التأكد من أن السكربت خالٍ من الأخطاء ويعمل بصورة جيدة.
يمكن الوصول إلى البيانات المرسلة إلى المخدّم عن طريق الصنف FieldStorage
، وإن كان النموذج يتضمّن حروفًا بترميز غير ترميز ASCII فيمكن استخدام المعامل المفتاحي encoding
لتعيين قيمة الترميز المستخدم في الملف الحاوي على النموذج، ويمكن الوصول إلى الترميز المستخدم عادة في الوسم META
في قسم HEAD
من ملف HTML أو عن طريق الترويسة Content-Type
).
يقرأ الصنف محتويات النموذج من المدخلات القياسية أو من البيئة (بالاعتماد على قيمة عدد من متغيرات البيئة والمعيّنة حسب معايير CGI). قد يستهلك الصنف المدخلات القياسية؛ لذا يجب تهيئته مرة واحدة فقط.
تشبه نسخ الصنف FieldStorage
قواميس بايثون إلى حدّ كبير، إذ يمكن فهرسة الكائن كما هو الحال مع القواميس، ويمكن اختبار وجود عنصر معيّن باستخدام المعامل in
، ويدعم الكائن كذلك استخدام التابع keys()
الخاصّ بالقواميس إضافة إلى الدالة الداخلية len()
. لا تظهر حقول النموذج التي تتضمّن سلاسل نصية فارغة في القاموس وسيجري تجاهلها، وللإبقاء على مثل هذه القيم، يجب إعطاء المعامل المفتاحي keep_blank_values
القيمة True
عند إنشاء نسخة من الصنف FieldStorage
.
فعلى سبيل المثال تتحقّق الشيفرة التالية (والتي تفترض أنّ الترويسة Content-Type
والسطر الفارغ قد طُبعا مسبقًا) من أن الحقلين name
و addr
هما حقلان لا يحتويان على سلاسل نصية فارغة:
form = cgi.FieldStorage()
if "name" not in form or "addr" not in form:
print("<H1>Error</H1>")
print("Please fill in the name and addr fields.")
return
print("<p>name:", form["name"].value)
print("<p>addr:", form["addr"].value)
...further form processing here...
الحقول في هذه الشيفرة والتي تم الوصول إليها باستخدام العبارة form[key]
هي بحدّ ذاتها نسخ من الكائن FieldStorage
(أو MiniFieldStorage
وذلك حسب ترميز النموذج). يمكن الوصول إلى قيمة الحقل النصية باستخدام الخاصية value في نسخ الكائن، ويعيد التابع getvalue()
السلسلة النصية مباشرة، ويستقبل كذلك معاملًا آخر اختياريًا يُستخدم لتعيين القيمة التي يعيدها التابع إن لم يكن المفتاح المطلوب موجودًا.
إن تضمّنت البيانات المرسلة من النموذج على حقول متعددة تحمل الاسم ذاته، فإنّ الكائن المستحصل من العبارة form[key]
لن يكون كائن FieldStorage
أو MiniFieldStorage
ولكن قائمة من هذه الكائنات. وكذلك الأمر بالنسبة للتابع form.getvalue(key)
إذ سيعيد قائمة من السلاسل النصية في مثل هذه الحالة. إن كنت تتوقع حدوث هذه الحالة (أي عندما يتضمّن نموذج HTML الخاصّ بك على عدد من الحقول التي تحمل الاسم عينه) فاستخدم التابع getlist()
والذي يعيد قائمة من القيم دائمًا (وبهذه الصورة لن تضطر إلى التعامل مع الحالات الخاصة التي تتضمّن عنصرًا واحدًا فقط).
تربط الشيفرة التالية أي عدد من الحقول التي تحمل الاسم username
ويفصل بين قيمها بفاصلة:
value = form.getlist("username")
usernames = ",".join(value)
إن كان الحقل يمثّل ملفًّا مرفوعًا، فإنّ الوصول إلى قيمة الحقل باستخدام الخاصية value
أو باستخدام التابع getvalue()
سيؤدي إلى قراءة الملف بأكمله في الذاكرة على هيئة بايتات، وقد يكون هذا أمرًا غير مرغوب به؛ لذا يمكن التحقّق من رفع الملف إما باختبار الخاصية filename
أو الخاصية file
، ثم يمكن بعد ذلك قراءة البيانات من الخاصية file
قبل إغلاقه تلقائيًا كجزء من عملية التخلص من نسخة الصنف FieldStorage
(القيمة المعادة من التابعين read()
و readline()
هي البايتات):
fileitem = form["userfile"]
if fileitem.file:
# It's an uploaded file; count lines
linecount = 0
while True:
line = fileitem.file.readline()
if not line: break
linecount = linecount + 1
يمكن استخدام كائنات FieldStorage
مع عبارات with
، والتي ستغلق هذه الكائنات عند انتهاء عمل العبارة.
تعين القيمة -1 للخاصية done في الكائن عند حدوث خطأ أثناء جلب محتويات الملف المرفوع (عندما يقاطع المستخدم عملية إرسال النموذج بضغطه لزر الرجوع أو زر الإلغاء مثلًا).
تتيح مسوّدة معيار رفع عدد من الملفات باستخدام حقل واحد (باستخدام الترميز multipart/*
)، فإنّ العناصر المستحصلة تكون على هيئة كائن FieldStorage
شبيه بالقاموس. ويمكن التحقّق من حدوث هذه الحالة بواسطة الخاصية type
، والتي يجب أن تكون multipart/form-data
(أو أيّ نوع MIME آخر يطابق العبارة multipart/*
)، وفي هذه الحالة يمكن المرور على عناصر الكائن تعاوديًا كما هو الحال مع كائن النموذج الأساسي.
إذا أرسل النموذج إلى المخدّم بالأسلوب القديم (كسلسلة استعلام نصية أو كبيانات ذات جزء واحد من نوع application/x-www-form-urlencoded
) فإنّ العناصر ستكون نسخًا من الصنف MiniFieldStorage
. وفي هذه الحالة تحمل الخصائص list
و file
و filename
القيمة None
دائمًا.
يتضمّن النموذج المرسل بطريقة POST والذي يضمّ سلسلة استعلام نصية أيضًا عناصر FieldStorage
و MiniFieldStorage
.
ملاحظات:
- تُغلَق الخاصية
file
تلقائيًا عند التخلص من كائنFieldStorage
التابعة له في الإصدار 3.4 من بايثون. - أصبح الصنف
FieldStorage
داعمًا لبروتوكول إدارة السياق context management في الإصدار 3.5 من بايثون.
الواجهة البرمجية لوحدة cgi
تتضمّن الواجهة البرمجية لوحدة cgi
تابعين بسيطين، يمكن بواسطتهما معالجة بيانات النموذج بصورة عامة، دون الحاجة للتأكد من أنّ القيم المرسلة تمتلك الاسم ذاته أم لا.
يمكن استخدام الشيفرة التالية إن كان هناك احتمال أن يُرسل المستخدم أكثر من قيمة تحمل الاسم عينه:
item = form.getvalue("item")
if isinstance(item, list):
# يطلب المستخدم أكثر من عنصر واحد
else:
# يطلب المستخدم عنصرًا واحدًا فقط
هذه الحالة شائعة جدًّا، فعلى سبيل المثال قد يتضمّن النموذج عددًا من مربعات الاختيار checkboxes التي تحمل نفس الاسم:
<input type="checkbox" name="item" value="1" />
<input type="checkbox" name="item" value="2" />
ولكن في معظم الحالات، يكون هناك عنصر نموذج واحد يحمل اسمًا معيّناً في النموذج وتتوقع أن تحصل على قيمة واحدة مرتبطة بذلك العنصر باستخدام الشيفرة التالية على سبيل المثال:
user = form.getvalue("user").upper()
ولكن المشكلة في هذه الشيفرة هو أنّ عليك أن تتوقع أنّ العميل سيقدّم مدخلات صالحة للاستخدام في سكربتاتك. فعلى سبيل المثال، إن أضيفت عبارة user=foo
إلى سلسلة الاستعلام النصية، فإنّ السكربت سيتوقف عن العمل؛ وذلك لأنّ التابع getvalue("user")
سيعيد قائمة عوضًا عن سلسلة نصية، وليس بالإمكان استدعاء التابع upper()
على قائمة (لأنّ القوائم لا تمتلك تابعًا بهذا الاسم) وتكون النتيجة الحصول على الاستثناء AttributeError
.
ولهذا فإنّ الطريقة المناسبة لقراءة البيانات من النماذج هي استخدام شيفرة تتحقّق ممّا إذا كانت القيمة المستحصلة قيمة مفردة أو قائمة من القيم؛ ولكن هذه طريقة مزعجة وتؤدي إلى الحصول على شيفرة صعبة القراءة.
الطريقة الأنسب هي استخدام التابعين getfirst()
و getlist()
.
التابع FieldStorage.getfirst()
يعيد هذا التابع قيمة واحدة دائمًا ترتبط بالحقل الذي يحمل الاسم المعطى.
التابع FieldStorage.getlist()
يعيد هذا التابع دائمًا قائمة بالقيم المرتبطة بالحقل المعطى.
دوالّ الوحدة cgi
تقدّم الدالة مجموعة من الدوال المساعدة:
الدالة cgi.parse()
تحلّل الدالة الاستعلام الموجود في بيئة معينة أو في ملف معيّن.
الدالة cgi.parse_qs()
هذه الدالة مهملة في هذه الوحدة، وهي موجودة لغرض التوافق مع الإصدارات السابقة من بايثون. استخدم الدالة urllib.parse.parse_qs()
عوضًا عنها.
الدالة cgi.parse_qsl()
هذه الدالة مهملة في هذه الوحدة، وهي موجودة لغرض التوافق مع الإصدارات السابقة من بايثون. استخدم الدالة urllib.parse.parse_qsl()
عوضًا عنها.
الدالة cgi.parse_multipart()
تحلّل الدالة المدخلات من نوع multipart/form-data (للملفات المرفوعة).
الدالة cgi.parse_header()
تحلّل الدالة ترويسة MIME (مثل Content-Type) إلى قيمة رئيسة وقاموسٍ من المعاملات.
الدالة cgi.test()
سكربت اختبار CGI، يمكن استخدامه كبرنامج رئيسي. تكتب الدالة ترويسة HTTP مصغّرة وتنسّق جميع المعلومات المقدّمة إلى السكربت في نموذج HTML.
الدالة cgi.print_environ()
تنسّق الدالة بيئة الصدفة بصيغة HTML.
الدالة cgi.print_form()
تنسّق الدالة النموذج بصيغة HTML.
الدالة cgi.print_directory()
تنسّق الدالة المجلّد الحالي بصيغة HTML.
الدالة cgi.print_environ_usage()
تطبع الدالة قائمة من متغيرات البيئة المفيدة (المستخدمة من قبل CGI) بصيغة HTML.
الدالة cgi.escape()
تحوّل الدالة الحروف '&'
و '>'
و '<'
في السلسلة النصية المعطاة إلى تسلسل حروف آمن في HTML. تُستخدم هذه الدالة لعرض النصوص التي قد تحتوي على مثل هذه المحارف.
معالجة المسائل الأمنية
إن كنت تستدعي برنامجًا خارجيًا (بواسطة الدالة os.system()
أو os.popen()
أو أي دالة أخرى مشابهة) فعليك أن تحرص جيّدًا على عدم تمرير السلاسل النصية التي تستقبلها من العميل إلى الصدفة. تعدّ هذه الحالة من الخروقات الأمنية الشائعة والتي يستغلّها المخترقون الأذكياء على الويب لاستغلال سكربتات CGI الضعيفة لتنفيذ الأوامر في الصدفة. كذلك لا يمكن الوثوق بعناوين URL أو أي جزء منها بل وحتى أسماء الحقول؛ إذ ليس من الضروري أن يأتي الطلب من النموذج الخاصّ بك فقط.
إن كنت بحاجة إلى تمرير سلسلة نصية من نموذج إلى الصدفة، ولتكون في مأمن من الخروقات الأمنية، يجب عليك أن تحرص على أن تتضمّن السلسلة النصية أرقامًا وحروفًا وخطوط فاصلة dashes وشرطات سفلية underscores ونقاط periods فقط.
تثبيت سكربت CGI في أنظمة يونكس
يجب عليك مراجعة توثيق مخدّم HTTP الذي تستخدمه وكذلك مراجعة مدير النظام المحلي لمعرفة المجلد الذي يجب أن تُثبّت فيه سكربتات CGI، وعادة ما يكون المجلد الذي يحمل الاسم cgi-bin
في المخدّم.
احرص على أن يكون سكربتك سهل القراءة وقابلًا للتنفيذ من قبل الآخرين، ويجب أن يكون نمط ملفات يونكس هو النمط الثماني 0o755
(استخدم الأمر chmod 0755 filename
). احرص كذلك على أن يتضمّن السطر الأول في السكربت الرمز #! في العمود الأول متبوعًا بمسار مفسّر بايثون، فمثلًا:
#!/usr/local/bin/python
احرص على أن يكون مفسّر بايثون موجودًا وقابلًا للتشغيل من قبل المستخدم الذي يحمل الاسم "others".
احرص على أن تكون جميع الملفات التي يقرأها السكربت أو يكتب فيها قابلة للقراءة والكتابة من قبل المستخدم الذي يحمل الاسم "others"، ويجب أن تمتلك الملفات القابلة للقراءة الوضع 0o644
والملفات القابلة للكتابة الوضع 0o666
. سبب ذلك هو أنّ مخدّم HTTP سينفّذ السكربت الخاص بك -ولأسباب أمنية- تحت اسم المستخدم "nobody" ودون أيّ امتيازات خاصة. يمكن لهذا المستخدم أن يقرأ الملفات (يكتب فيها وينفّذها) التي يمكن لأي شخص أن يقرأها (يكتب فيها وينفّذها). إضافة إلى أنّ المجلّد الحالي في وقت التنفيذ يكون مختلفًا (عادة ما يكون مجلد cgi-bin
في المخدّم) وكذلك الأمر بالنسبة متغيرات البيئة إذ أنّها تكون مختلفة من تلك التي تحصل عليها عند الولوج إلى المخدّم. ويمكن القول أنّه يجب أن لا تعتمد على مسار البحث عن الملفات القابلة للتنفيذ (PATH
) أو وحدة مسار البحث في بايثون (PYTHONPATH
) لأنّهما لن يقدّما قيمًا مفيدة.
إن كنت بحاجة إلى تحميل وحدات معيّنة من مجلد غير موجود في المسار الافتراضي لوحدة مسار البحث في بايثون، يمكنك تغيير المسار في السكربت الخاص بك، قبل استيراد الوحدات الأخرى، فعلى سبيل المثال:
import sys
sys.path.insert(0, "/usr/home/joe/lib/python")
sys.path.insert(0, "/usr/local/lib/python")
بهذه الطريقة سيجري البحث في المجلد الأخير أوّلًا.
تختلف تعليمات التثبيت في الأنظمة الأخرى؛ لذا تحقّق من توثيق مخدّم HTTP الذي تستخدمه (تتضمّن التوثيقات عادة قسمًا خاصًّا بسكربتات CGI).
اختبار سكربت CGI
لن تعمل سكربتات CGI بصورة عامة عند تنفيذها في سطر الأوامر، وقد يعمل السكربت في سطر الأوامر على نحوٍ جيّد، ولكن يمكن لهذا السكربت أن يتسبب بمشاكل كبيرة عند تشغيله في المخدّم. ومع ذلك فهناك سبب واحد فقط يدعو إلى اختبار السكربتات في سطر الأوامر، وهو أنّ هذه السكربتات قد تتضمن أخطاء لغوية، وإن حدث ذلك فإنّ مفسّر بايثون لن ينفّذ هذه السكربتات إطلاقًا، وسيرسل مخدّم HTTP خطأ قد يكون مبهمًا بالنسبة للعميل.
إن السكربت الخاصّ بك خاليًا من الأخطاء ولكنّه مع ذلك لا يعمل، فراجع القسم تنقيح سكربتات CGI.
تنقيح سكربتات CGI
تأكد في البداية من عدم وجود أي أخطاء أثناء تثبيت السكربت (راجع قسم تثبيت سركبت CGI في أنظمة يونكس) للمزيد من التفاصيل.
للتحقّق من أنّ عملية التثبيت قد تمّت على ما يرام، يمكن تثبيت نسخة من ملف هذه الوحدة (cgi.py
) كسكربت CGI. يؤدي تنفيذ هذا الملف كسكربت إلى تخلّص الملف من البيئة الخاصة به إضافة إلى محتويات النموذج في نموذج HTML. امنح الملف الصلاحيات المطلوبة، ثم أرسله كطلب، فإن كان الملف مثبّتًا في مجلد cgi-bin
القياسي، فسيكون بالإمكان إرسال الملف كطلب عن طريق إدخال العنوان التالي في متصفح الويب:
http://yourhostname/cgi-bin/cgi.py?name=Joe+Blow&addr=At+Home
إن كانت النتيجة هي ظهور الخطأ 404 فهذا يعني أنّ المخدّم لم يتمكّن من العثور على السكربت، ويمكن أن يعني هذا أنّك بحاجة إلى تثبيت السكربت في مجلّد مختلف. أما في حال ظهور خطأ مغاير فيعني ذلك وجود خطأ في عملية التثبيت، وأنّك لن تكون قادرًا على المتابعة ما لم تقم بمعالجة المشكلة. أما إن حصلت على قائمة مرتّبة ومنسّقة من البيئة ومحتويات النموذج (في هذا المثال سيظهر الحقل "addr"
مع القيمة "At Home"
والحقل "name"
مع القيمة "Joe Blow"
)، فهذا يعني أنّ السكربت cgi.py
مثبّت بطريقة صحيحة. يمكنك اتباع الخطوات السابقة مع السكربت الخاص بك؛ وبهذا تكون قادرًا على تنقيحه.
الخطوة التالية هي استدعاء الدالة test()
في الوحدة cgi
من داخل السكربت الخاصّ بك، وذلك باستبدال الشيفرة الرئيسية في السكربت بالعبارة
cgi.test()
يجب أن تحصل على نفس النتائج التي حصلت عليها عند تثبيت ملف cgi.py
كسكربت.
إن أطلق سكربت بايثون اعتيادي استثناءً غير معالج (لأي سبب من الأسباب: خطأ في كتابة اسم الوحدة، عدم القدرة على فتح ملفّ معين... الخ)، فإنّ مفسّر بايثون سيطبع رسالة خطأ منسّقة ثم يوقف عمل السكربت. ينفّذ مفسّر بايثون الأمر ذاته مع الاستثناءات التي تطلقها سكربتات CGI، ولكنّ رسالة الخطأ ستذهب على الأرجح إلى ملفات التسجيل log files في مخدّم HTTP أو يجري تجاهلها تمامًا.
يمكن إرسال رسائل الأخطاء إلى متصفّح الويب بسهولة باستخدام الوحدة cgitb
، وذلك بإضافة الأسطر التالية إلى بداية السكربت:
import cgitb
cgitb.enable()
تؤدي إضافة الأسطر السابقة إلى عرض تقرير مفصّل عند حدوث أي مشكلة في السكربت، ويمكن الاستفادة من هذا التقرير في معرفة السبب الرئيسي لتوقّف السكربت عن العمل.
إن كنت تشكّ في وجود مشكلة عند استيراد الوحدة cgitb
فيمكن استخدام الطريقة التالية (والتي تستخدم وحدات بايثون الداخلية فقط):
import sys
sys.stderr = sys.stdout
print("Content-Type: text/plain")
print()
...your code here...
تعتمد هذه الطريقة على مفسّر بايثون في طباعة رسالة الخطأ، وتعرض المخرجات على هيئة نصوص صرفة palin text، الأمر الذي يؤدي إلى عدم معالجة شيفرة HTML. إن كان السكربت يعمل بصورة جيدة فإنّ شيفرة HTML الخام ستُعرض على العميل، وإن أطلق السكربت استثناءً فإنّ الشيفرة ستعرض رسالة الخطأ بعد طباعة السطرين الأوليين على الأرجح، وبما أنّ شيفرة HTML لن تفسّر فإنّ رسالة الخطأ ستكون سهلة القراءة.
حلول للمشاكل الشائعة
- تخزّن معظم مخدمات HTTP مخرجات سكربتات CGI مؤقّتًا إلى حين اكتمال عمل السكربت. وهذا يعني عدم إمكانية عرض تقرير بسير عمل السكربت للعميل أثناء تنفيذ السكربت.
- راجع تعليمات التثبيت في قسم (تثبيت سكربت CGI في أنظمة يونكس).
- راجع ملفات التسجيل log files في مخدّم HTTP الذي تستخدمه (يمكن استخدام الأمر
tail -f logfile
في نافذة مستقلة). - احرص دائمًا على مراجعة السكربت والتأكد من عدم وجود أخطاء لغوية، وذلك بتنفيذ الأمر
python script.py
مثلًا. - إن لم يحتوِ السكربت على أخطاء لغوية، جرّب إضافة العبارة
import cgitb; cgitb.enable()
في بداية السكربت. - تأكد من إمكانية العثور على البرامج الخارجية عند تنفيذها، وهذا يعني عادة استخدام أسماء مسارات مطلقة. لا يمكن استخدام PATH لأنّه لا يمتلك قيمة مفيدة في سكربتات CGI.
- تأكد من أن المستخدم الذي ينفّذ السكربت الخاصّ بك قادر على القراءة والكتابة من الملفات الخارجية. يمتلك هذا المستخدم معرّفًا
userid
في مخدّم الويب الذي ينفّذ السكربت، أو يمكن أن يُعيّن المعرّف بواسطة الميزةsuexec
في مخدّم الويب. - لا تمنح سكربت CGI الوضع
set-uid
؛ لأنّ السكربت لن يعمل في معظم الأنظمة، إضافة إلى أنّ ذلك قد يتسبب في حدوث مشاكل أمنية.