zoukankan      html  css  js  c++  java
  • 【.net 深呼吸】限制执行代码的权限

    前面好几篇文章,老周都跟大伙伴们聊了跟应用程序域有关的话题,干脆咱们一聊到底吧,做学问就应该这样,有恒心。

    App Domain的创建新应用程序域的方法中,有一个特殊的重载:

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

    这个重载比较特殊,它与咱们今天扯的话题接近,因为它的参数列表中有一个是 PermissionSet 类型的,它表示一个权限的集合,当创建应用程序域时,通过这个权限集合,限制在新应用程序域中执行的代码的权限。在各个CreateDomain方法的重载中,只有这个有设置权限集的参数。

    运用这个重载方法,可以将你觉得要限制权限的代码放到这个新的应用程序域中执行,即创建一个“沙箱”。比如,你拿到别人写的一个类库A,不过你不知道这个类库A在执行过程中会干什么,于是,你希望在使用这个来路不明的类库时,可以限制一下它,例如不让它读写文件,或者说只允许它访问某些目录和文件。

    为了偷工减料,写出豆腐渣程序,老周举的这个例子,是把做测试用的代码写到主程序集中。它有一个类,类中有一个方法,调用之后,会在“文档”库中创建一个文件,然后写入一些见不得人的内容。

        public class Demo : MarshalByRefObject
        {
            public void WirteFile()
            {
                string myDoc = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                string filePath = Path.Combine(myDoc, Guid.NewGuid() + ".txt");
                using (FileStream fs = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read))
                {
                    byte[] data = Encoding.UTF8.GetBytes("卖假药啦。");
                    fs.Write(data, 0, data.Length);
                }
            }
        }

    然后,创建一个新的应用程序域,再在新域里面执行刚刚写好的类。之所以继承MarshalByRefObject类,是希望可以跨应用程序域调用,并且是按引用传递。

    调用如下:

                // 添加程序集凭据
                // 此处是为了装逼,才写以下几行
                // 其实你可以直接用 AppDomain.CurrentDomain.Evidence
                Evidence evd = new Evidence();
                Assembly currAss = Assembly.GetExecutingAssembly();
                Hash hsevd = new Hash(currAss);
                evd.AddAssemblyEvidence<Hash>(hsevd);
    
                // 准备相关信息
                AppDomainSetup setup = new AppDomainSetup();
                setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
    
                // 准备权限
                PermissionSet ps = new PermissionSet(PermissionState.None);
                // Execution 权限必须,不然连代码都不能执行了
                SecurityPermission secps = new SecurityPermission(SecurityPermissionFlag.Execution);
                ps.AddPermission(secps);
    
                // 创建新域
                AppDomain newAppdm = AppDomain.CreateDomain("good", evd, setup, ps);
                // 创建测试对象实例
                Demo d = (Demo)newAppdm.CreateInstanceAndUnwrap(currAss.FullName, $"{nameof(TestApp)}.{nameof(Demo)}");
                d.WriteFile();
    
                d = null;
                AppDomain.Unload(newAppdm);

    Evidence这东西比较抽象,你可以理解为程序集用来证明它是合法身份的一些信息,比如这里,我用当前程序集的 Hash 值来做为证明,因为这个 Demo 类是写在当前程序集中的(刚刚说了,为了偷工减料)。

    注意权限集合是 PermissionSet 类,它是个容器,你可以根据需要,向里面添加权限声明。在上面代码中,我加了一个最基本的安全权限——SecurityPermission,这个你得加,因为它是最最最基本的权限,如果不加,连代码都执行不了,那就等于这段代码是废的,用 SecurityPermissionFlag 枚举来描述代码应具有的基础权限,这些值可以组合使用,Execution表示允许执行代码,如果你连这个权限也不给,干脆把 Demo 类开除算了。

    顺便说一下,在new出PermissionSet 实例时,构造函数会要求一个 PermissionState值,你应该把它设置为 None,这样权限才会对代码进行限制,如果用了Unrestricted ,表示不做限制。

    调用 CreateInstanceAndUnwrap 方法时,是在新的应用程序域中创建 Demo 实例,但可以通过引用,把实例封送到当前应用程序域中,这样可以跨域调用,实际上 WriteFile 方法是在新的应用程序域中执行的。

    如果你要允许代码访问文件和目录,你必须加上FileIOPermission权限,但这里我没加,看看运行后会如何。

    因为我不给它权限,所以它没办法写入文件了。

    现在我们来改一下代码,在创建应用程序域前,向权限集合添加 FileIOPermission 权限。

                string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                FileIOPermission fps = new FileIOPermission(FileIOPermissionAccess.AllAccess, docPath);
                ps.AddPermission(fps);

    再执行一下,如果执行成功,会发现“文档”库里面多了一个文件。

    好了,今天就扯到这里了。

    示例代码下载地址

  • 相关阅读:
    JDBC 查询的三大参数 setFetchSize prepareStatement(String sql, int resultSetType, int resultSetConcur)
    有空必看
    SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换
    FusionCharts JavaScript API Column 3D Chart
    FusionCharts JavaScript API
    FusionCharts JavaScript API
    Extjs 继承Ext.Component自定义组件
    eclipse 彻底修改复制后的项目名称
    spring 转换器和格式化
    Eclipse快速生成一个JavaBean类的方法
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/6063016.html
Copyright © 2011-2022 走看看