كيفية القيام بما يلي: تشغيل تعليمات برمجية موثوق بها جزئياً في آلية لتحديد الصلاحيات‬‬

آلية تحديد الصلاحيات هي عملية تشغيل التعليمات البرمجية في بيئة مقيدة أمنياً، مما يحد من أذونات الوصول للتعليمات البرمجية الممنوحة لها. على سبيل المثال، إذا كان لديك مكتبة مدارة من مصدر غير موثوق به بالكامل، يجب عدم تشغيلها على سطح المكتب كما لو كان موثوقاً بها ثقة كاملة. بدلاً من ذلك، يجب وضعها في آلية لتحديد الصلاحيات لتقييد الأذونات الخاصة بها لتلك التي تتوقع أن تحتاجها; على سبيل المثال، الإذن Execution.

يمكنك أيضاً استخدام آلية تحديد الصلاحيات لاختبار التعليمات البرمجية التي ستوزعها و التي سيتم تنفيذها في وضع الثقة الجزئية في بعض البيئات.

و تعد AppDomain طريقة فعالة لتوفير حدود عازلة للتطبيقات المُدارة. مجالات التطبيق التي يتم استخدامها من أجل تشغيل تعليمات برمجية موثوق بها جزئيًا لديها الأذونات المقترنة بها و التي تعرّف الموارد المحمية المتوفرة عند التشغيل ضمن AppDomain. التعليمات البرمجية التي يتم تشغيلها داخل AppDomain محددة من قبل الأذونات المقترنة بـ AppDomain و مسموح لها فقط بالوصول إلى الموارد المحددة. يتضمن AppDomain أيضاً مصفوفة StrongName و التي تستخدم لتعريف التجميعات التي يتم تحميلها في وضع الثقة الكاملة. وهذا يمكّن مُنشئ AppDomain لبدء AppDomain جديد أو استخدم التجميعات المساعد و التي تحتاج الوصول إلى موارد محمية. و يوجد خيار آخر لتحميل التجميعات بوضع الثقة الكاملة و هو عن طريق وضعها في مخزن التجميع العمومي المؤقت; ومع ذلك، سيؤدي ذلك إلى تحميل التجميعات بوضع الثقة الكاملة في كافة مجالات التطبيق التي تم إنشاؤها على هذا الكمبيوتر. قائمة الأسماء القوية تدعم قرارات لكل AppDomain و هو يوفر التحديد بشكل أكثر تقييداً.

يمكنك استخدام التحميل الزائد للأسلوب AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) لتحديد مجموعة الأذونات للتطبيقات التي تعمل ضمن آلية تحديد الصلاحيات. يمكنّك هذا التحميل الزائد من تحديد الأذونات الممنوحة لأحد التطبيقات مما يوفر لك مستوى أمان الوصول إلى التعليمات البرمجية الذي تريده بالضبط. التجميعات التي يتم تحميلها في AppDomain باستخدام هذا التحميل الزائد يمكنها أن تحتوي إما على مجموعة الأذونات الممنوحة فقط، أو يمكنها أن تكون موثوق به ثقة كاملة. يتم منح التجميع الثقة الكاملة إذا كان موجوداً في مخزن التجميع العمومي المؤقت أو مذكورة في fullTrustAssemblies (المتغير StrongName[]). ومع ذلك، استهداف تجميع له الثقة الكاملة يتجاوز الاستخدام المقصود من ‏‫آلية تحديد الصلاحيات‬.

التحميل الزائد له التوقيع التالي:

AppDomain.CreateDomain( string friendlyName,
                        Evidence securityInfo,
                        AppDomainSetup info,
                        PermissionSet grantSet,
                        params StrongName[] fullTrustAssemblies);

متغيرات التحميل الزائد لأسلوب CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) تحدد اسم AppDomain، دليل التجميع، و كائن AppDomainSetup الذي يقوم بتعريف قاعدة التطبيق لآلية تحديد الصلاحيات، و مجموعة الأذونات المستخدمة، و الأسماء القوية للتجميعات الموثوق بها ثقة كاملة.

قاعدة التطبيق المحددة في المتغير info لا يجب أن تكون قاعدة التطبيق لتطبيق الاستضافة. و إذا كان كذلك، سيكون التجميع المستضاف قادراً على استخدام الأسلوب Load لتحميل التجميعات الأخرى في ذلك المجلد، وهو ما قد لا يكون مصمماً للكشف عن طلبات الاستدعاء من طالبي الاستدعاء الموثوق بهم جزئيًا.

للمتغير grantSet، يمكنك تحديد إما مجموعة الأذونات التي قمت بإنشائها بشكل صريح، أو أحد مجموعات الإذن المسماة مثل Internet.

و بعكس معظم تحميلات AppDomain، دليل التجميع (والذي يتم توفيره بواسطة المتغير securityInfo)يُستخدم لتحديد مجموعة الأذونات الممنوحة. و بدلاً من ذلك، يتم تحديده بشكل مستقل بواسطة المتغير grantSet. ومع ذلك، يمكن استخدام الدليل لأغراض أخرى مثل تحديد نطاق التخزين المنعزل.

لتشغيل تطبيق في ‏‫آلية تحديد الصلاحيات‬

  1. قم بإنشاء مجموعة الأذونات المراد منحها للتطبيق الغير موثوق فيه. الإذن الأدنى الذي يمكنك منحه هو الإذن Execution. يمكنك أيضاً منح أذونات إضافية كنت تعتقد أنها آمنة للتعليمات البرمجية الغير موثوق بها; على سبيل المثال، IsolatedStorageFilePermission. تنشئ التعليمات البرمجية التالية مجموعة أذونات جديدة بالإذن Execution فقط.

    PermissionSet permSet = new PermissionSet(PermissionState.None);
    permSet.AddPermission(new      SecurityPermission(SecurityPermissionFlag.Execution));
    

    و بدلاً من ذلك، يمكنك استخدام مجموعة أذونات مسماة موجودة، مثل إنترنت.

    Evidence ev = new Evidence();
    ev.AddHostEvidence(new Zone(SecurityZone.Internet));
    PermissionSet internetPS = SecurityManager.GetStandardSandbox(ev);
    

    GetStandardSandboxأسلوب ترجع أما Internetمجموعة الأذونات أو LocalIntranetتعيين الأذونات استناداً إلى المنطقة في الدليل. GetStandardSandboxأيضا إنشاء أذونات الهوية لبعض pكـsed كائنات الدليل كـ المراجع.

  2. قم بتوقيع التجميع الذي يحتوي على الفئة المستضيفة (و المسماة Sandboxer في هذا المثال) و التس تستدعي التعليمات البرمجية الغير موثوق بها. قم بإضافة StrongName المستخدمة في توقيع التجميع لمصفوفة StrongName لمتغير fullTrustAssemblies لطلب الاستدعاء CreateDomain. يجب تشغيل فئة الاستضافة بوضع الثقة الكاملة لتمكين تنفيذ التعليمات البرمجية الموثوق بها جزئياً أو لتقديم خدمات لتطبيقات موثوق بها جزئياً. و فيما يلي كيفية قراءة StrongName من التجميع:

    StrongName fullTrustAssembly = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<StrongName>();
    

    لا تملك التجميعات .NET framework مثل mscorlib و System.dll لا يجب إضافتها لقائمة الثقة الكاملة نظراً لأنه يتم تحميلها بوضع الثقة الكاملة من مخزن التجميع العمومي المؤقت.

  3. قم بتهيئة المتغير AppDomainSetup للأسلوب CreateDomain. و مع هذا المتغير، و يمكنك التحكم في العديد من الإعدادات لـ AppDomain الجديد. تعد الخاصية ApplicationBase إعداداً هاماً، و يجب أن تكون مختلفة عن الخاصية ApplicationBase لـ AppDomain الخاص بتطبيق الاستضافة. إذا كانت إعدادات ApplicationBase متشابهة، يمكن للتطبيق الموثوق به جزئياً تحميل التطبيق المضيف، بوضع الثقة الكاملة، و هو استثناء تعرفه، وبالتالي استغلاله. و يعد هذا سبباً آخر لماذا لا يوصى باستخدام catch (استثناء). إعداد قاعدة التطبيقات على المضيف بشكل مختلف عن قاعدة التطبيقات للتطبيق الذي يعمل ضمن ‏‫آلية تحديد الصلاحيات‬ يساعد على تقليل خطر استغلال الثغرات.

    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = Path.GetFullPath(pathToUntrusted);
    
  4. اطلب استدعاء التحميل الزائد للأسلوب CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) لإنشاء مجال التطبيق باستخدام المتغيرات التي حددناها.

    التوقيع لهذا الأسلوب هو:

    public static AppDomain CreateDomain(string friendlyName, 
        Evidence securityInfo, AppDomainSetup info, PermissionSet grantSet, 
        params StrongName[] fullTrustAssemblies)
    

    معلومات إضافية

    • و هذا هو التحميل الزائد الوحيد للأسلوب CreateDomain و الذي يأخذ PermissionSet كمتغير، وبالتالي هو التحميل الزائد الوحيد الذي يمكنك من تحميل تطبيق ما في إعداد الثقة الجزئية.

    • المتغير evidence لا يُستخدم لحساب مجموعة الأذونات; و لكن يستخدم للتعريف بواسطة ميزات أخرى من.NET Framework.

    • و يعد إعداد الخاصية ApplicationBase للمتغير infoإلزامي لهذا التحميل الزائد.

    • يحتوي المتغير fullTrustAssembliesعلى الكلمة الأساسية params مما يعني أنه من غير الضروري إنشاء مصفوفة StrongName. و من المسموح تمرير 0 أو 1 أو أكثر من الأسماء القوية كمتغيرات.

    • التعليمات البرمجية الخاصة بإنشاء مجال التطبيق هي:

    AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);
    
  5. قم بتحميل التعليمات البرمجية في ‏‫آلية تحديد الصلاحيات‬ AppDomain التي قمت بإنشائها. و يمكن القيام بذلك بطريقتين:

    الطريقة الثانية هي المفضلة،، و ذلك لأنها تسهّل تمرير المتغيرات لمثيل AppDomain الجديد. يوفر الأسلوب CreateInstanceFrom ميزتين هامتين:

    • يمكنك استخدام قاعدة تعليمات برمجية لتشير إلى موقع لا يحتوي على التجميع الخاص بك.

    • يمكنك القيام بالإنشاء ضمن Assert للثقة الكاملة (PermissionState.Unrestricted)، وهو الذي يمكنك من إنشاء مثيل لفئة هامة. (و يحدث هذا حينما لا يكون لدى التجميع الخاص بك علامات الشفافية ويتم تحميله بوضع الثقة الكاملة.) و لذلك، يجب أن تكون حذراً و تقوم بإنشاء فقط التعليمات البرمجية التي تثق بها باستخدام هذه الميزة، و نوصي أن تقوم بإنشاء مثيلات الفئات الموثوق بها ثقة كاملة فقط في مجال التطبيق الجديد.

    ObjectHandle handle = Activator.CreateInstanceFrom(
    newDomain, typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,
           typeof(Sandboxer).FullName );
    

    لاحظ أنه لإنشاء مثيل من فئة في مجال جديد، يجب أن تقوم الفئة بتوسيع الفئة MarshalByRefObject

    class Sandboxer:MarshalByRefObject
    
  6. قم بفك تضمين مثيل المجال الجديد إلى مرجع في هذا المجال. و يتم استخدام هذا المرجع لتنفيذ التعليمات البرمجية غير الموثوق بها.

    Sandboxer newDomainInstance = (Sandboxer) handle.Unwrap();     
    
  7. قم باستدعاء الأسلوب ExecuteUntrustedCode في المثيل AppDomain الذي أنشأته للتو.

    newDomainInstance.ExecuteUntrustedCode(untrustedAssembly, untrustedClass, entryPoint, parameters);
    

    يتم تنفيذ هذا الاستدعاء في مجال التطبيق الذي يعمل ضمن ‏‫آلية تحديد الصلاحيات‬، والتي تقوم بتقييد الأذونات.

    public void ExecuteUntrustedCode(string assemblyName, string typeName, string entryPoint, Object[] parameters)
        {
            //Load the MethodInfo for a method in the new Assembly. This might be a method you know, or 
            //you can use Assembly.EntryPoint to get to the entry point in an executable
            MethodInfo target = Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);
            try
            {
                // Invoke the method
                target.Invoke(null, parameters);
            }
            catch (Exception ex)
            {
            //When information is obtained from a SecurityException there is extra information provided if it is  
            //accessed in full-trust.
                (new PermissionSet(PermissionState.Unrestricted)).Assert();
                Console.WriteLine("SecurityException caught:\n{0}", ex.ToString());
    CodeAccessPermission.RevertAssert();
                Console.ReadLine();
            }
        }
    

    يُستخدم System.Reflection للحصول على مؤشر لأسلوب ما في تجميع موثوق به جزئيًا. يمكن استخدام المؤشر لتنفيذ التعليمات البرمجية بطريقة آمنة بالحد الأدنى من الأذونات.

    لاحظ أن Assert للحصول على إذن الثقة الكاملة قبل طباعة SecurityException.

     new PermissionSet(PermissionState.Unrestricted)).Assert()
    

    و يتم ذلك للحصول على معلومات موسعة من SecurityException. و بدون Assert، سيكتشف الأسلوب ToString الخاص بـ SecurityException وجود تعليمات برمجية موثوق بها جزئيًا في المكدس و سيقوم بتقييد المعلومات التي يتم إرجاعها. و قد يكون ذلك ثغرة أمنية إذا كان من الممكن للتعليمات البرمجية الموثوق بها جزئياً قراءة هذه المعلومات ولكن يمكن تقليل خطر ذلك بعدم منح UIPermission. يجب استخدام تأكيد الثقة الكاملة بقلة و فقط عندما تكون متأكداً أنك لا تسمح للتعليمات البرمجية الموثوق بها جزئياً من الترقية للثقة كاملة. و كقاعدة أساسية لا تقوم باستدعاء تعليمات برمجية لا تثق بها في نفس الدالة وبعد استدعاء التأكيد (assert) للثقة الكاملة. و من الجيد دائماً إعادة التأكيد (assert) بعد الانتهاء من استخدامه.

مثال

يقوم المثال التالي بتنفيذ الإجراء في المقطع السابق. و في المثال، يوجد مشروع يسمى Sandboxer في حل Visual Studio و يوجد أيضاً مشروع باسم UntrustedCode، و هو يقوم بتطبيق الفئة UntrustedClass. و يفترض هذا السيناريو أنك قمت بتحميل تجميع مكتبة يحتوي على أسلوب متوقع أن يقوم بإرجاع true أو false للإشارة إلى ما إذا كان الرقم الذي قمت بإدخاله هو رقم من متسلسلة Fibonacci. وبدلاً من ذلك، يحاول الأسلوب بقراءة ملف من جهاز الكمبيوتر الخاص بك. و يوضح المثال التالي التعليمات البرمجية الغير موثوق بها

using System;
using System.IO;
namespace UntrustedCode
{
    public class UntrustedClass
    {
        // Pretend to be a method checking if a number is a Fibonacci
        // but which actually attempts to read a file.
        public static bool IsFibonacci(int number)
        {
           File.ReadAllText("C:\\Temp\\file.txt");
           return false;
        }
    }
}

يُظهر المثال التالي التعليمات البرمجية لتطبيق Sandboxer و الذي ينفذ التعليمات البرمجية الغير موثوق بها.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security;
using System.Security.Policy;
using System.Security.Permissions;
using System.Reflection;
using System.Runtime.Remoting;

//The Sandboxer class needs to derive from MarshalByRefObject so that we can create it in another AppDomain and refer
//to it from the default AppDomain
class Sandboxer : MarshalByRefObject
{
    const string pathToUntrusted = @"..\..\..\UntrustedCode\bin\Debug";
    const string untrustedAssembly = "UntrustedCode";
    const string untrustedClass = "UntrustedCode.UntrustedClass";
    const string entryPoint = "IsFibonacci";
    private static Object[] parameters = { 45 };
    static void Main()
    {
        //Setting the AppDomainSetup. It is very important to set the ApplicationBase to a folder other than the one
        //in which the sandboxer resides
        AppDomainSetup adSetup = new AppDomainSetup();
        adSetup.ApplicationBase = Path.GetFullPath(pathToUntrusted);

        //Setting the permissions for the AppDomain. We give the permission to execute and to read/discover the location
        //where the untrusted code is loaded
        PermissionSet permSet = new PermissionSet(PermissionState.None);
        permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

        //We want the sandboxer assembly's strong name, so that we can add it to the full trust list
        StrongName fullTrustAssembly = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<StrongName>();

        //Now we have everything we need to create the AppDomain, so let's create it
        AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);

        //Use CreateInstanceFrom to load an instance of the Sandboxer class into the new AppDomain. 
        ObjectHandle handle = Activator.CreateInstanceFrom(
            newDomain, typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,
            typeof(Sandboxer).FullName
            );
        //Unwrap the new domain instance into an reference in this domain and use it to execute the untrusted code
        Sandboxer newDomainInstance = (Sandboxer) handle.Unwrap();
        newDomainInstance.ExecuteUntrustedCode(untrustedAssembly, untrustedClass, entryPoint, parameters);
    }
    public void ExecuteUntrustedCode(string assemblyName, string typeName, string entryPoint, Object[] parameters)
    {
        //Load the MethodInfo for a method in the new Assembly. This might be a method you know, or 
        //you can use Assembly.EntryPoint to get to the main function in an executable
        MethodInfo target = Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);
        try
        {
            //Now invoke the method
            bool retVal = (bool)target.Invoke(null, parameters);
        }
        catch (Exception ex)
        {
            // When we print informations from a SecurityException there is extra information that can be printed if we are 
            //calling it with a full-trust stack.
            (new PermissionSet(PermissionState.Unrestricted)).Assert();
            Console.WriteLine("SecurityException caught:\n{0}", ex.ToString());
            CodeAccessPermission.RevertAssert();
            Console.ReadLine();
        }
    }
}

راجع أيضًا:

المبادئ

إرشادات كتابة التعليمات البرمجية الآمنة