XML 安全: 使用 XACML 控制信息访问
为适当的人提供适当的访问权限和首先拥有信息同样重要(如果不是更重要的话)。可扩展访问控制标记语言(或 XACML)提供了创建策略和规则来控制信息访问的机制。本文中,作者 Manish Verma 将继续关于 XML 问题的讨论,介绍如何在应用程序中集成 XACML。
关于 XML 安全的上一篇文章中,我讨论了安全性断言标记语言(Security Assertion Markup Language,SAML)。本文将讨论可扩展访问控制标记语言,并从上一期留下的问题开始。SAML 提供了进行验证和授权断言以及传递断言的机制,而 XACML 则提供了定义进行必要的授权决策所需规则的语言。
比如,假设这样一个场景:某一主体请求访问目标资源,策略执行点(PEP)在决定将目标资源发布给主体之前,要使用策略决策点(PDP)进行检查。访问目标资源请求的生成,以及后续的允许或拒绝访问响应,都属于 SAML 的范围。XACML 解决了 PEP 与 PDP 之间的策略决策交换问题。
访问控制 XACML
XACML 的初衷是开发一项访问控制和授权系统的标准。目前多数系统都以专有的方式实现访问控制和授权。
典型的访问控制和授权场景包括三个主要实体:主体、资源和动作以及它们的属性。主体请求得到对资源执行动作的权限。比如,访问请求“允许财务经理在财务服务器上的发票文件夹中创建文件”中,主体是“财务经理”,目标资源是“财务服务器上的发票文件夹”,动作是“创建文件”。
在专有访问控制系统中,这些实体及其属性的信息保存在资料库中。这种资料库称为访问控制链表(ACL)。不同的专有系统有不同的实现 ACL 的机制,因此难以交换和共享信息。
XACML 的目标
XACML 主要将解决以下问题:
1,创建一种可移植的、标准的方式来描述访问控制实体及其属性。
2, 提供一种机制,以比简单地拒绝访问或授权访问更细粒度的控制访问,也就是说,在“允许”或 “拒绝”之前或之后执行某些操作。
XACML 和 SAML:差别与类似之处
XACML 体系结构与 SAML 体系结构是紧密相关的。它们有很多相同的概念,要处理的问题域也在很大程度上重叠:验证、授权和访问控制。但是在同一问题域中,它们要解决的是不同的问题。SAML 要解决的是验证,并提供一种机制,在协同实体间传递验证和授权决策,而 XACML 则专注于传递这些授权决策的机制。
SAML 标准提供了允许第三方实体请求验证和授权信息的接口。内部如何处理这些授权请求则由 XACML 标准解决。XACML 不但处理授权请求,而且还定义了一种机制,来创建进行授权决策所需的规则、策略和策略集的完整基础设施。我将在 XACML 详解中详细说明这些概念。
既然 SAML 与 XACML 分享同一个领域,那么这两种规范很可能最终会合并成一个规范,希望如此。
XACML 体系结构
XACML 由图 1 中所示的多个组件组成。其中一些组件还可以与 SAML 共享。共享的组件用红线框标记。
图 1. XACML 主要组件
授权请求到达策略执行点(PEP)。PEP 创建一个 XACML 请求并发送到策略决策点(PDP),后者评估请求并返回一个响应。该响应可以是允许访问的,也可以是拒绝访问的,并具有适当的义务。本文稍后部分将解释这些义务。
PDP 评估请求中的相关策略和规则后会作出决策。可以应用的策略有多种,PDP 并没有评估所有的策略,而是根据策略目标选择相关的策略进行评估。策略目标包括关于主体、动作和其他环境属性的信息。后面的策略目标创建一节中将说明 PDP 选择评估策略的完整过程。
为了获得策略,PDP 要用到策略访问点(PAP),PAP 编写策略和策略集,供 PDP 使用。PDP 也可以调用策略信息点(PIP)服务检索与主体、资源或者环境有关的属性值。PDP 作出的授权决策被发送到 PEP。PEP 履行义务,并根据 PDP 发送的授权决策允许或拒绝访问。
XACML 详解
为了说明 XACML 的不同组件,我们将以一个具体的访问请求为例,创建所需的全部 XACML 组件。访问请求如下:主体 mverma@secf.com 属于 owner 组(主体的属性),它尝试对资源 file:///D:/Documents/Administrator/Desktop/Project Plan.html 执行 open 动作。创建所有必需的 XACML 组件之后,应该能够获得该请求的授权决策。
要记住,XACML 有三个顶层组件:策略、PEP 和 PDP。为定义的请求创建 XACML 基础设施的过程,也是紧密围绕这三个组件进行的。图 2 说明了这些组件之间的联系:
图 2. 策略语言模型
首先要创建一个策略处理请求。
XACML 策略
策略包括:一组规则、规则组合算法的标识符、一组义务和一个目标。这就是 XACML 最主要的方面。XACML 的多数动作发生在策略中。
现在说明如何创建能够处理该请求的策略,策略的范围应该比请求的范围广。您可以创建一个这样的策略:secf.com 名称空间中有电子邮件名的所有用户都可以对资源 file:///D:/Documents/Administrator/Desktop/Project Plan.html 执行任何操作。注意,策略比请求更具一般性。
规则组合算法
规则组合算法结合策略中所有规则的结果得到最终的授权决策。XACML 定义了下面的规则组合算法(也可定义自己的算法):
1,eny-overrides(拒绝覆盖): 只要有一条规则的评估为 Deny,那么最终授权决策也是 Deny。
Ordered-deny-overrides(有序拒绝覆盖): 与拒绝覆盖相同,只不过评估相关规则的顺序与将规则添加到策略中的顺序相同。
2,ermit-overrides(允许覆盖): 只要有一条规则计算的评估为 Permit,则最终授权决策也是 Permit。
3,rdered-permit-overrides(允许按顺序覆盖): 与允许覆盖相同,只不过评估相关规则的顺序与规则添加到策略中的顺序相同。
4,irst-applicable(最先应用): 遇到的第一条相关规则的评估结果作为最终授权决策的评估结果。
策略包括几种子组件:目标、规则、规则组合算法和义务。理解策略就必须理解这些子组件(subcomponent)。现在来看看各种子组件的重要性:
目标(Target):
每个策略只有一个目标。该目标有助于确定策略是否与请求有关。策略和请求的相关性决定了是否要为请求评估该策略。这是通过定义目标中的三类属性(主体、资源、动作)及其属性值来实现的。目标中不一定都有这三类属性。将这些属性的值与请求中具有相同属性的值进行比较,如果匹配(对其应用某些函数之后,将会看到它们是否匹配),则认为该策略是相关的,并对其进行评估。
规则:一个策略可以与多条规则相关联。每条规则由条件、结果和目标组成。
条件是关于属性的陈述,评估结果为 True、False 或 Indeterminate。
结果是符合规则的预期后果,值可以为 Permit 或 Deny。
目标:与策略中的目标相同,都有助于确定规则是否与请求有关,实现的机制也与策略中的目标的实现机制类似。
规则的最终结果取决于条件的评估。如果条件返回 Indeterminate,则该规则返回 Indeterminate。如果条件返回 False,则规则返回 NotApplicable。如果条件返回 True,则返回 Effect 元素的值,可以是 Permit 或 Deny。
规则组合算法:如上一条规则所述,一个策略可以包含多条规则。不同的规则有可能得到冲突的结果。规则组合算法负责解决这种冲突,每一策略、每一请求都得到一个最终结果。每个策略只能使用一种规则组合算法。详情请参阅规则组合算法。
义务:要知道,XACML 的目标之一是提供更高层次的访问控制,而不仅仅是允许和拒绝决策,义务是实现这种控制的机制。义务是 PEP 必须与授权决策的实施一起执行的动作。在评估策略之后,特定的义务与授权决策一起发送给 PEP。除了强制实施授权决策外,PEP 还负责执行义务所规定的操作。
策略的创建
这一节深入探讨策略创建代码。首先要创建 policy 对象(参见清单 1)和需要的子组件。 本文中的代码例子都使用 Sun 的 XACML 实现(请参阅参考资料)。
清单 1. 创建策略
// Create policy identifier and policy description
URI policyId = new URI("ProjectPlanAccessPolicy");
String description =
"This AccessPolicy applies to any account at secf.com "
+ "accessing file:///D:/Documents/Administrator/Desktop/Project Plan.html.";
// Rule combining algorithm for the Policy
URI combiningAlgId = new URI(OrderedPermitOverridesRuleAlg.algId);
CombiningAlgFactory factory = CombiningAlgFactory.getInstance();
RuleCombiningAlgorithm combiningAlg =
(RuleCombiningAlgorithm) (factory.createAlgorithm(combiningAlgId));
// Create the target for the policy
Target policyTarget = createPolicyTarget();
// Create the rules for the policy
List ruleList = createRules();
// Create the policy
Policy policy =
new Policy(
policyId,
combiningAlg,
description,
policyTarget,
ruleList);
// Display the policy on the std out
policy.encode(System.out, new Indenter());
清单 1 说明了 policy 的创建过程。真是这样吗?policy 创建的主要部分是子组件的创建。该清单仅仅说明了如何将这些子组件组合成 policy 对象。不过,它确实说明了 policy 的总体结构。策略的组成过程如下:
选择策略的规则组合算法。我使用了 ordered-permit-override 算法,因此将按照策略中规定的顺序评估规则。
创建策略的目标。目标创建有点复杂,它需要专门的代码清单和说明。清单 2 说明了策略的目标创建。
创建策略相关的规则。与目标相同,规则创建也是一个复杂的过程,需要专门的清单。清单 3 专用于说明规则创建。
创建所有必需的策略子组件之后,用它们创建 policy 对象。
将 policy 对象保存到文件中以便传递给 PDP。
下面几小节说明策略子组件的创建。首先是目标创建,最后以规则创建和相关条件结束关于策略创建的讨论。
策略目标创建
目标创建是创建策略的一个重要方面。目标是为评估请求选择相关策略的机制。为了创建策略目标,需要定义属于主体、资源和动作这三种类型的实体的属性和属性值。当 PDP 评估请求时,它将查找目标属性值与请求中的属性值相同的策略。
然后将说明如何通过编程来实现这些目标。XACML 提供了一种称为 AttributeDesignator 的机制,用它来比较请求与策略目标中的属性值。
利用 AttributeDesignator,可以通过定义属性名和类型来指定一个属性。此外,还可以指定属性值,也就是在目标中规定与请求中的属性值进行比较的那个值。要进行比较,需要选择一个预置的函数。该函数以及 AttributeDesignator 都可以用来创建 TargetMatch 对象。您可以创建多个 TargetMatch 对象,每个对象对应一个属性。同一类的所有 TargetMatch 对象放在一个列表中,并传递给 Target 对象。这个 Target 对象就是用来创建 Policy 的目标对象。
策略的形式应该如下所示:“允许 secf.com 名称空间中列有电子邮件名的任何用户对资源 file:///D:/Documents/Administrator/Desktop/Project Plan.html 执行任何动作。”对于该策略,可以创建包含两个属性的目标,分别用于主体和资源。不需要为动作创建属性,因为要创建的策略对动作没有任何限制。
清单 2 说明了策略目标的创建。因为要比较请求中主体电子邮件字段的值和目标中规定的字段的值,因此对于主体,可以在目标中指定属性 urn:oasis:names:tc:xacml:1.0:data-type:rfc822Name(电子邮件 ID)。在目标中定义属性类型(urn:oasis:names:tc:xacml:1.0:data-type:rfc822Name)、属性名(urn:oasis:names:tc:xacml:1.0:subject:subject-id)和比较函数(urn:oasis:names:tc:xacml:1.0:function:rfc822Name-match)。 该函数将目标中规定的属性值与请求中具有相同属性的值进行比较。资源类型 http://www.w3.org/2001/XMLSchema#anyURI 的属性也是按照类似的步骤进行比较的。
清单 2. 创建策略目标
public static Target createPolicyTarget() throws URISyntaxException {
List subjects = new ArrayList();
List resources = new ArrayList();
// Attributes of Subject type
// Multiple subject attributes can be specified. In this
// case only one is being defined.
List subject = new ArrayList();
URI subjectDesignatorType =
new URI("urn:oasis:names:tc:xacml:1.0:data-type:rfc822Name");
URI subjectDesignatorId =
new URI("urn:oasis:names:tc:xacml:1.0:subject:subject-id");
// Match function for the subject-id attribute
String subjectMatchId =
"urn:oasis:names:tc:xacml:1.0:function:rfc822Name-match";
AttributeDesignator subjectDesignator =
new AttributeDesignator(
AttributeDesignator.SUBJECT_TARGET,
subjectDesignatorType,
subjectDesignatorId,
false);
StringAttribute subjectvalue = new
StringAttribute("secf.com");
// get the factory that handles Target functions
FunctionFactory factory =
FunctionFactory.getTargetInstance();
// get an instance of the right function for matching
// subject attributes
Function subjectFunction =
factory.createFunction(subjectMatchId);
TargetMatch subjectMatch = new TargetMatch(
TargetMatch.SUBJECT,
subjectFunction,
subjectDesignator,
subjectvalue);
subject.add(subjectMatch);
// Attributes of resource type
// Multiple resource attributes can be specified. In this
// case only one is being defined.
List resource = new ArrayList();
URI resourceDesignatorType =
new URI("http://www.w3.org/2001/XMLSchema#anyURI");
URI resourceDesignatorId =
new URI("urn:oasis:names:tc:xacml:1.0:resource:resource-id");
// Match function for the resource-id attribute
String resourceMatchId =
"urn:oasis:names:tc:xacml:1.0:function:anyURI-equal";
AttributeDesignator resourceDesignator =
new AttributeDesignator(
AttributeDesignator.RESOURCE_TARGET,
resourceDesignatorType,
resourceDesignatorId,
false);
AnyURIAttribute resourcevalue =
new AnyURIAttribute(
new URI("file:///D:/Documents/Administrator/Desktop/Project Plan.html"));
// Get an instance of the right function for matching
// resource attribute
Function resourceFunction =
factory.createFunction(resourceMatchId);
TargetMatch resourceMatch = new TargetMatch(
TargetMatch.RESOURCE,
resourceFunction,
resourceDesignator,
resourcevalue);
resource.add(resourceMatch);
// Put the subject and resource sections into their lists
subjects.add(subject);
resources.add(resource);
// Create and return the new target. No action type
// attributes have been specified in the target
return new Target(subjects, resources, null);
}
规则的创建
规则是策略中最重要的子组件。如前所述,规则实质上是评估 Permit、Deny、Indeterminate 或 NotApplicable 的条件。
继续创建处理授权请求(请参阅“XACML 详解”中的第一张图片)所需要的 XACML 组件,现在需要创建适当的规则。创建规则实质上就是创建相应的条件,如清单 3 所示。查看代码,可以看到它完成了以下三项操作:
创建规则目标。
定义规则结果。
创建条件。
其中的两个:创建规则目标和定义规则结果,非常简单。第三点创建条件比较复杂,后面还将详细说明。
要创建的符合该请求的规则应该是:“如果主体 mverma@secf.com 属于组 owner,并尝试打开资源 file:///D:/Documents/Administrator/Desktop/Project Plan.html,那么允许访问。” 创建这样的规则,首先要创建目标,以便针对将处理的请求评估规则。创建规则目标的方式类似于策略目标的创建方式。然后,将规则的 effect 定义为 Permit。为了让规则返回 effect 的值,关联条件必须返回 True。清单 4 中的代码说明了条件的创建。
清单 3. 创建规则
public static List createRules() throws URISyntaxException {
// Step 1: Define the identifier for the rule
URI ruleId = new URI("ProjectPlanAccessRule");
String ruleDescription = “Rule for accessing project plan";
// Step 2: Define the effect of the rule
int effect = Result.DECISION_PERMIT;
// Step 3: Get the target for the rule
Target target = createRuleTarget();
// Step 4: Get the condition for the rule
Apply condition = createRuleCondition();
// Step 5: Create the rule
Rule openRule = new Rule(ruleId, effect,ruleDescription, target, condition);
// Create a list for the rules and add the rule to it
List ruleList = new ArrayList();
ruleList.add(openRule);
return ruleList;
}
AttributeDesignator 返回值
AttributeDesignator 可能为一个属性返回多个值(因为请求可能包含多个匹配)。为此,XACML 有一种称为袋子(bag)的特殊数据类型。袋子是允许重复的无序集合。AttributeDesignator 总是返回袋子,即使只有一个返回值。一旦返回一个袋子,就将对袋子中的值与预期值进行比较,以便作出授权决策。对于目标,不需要使用函数从袋子中提取任何值,但是对于条件,却需要这样做。这是因为,对于目标,PDP 会自动为 AttributeDesignator 返回的每个元素应用匹配的函数。
清单 4 说明了规则条件的创建。与为策略目标创建 AttributeDesignator 的方式基本相同,也需要创建 AttributeDesignator 对象,比较请求中的属性值和条件中规定的值。在这里,我们感兴趣的属性是主体所属的 group。如果要让条件返回 True,那么 group 属性的值必须是 owner。
惟一要补充的是,必须满足以下条件:使用函数从 AttributeDesignator 返回的多个值中提取一个值。该例中使用函数 urn:oasis:names:tc:xacml:1.0:function:string-one-and-only。关于处理 AttributeDesignator 中的多个值的更多信息,请参阅 AttributeDesignator 返回值。
正如从清单 4 中可以看到的,需要创建 AttributeDesignator 对象来定义希望与请求中的值进行比较的属性。比较算法是用来比较属性值的。然后要定义可以从 AttributeDesignator 返回的那些值中选出一个值的函数。最后创建 Apply 对象,它类似于目标中创建的 TargetMatch 对象。Apply 对象的目的是对从袋子(由 AttributeDesignator 返回)中选择的值应用比较函数,将它与条件中规定的值进行比较。
清单 4. 创建规则条件
public static Apply createRuleCondition() throws URISyntaxException {
List conditionArgs = new ArrayList();
// Define the name and type of the attribute
// to be used in the condition
URI designatorType = new URI("http://www.w3.org/2001/XMLSchema#string");
URI designatorId = new URI("group");
// Pick the function that the condition uses
FunctionFactory factory = FunctionFactory.getConditionInstance();
Function conditionFunction = null;
try {
conditionFunction =
factory.createFunction(
"urn:oasis:names:tc:xacml:1.0:function:" + "string-equal");
} catch (Exception e) {
return null;
}
// Choose the function to pick one of the
// multiple values returned by AttributetDesignator
List applyArgs = new ArrayList();
factory = FunctionFactory.getGeneralInstance();
Function applyFunction = null;
try {
applyFunction =
factory.createFunction(
"urn:oasis:names:tc:xacml:1.0:function:"
+ "string-one-and-only");
} catch (Exception e) {
return null;
}
// Create the AttributeDesignator
AttributeDesignator designator =
new AttributeDesignator(
AttributeDesignator.SUBJECT_TARGET,
designatorType,
designatorId,
false
null);
applyArgs.add(designator);
// Create the Apply object and pass it the
// function and the AttributeDesignator. The function
// picks up one of the multiple values returned by the
// AttributeDesignator
Apply apply = new Apply(applyFunction, applyArgs, false);
// Add the new apply element to the list of inputs
// to the condition along with the Attributevalue
conditionArgs.add(apply);
StringAttribute value = new StringAttribute("owner");
conditionArgs.add(value);
// Finally, create and return the condition
return new Apply(conditionFunction, conditionArgs, true);
}
这样,策略创建任务就完成了。我们简单回顾一下策略创建过程。首先,要创建必要的策略子组件:策略目标、规则和规则组合算法。不用为策略创建义务,因为它们是可选的。创建规则实质上意味着创建规则目标和条件,这些都使用它们自己的代码清单作了说明。这里使用的所有的策略子组件都是用来创建策略的。
创建处理授权请求所需 XACML 组件的下一步是创建 PEP。PEP 的作用是什么呢?它为您创建的所有这些 XACML 组件创建授权请求!
策略实施点(PEP)
PEP 根据请求者的属性、请求的资源、动作和其他信息创建请求。在这里,我将展示创建请求的机制。为了方便起见,我将再次使用该请求:主体 mverma@secf.com 属于 owner 组(主体的属性),并尝试对资源 file:///D:/Documents/Administrator/Desktop/Project Plan.html 执行 open 动作。要创建这样的请求,您需要两个主体属性、一个资源属性和一个动作属性。两个主体属性是 rfc822Name(电子邮件 ID)和主体所属的组。资源属性是资源的 URI,动作属性是打开资源的动作。清单 5 说明了 PEP 和所有这些属性的创建。
现在,您已经看到了策略的创建和 PEP 请求的生成,剩下的只有创建 PDP 了。
策略决策点(PDP)
PDP 针对策略评估请求,并返回响应。
因为 XACML 规范没有规定把策略和请求传递给 PDP 以便评估请求的具体机制,所以可以选择您认为方便的任何机制。该例中,策略和请求是作为命令行参数传递给 PDP。
注意,PDP 仍然基于需要服务的请求类型。它是一种通用组件,接受任何请求和相关的策略集合,并为可用的策略评估请求。清单 6 说明了如何创建 PDP,以及如何用它来评估来自 PEP 的请求。PDP 创建和请求评估的详细过程将在清单 6 后介绍。
清单 6. 创建 PDP
public static void main(String[] args) throws Exception {
if (args.length < 2) {
System.out.println("Usage: <request> <AccessPolicy> [policies]");
System.exit(1);
}
// Step 1: Get the request and policy file from the command line
String requestFile = null;
requestFile = args[0];
String[] policyFiles = new String[args.length - 1];
for (int i = 1; i < args.length; i++)
policyFiles[i - 1] = args[i];
// Step 2: Create a PolicyFinderModule and initialize it
// Use the sample FilePolicyModule, which
// is configured using the policies from the command line
FilePolicyModule filePolicyModule = new FilePolicyModule();
for (int i = 0; i < policyFiles.length; i++)
filePolicyModule.addPolicy(policyFiles[i]);
// Step 3: Set up the PolicyFinder that this PDP will use
//
PolicyFinder policyFinder = new PolicyFinder();
Set policyModules = new HashSet();
policyModules.add(filePolicyModule);
policyFinder.setModules(policyModules);
// Step 4: Create the PDP
PDP pdp = new PDP(new PDPConfig(null, policyFinder, null));
// Get the request send by the PEP
RequestCtx request =
RequestCtx.getInstance(new FileInputStream(requestFile));
// Step 5: Evaluate the request. Generate the response.
ResponseCtx response = pdp.evaluate(request);
// Display the output on std out
response.encode(System.out, new Indenter());
}
下面将一步步地说明 PDP 的创建和请求评估:
从命令行参数获得请求和策略文件。
创建 PolicyFinderModule,并使用 FilePolicyModule 对象将其初始化。使用命令行中的策略配置 FilePolicyModule。PolicyFinderModule 是下面三个发现程序模块(finder module)之一:
PolicyFinderModule
AttributeFinderModule
ResourceFinderModule
这些模块是 SUN XACML 实现用来发现属性、策略和资源的机制。
设置 PDP 将使用的策略发现程序。
通过初始化 PDP 类来创建 PDP。把使用 FilepolicyModule 配置的 PDPConfig 对象提供给 PDP 构造函数。
为 PDP 调用 evaluate 方法评估来自 PEP 的请求,这将返回授权决策。如果完全按照本文所述创建 XACML 组件,PDP 将提供 Permit 授权决策。
授权决策传回 PEP。可以选择把决策发回 PEP 的机制。
到此为止,处理请求需要创建的三种主要 XACML 组件 —— 策略、PEP 和 PDP —— 都已经介绍完毕,并给出了相应的例子。
结束语
访问控制几乎是所有应用程序或多或少都要用到的一个领域。XACML 是将该领域标准化的一次尝试。虽然 XACML 是一种冗长的语言,但是一旦掌握了其基本概念和语言流,就很容易构造访问控制机制。
本文介绍了创建基本的 XACML 组件的过程:
策略,包括规则和策略目标
PEP
PDP
掌握这些知识之后,就可以在当前和将来的应用程序中采用 XACML 处理访问控制。