跳到主要内容

访问控制策略

对于一些平台型的应用,它们可能会加载和执行第三方开发的代码。如果不对这些第三方代码加以限制,会带来安全隐患。 访问控制机制用于控制这些第三方代码能访问的函数集合。

访问策略文件

我们使用访问策略文件来方便地配置控制信息。目前只支持类级别的访问控制(将来可能会支持函数粒度的访问控制),即如果某个类不允许访问, 则不能调用该类型的任何函数,包括类构造函数(来自父类的函数如果不在限制范围,则仍然可以调用)。一个典型的策略配置文件如下:

<AccessPolicy>

<Rule id="DisableIO">
<assembly fullname="mscorlib">
<type fullname="System.IO.*"/> disable
<type fullname="System.IO.File" access="1"/> enable
</assembly>
</Rule>

<Rule id="DisableReflection">
<assembly fullname="mscorlib">
<type fullname="System.Reflection.*"/>
</assembly>
</Rule>

<Rule id="DisableHybridCLR">
<assembly fullname="HybridCLR.Runtime">
<type fullname="*"/>
</assembly>
</Rule>

<Target assembly="Tests2" accessAssemblyNotInRules="0" rules="DisableReflection,DisableIO,DisableHybridCLR"/>

</AccessPolicy>

配置规则

顶级tag为 AccessPolicy,包含0-N个Rule和Target两类配置项。

Rule

每个Rule包含多个程序集的访问控制规则。每个Rule会计算出一个限制访问的类型集合,最终的限制访问的集合为所有Rule的并集,即只要某个Rule限制访问某个类型, 则最终不允许访问这个类型。

名称类型可空描述
id属性Rule的id。字符串类型,不可为空,必须全局唯一
assembly子元素针对单个程序集的限制集合,Rule下可以包含0-N个assembly,同一个Rule下不可有同名assembly,但不同Rule间可以有同名程序集

assembly

针对单个程序集的限制规则集合,配置了禁止访问该程序集的哪些类型。

名称类型可空描述
fullname属性程序集名。字符串类型,不可为空。程序集名不包含'.dll'(例如 mscorlib)
type子元素针对单个或者一组类型的限制规则。可以有0-N个元素

同一个assembly内,如果出现多条规则都与某个类型相关,则以最后一条生效的规则为准。例如:

        <assembly fullname="mscorlib">
<type fullname="System.IO.*"/> disable
<type fullname="System.IO.File" access="1"/> enable
</assembly>

上面的例子中,尽管<type fullname="System.IO.*"/>禁止访问System.IO命名空间下的所有类型,包括System.IO.File,但接下的 <type fullname="System.IO.File" access="1"/>又单独取消了对System.IO.File的访问限制。

type

针对一个或者一组类型的限制规则。

名称类型可空描述
fullname属性类型全名或者类型通配符。如果为类型通配符时,只支持*全匹配或者xx.yy.zz.*命名空间通配,不支持xx.yy*这种前缀通配或者xx.*.yy这种任意通配。如果想匹配所有命名空间为空的类型,使用.*
access属性是否可访问,默认为false。当取 true, yes, 1 时为true,取false, no, 0时为false

Target

Target配置了程序集中代码被施加的访问限制规则。

名称类型可空描述
assembly属性访问限制的作用目标程序集,即该程序集中代码访问的函数必须满足rules中指定的访问限制规则
accessAssemblyNotInRules属性是否可以访问rules中未涉及的程序集。默认为false,当取 true, yes, 1 时为true,取false, no, 0时为false
rules属性Rule id列表,以,分割。assembly中代码访问的函数必须不在任何一个Rule的限制集合中

XmlAccessPolicyReader与BinaryAccessPolicyWriter

访问策略文件为xml格式,解析比较复杂,运行时加载有一定的开销。因此需要打包时提前将AccessPolicy.xml转为AccessPolicy.bin文件,运行时加载AccessPolicy.bin文件。

HybridCLR.Editor.Security.AccessPolicyUtil类实现了ConvertXmlAccessPolicyToBinaryAccessPolicy函数,用于将xml格式转换为bin格式。 示例代码如下:

        [MenuItem("Test/ConvertXmlAccessPolicyToBinary")]
public static void ConvertXmlAccessPolicyToBinary()
{
string accessPolicyDir = Application.dataPath + "/AccessPolicy";
AccessPolicyUtil.ConvertXmlAccessPolicyToBinaryAccessPolicy($"{accessPolicyDir}/AccessPolicy.xml",
$"{accessPolicyDir}/AccessPolicy.bytes");
}

校验AccessPolicy配置合法性

实践中很容易错误地填写了assembly.fullnametype.fullname之类的名字,导致没有正确执行期望的访问控制策略。 HybridCLR.Editor.Security.AccessPolicyConfigValidator 用于检查 AccessPolicy的合法性,避免这种错误。

函数说明
ValidateRules检查Rule规则的合法性
ValidateTargets检查Target规则的合法性

示例代码如下:

    public static void ValidateAccessPolicy()
{
var reader = new XmlAccessPolicyReader();
reader.LoadXmlFile("Assets/AccessPolicy/AccessPolicy.xml");
List<string> hotUpdateDllNames = SettingsUtil.HotUpdateAssemblyNamesExcludePreserved;
var assemblyCache = new AssemblyCache(MetaUtil.CreateHotUpdateAndAOTAssemblyResolver(EditorUserBuildSettings.activeBuildTarget, hotUpdateDllNames));
var validator = new AccessPolicyConfigValidator(assemblyCache);

var accessPolicy = reader.GetAccessPolicy();
validator.ValidateRules(accessPolicy);
validator.ValidateTargets(accessPolicy, new List<string> { "Tests2" });
}

预校验程序集是否满足AccessPolicy

Assembly.Load加载程序集时不检查程序集中是否存在非法调用,运行过程中第一次调用某函数时才检查调用该函数是否符合AccessPolicy,这带来不便。 HybridCLR.Editor.Security.AssemblyValidator用于离线预校验assembly中所有调用是否符合AccessPolicy。

示例代码如下:

        public static void ValidateAssembly()
{
var reader = new XmlAccessPolicyReader();
reader.LoadXmlFile("Assets/AccessPolicy/AccessPolicy.xml");
var validator = new AssemblyValidator(reader.GetAccessPolicy());
string test2DllPath = $"{SettingsUtil.GetHotUpdateDllsOutputDirByTarget(EditorUserBuildSettings.activeBuildTarget)}/Tests2.dll";
var mod = ModuleDefMD.Load(test2DllPath);
validator.ValidateAssembly(mod);
}

运行时设置访问策略

RuntimeApi类中提供了LoadAccessPolicy函数用于设置访问策略。运行过程中可以多次调用该函数更新访问策略。

示例代码如下:


void LoadAccessPolicy()
{
byte[] accessPolicyData = File.ReadAllBytes($"{Application.streamingAssetsPath}/AccessPolicy.bin");
RuntimeApi.LoadAccessPolicy(accessPolicyData);
}