Policy文件中的属性扩展(Property Expansion)
---- 属性扩展与shell中使用的变量扩展类似,它的格式为:
"${some.property}"
实际使用的例子为:
permission java.io.FilePermission
"${user.home}", "read";
"${user.home}"的值为"d:\Project",
因此,下面的语句和上面的语句是一样的:
permission java.io.FilePermission "
d:\Project ", "read";
三. 实例
---- 当初始化Policy时,首先装载系统Policy,然后再增加用户Policy,如果两者都不存在,则使用缺省的Policy,即原始的沙箱模型。
---- 系统Policy文件的缺省位置为:
{java.home}/lib/security/java.policy (Solaris)
{java.home}\lib\security\java.policy (Windows)
用户Policy文件的缺省位置为:
{user.home}/.java.policy (Solaris)
{user.home}\.java.policy (Windows)
---- 其实,在实际使用中,我们可能不会象上面介绍的那么复杂,特别是在不使用数字签名时。这时,我们完全可以借鉴JDK 1.2提供给我们的现成的\jdk1.2\jre\lib\security\java.policy文件,根据我们的需要作相应的修改,本文就针对不使用数字签名情况详细说明安全策略文件的用法。
---- 下面,是一个完整的在Windows 95/98/NT下使用的.java.policy文件。在文件中,分别使用注释的形式说明了每个“permission”记录的用途。
// For LanServerTalk.java and LanClientTalk.java
grant {
//对系统和用户目录“读”的权限
permission java.util.PropertyPermission
"user.dir", "read";
permission java.util.PropertyPermission
"user.home", "read";
permission java.util.PropertyPermission
"java.home", "read";
permission java.util.PropertyPermission
"java.class.path", "read";
permission java.util.PropertyPermission
"user.name", "read";
//对线程和线程组的操作权限
permission java.lang.RuntimePermission
"modifyThread";
permission java.lang.RuntimePermission
"modifyThreadGroup";
//操作Socket端口的各种权限
permission java.net.SocketPermission
"-", "listen";
permission java.net.SocketPermission
"-", "accept";
permission java.net.SocketPermission
"-", "connect";
permission java.net.SocketPermission "-", "read";
permission java.net.SocketPermission "-", "write";
//读写文件的权限
permission java.io.FilePermission "-", "read";
permission java.io.FilePermission "-", "write";
//退出系统的权限,例如System.exit(0)
permission java.lang.RuntimePermission "exitVM";
};
四. java.policy文件的使用
---- 对于windows 95/98/NT,使用.java.policy文件的方法主要有下面两种。
---- 1. 使用缺省目录
---- 我们可以简单地将编辑好的.java.policy文件拷贝到windows 95/98/NT的HOME目录,这时,所有的applet(或Java应用程序)可能都拥有某些相同的权限,使用起来简单,但不灵活(例如:对于java.io.FilePermission ,其目标类的target_name必须使用绝对路径),如果不是在企业内部网中使用,还可能存在一定安全隐患。
---- 2. 在命令行中指定
---- 在命令行,如果我们希望传递一个Policy文件给appletviewer,还可以使用"-J-Djava.security.policy"参数来指定policy的位置:
appletviewer -J-Djava.security.
policy=pURL myApplet
---- pURL为Policy文件的位置。下面,是一个实际的例子,以当前目录的.java.policy文件所指定的安全策略运行当前目录的LanServerTalk.html(文件中装载并运行LanServerTalk.java):
appletviewer -J-Djava.security.policy
=.java.policy LanServerTalk.html
---- 这种方法使用灵活,特别是作为一个软件包在企业内部网中发布时,安装、设置和迁移软件,基本无须修改Policy文件的内容,使用起来相当简单,而且,安全许可的范围控制较精细。
__________________________________________________________________________________
缺省策略实现和策略文件语法
上次修改时间: 1998 年 10 月 30 日
Java 应用程序环境的策略(对不同来源的代码指定权限)由 Policy 对象来表示。更明确地说,就是由 Policy
类(包含在 java.security
包中)的实现抽象方法的 Policy
子类来表示。
Policy 对象所用策略信息的源位置由 Policy 实现决定。缺省 Policy 实现从静态策略配置文件获得自己的信息。本文档的其余部分叙述了缺省 Policy 实现及其所读取的策略文件中必须使用的语法。有关使用 Policy Tool 来创建策略文件(不必知道所需语法)的详细信息,请参阅《策略工具文档》 (for Solaris) (for Windows)。
以下是本文档其余部分的概要:
- 缺省 Policy 实现
- 缺省策略文件位置
- 更改 Policy 实现
- 策略文件语法
- 策略文件示例
- 策略文件中的属性扩展
- 相关文档
在缺省 Policy 实现中,可在一个或多个策略配置文件中指定策略。配置文件的作用是指定特定代码源的代码所能获得的权限。
可利用简单的文本编辑器或 Policy Tool 实用程序来编写策略文件。
缺省情况下,系统上只有单个全系统策略文件和唯一的(可选)用户策略文件。
首次调用缺省 Policy 对象的 getPermissions
方法或在任何时候调用 Policy 对象 refresh
方法时,即对其进行初始化。初始化包括分析策略配置文件(请参阅策略文件语法)及组装 Policy 对象。
如前所述,系统在缺省情况下具有单个全系统策略文件和唯一的用户策略文件。
系统策略文件的缺省位置为:
java.home/lib/security/java.policy (Solaris)
java.home\lib\security\java.policy (Windows)
注意: java.home 指的是名为“java.home”的系统属性的值,它指定 JDK 的安装目录。
系统策略文件可用于授予全系统代码权限。与 JDK 一起安装的 java.policy
文件可向标准扩展 (Java standard extensions) 授予全部权限,允许任何用户在无特权要求的端口进行监听,同时允许任何代码读取某些对安全不敏感的“标准”属性(例如“os.name”和“file.separator”属性)。
用户策略文件的缺省位置为:
user.home/.java.policy (Solaris)
user.home\.java.policy (Windows)
注意: user.home 指的是名为“user.home”的系统属性的值,它指定用户的主目录。在 Windows 系统中,假定用户名是 uName,“user.home”属性的缺省值为:
C:\Winnt\Profiles\uName(多用户 Windows NT 系统中)
C:\Windows\Profiles\uName(多用户 Windows 95 系统中)
C:\Windows(单用户 Windows 95 系统中)
初始化 Policy 时,将首先加载系统策略,然后在 Policy 中添加用户策略。如果两种策略均不存在,则采用内置策略。该内置策略与原始的沙箱策略相同。
策略文件的位置在安全属性文件中指定。安全属性文件的位置为:
java.home/lib/security/java.security (Solaris)
java.home\lib\security\java.security (Windows)
如上所述,java.home 指示 JDK 的安装目录。策略文件的位置被指定为其名称具有以下形式的属性的值:
policy.url.n
其中 n 为数字。应采用以下形式的语句行来指定每个属性值:
policy.url.n=URL
其中,URL 为 URL 规范。
例如,安全属性文件中将把缺省系统和用户策略文件定义为:
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy
有关利用特殊语法(例如利用 ${java.home} 来指定 java.home 属性值)来指定属性值的详细信息,请参阅属性扩展。
实际上,用户可以指定多个 URL(包括“http://”形式的 URL),从而加载所有指定的策略文件。也可注释掉或更改第二个 URL,从而禁止读取缺省用户策略文件。
该算法自 policy.url.1 开始,然后不断递增直到查不到 URL 为止。因此,如果有了 policy.url.1 和 policy.url.3,就不会读取 policy.url.3。
运行时指定其它策略文件
在执行应用程序时也可以指定附加的或不同的策略文件,方法是用“-Djava.security.policy”命令行参数来指定(该命令行参数设置 java.security.policy 属性值)。例如,如果使用
java -Djava.security.manager -Djava.security.policy=someURL SomeApp
这里 someURL 是指定策略文件位置的 URL,则除了加载安全属性文件中指定的所有策略文件外,还会加载本方法所指定的策略文件。
注意:
如果使用
java -Djava.security.manager -Djava.security.policy==someURL SomeApp
(请注意双等号),就会仅使用指定的策略文件,而安全属性文件中指出的策略文件将被忽略。
如果要将策略文件传递给 appletviewer,就应使用参数“-J-Djava.security.policy”,如下所示:
appletviewer -J-Djava.security.policy=someURL myApplet
请注意:如果将安全属性文件中的“policy.allowSystemProperty”属性设置为“false”,就会忽略“-Djava.security.policy”策略文件值(对于 java
和 appletviewer
命令)。缺省值为“true”。
可以用其它 policy 类来代替缺省 Policy 实现类,前提是前者属于抽象 Policy 类的子类并可实现 getPermissions
方法(及其它必要的方法)。
缺省 Policy 实现的更改可通过编辑安全属性文件来完成,其中安全属性文件指 JDK lib/security
目录中的 java.security
文件。
下面给出一种可在 java.security
中设置的属性类型的形式:
policy.provider=PolicyClassName
PolicyClassName 必须指定所需 Policy 实现类的完整名称。该属性的缺省安全属性文件项如下所示:
policy.provider=sun.security.provider.PolicyFile
要想自定义安全属性文件项,可通过更改属性值来指定另一个类,如下例所示:
policy.provider=com.mycom.MyPolicy
JDK 的策略配置文件可用于指定来自特定代码源的代码所能获得的权限(何种系统资源访问类型)。
为了使 applet(或在安全管理器下运行的应用程序)能够执行受保护的动作(例如读写文件),必须向 applet(或应用程序)授予进行该动作的权限。在缺省 Policy 实现中,必须由策略配置文件中的 grant 项授予该权限。有关详细信息,请参阅以下内容及 “Java 安全体系结构规范”(唯一的例外是:代码对位于与它自身同一 (URL) 位置并且对那一位置子目录下的文件总是自动拥有读权限,而无需授予明确的权限)。
策略配置文件主要包含授权项列表。其中可能包含“keystore”(密钥仓库)项及零个或多个“grant”(授权)项。
Keystore 项
keystore 是存放私钥及相关数字证书(例如验证对应的公钥的 X.509 证书链)的数据库。keytool 实用程序 (for Solaris) (for Windows) 用于创建和管理密钥仓库。策略配置文件中所指定的 keystore 用于查找在该文件的授权项中所指定的签名人公钥。如果某一授权项指定了签名人别名(请参阅以下内容),则在策略配置文件中必须含有 keystore 项。
目前,在策略文件中只能有一个 keystore 项(第一项后的其它 keystore 项将被忽略),且该项可位于文件授权项以外的任何位置。其语法如下所示:
keystore "some_keystore_url", "keystore_type";
其中“some_keystore_url”指定密钥仓库的 URL 位置,而“keystore_type”指定密钥仓库的类型。
URL 是相对于策略文件位置而言。因此,如果在安全属性文件中按以下方式指定策略文件:
policy.url.1=http://foo.bar.com/fum/some.policy
而且策略文件中含有以下项:
keystore ".keystore";
就会从下列位置加载密钥仓库:
http://foo.bar.com/fum/.keystore
URL 也可以是绝对 URL。
keystore type 定义密钥仓库信息的存储和数据格式,同时也定义用于保护密钥仓库中私钥及密钥仓库自身完整性的算法。Sun Microsystems 所支持的缺省类型是名为“JKS”的专用密钥仓库类型。因此,如果密钥仓库类型属于“JKS”,就无需在 keystore 项中加以指定。
授权项
通常认为执行代码来自于某“代码源”(由 CodeSource 类型的对象表示)。代码源不仅包含代码的源位置 (URL),而且还包括对包含与签写代码的私钥相对应的公钥的证书之引用。代码源中的证书通过用户密钥仓库中的符号别名引用。
每个授权项包括一个或多个“权限项”,前面为可选 codeBase
和 signedBy
名字/值对,用于指定要授予权限的代码。授权项的基本格式如下所示:
grant signedBy "signer_names", codeBase "URL" {
permission permission_class_name "target_name", "action",
signedBy "signer_names";
....
permission permission_class_name "target_name", "action",
signedBy "signer_names";
};
以上所有非斜体的项必须按原样出现(尽管大小写无关紧要且部分为可选项,如下所示)。 斜体项代表变量值。
授权项必须以 grant
开头。
SignedBy 和 CodeBase 域
signedBy
和 codeBase
名字/值对为可选域,其间的顺序无关紧要。
signedBy
值表示存储在密钥仓库中的证书别名。该证书内的公钥用于验证代码上的数字签名;用户可以向由私钥(私钥对应于该别名所指定的 keystore 项中的公钥)签名的代码授予权限。
signedBy
的值可以是由逗号分隔的多个别名。 例如“Adam,Eve,Charles”,其含义为“Adam,Eve 和 Charles 签名”;它们之间的关系是 AND(与)而非 OR(或)。更确切地说,“Adam 签名的代码”语句的含义是“JAR 文件中有含类文件的代码,这个 JAR 文件已用密钥仓库中别名为 Adam 的项中与公钥所对应的私钥签名”。
signedBy
域可选,这是因为如果省略该域,则表示“任何签名人”。代码是否有签名或由谁签名都没有关系。
codeBase
值表示的是代码源位置;用户可向来自该位置的代码授权。空 codeBase
项表示“任何代码”;代码来源于何处没有关系。
注意: codeBase
值是 URL,因此应该始终用正斜杠(而不要用反斜杠)作为目录分隔符,即使代码源实际在 Windows 系统上。这样,如果 Windows 系统上代码的源位置实际上是 C:\somepath\api\
,则 codeBase
策略项的外观将如下所示:
grant codeBase "file:/C:/somepath/api/" {
...
}
codeBase
值的准确含义要取决于最后的字符。后面跟着“/”的 codeBase
将匹配指定目录下的所有类文件(非 JAR 文件)。后面跟着“/*”的 codeBase
将匹配该目录下的所有文件(类文件和 JAR 文件)。后面跟着“/-”的 codeBase
将匹配该目录下的所有文件(类文件和 JAR 文件)及该目录下子目录中的所有文件。下表说明了各种不同的情况。 下载代码的 Codebase URL | 策略中的 Codebase URL | 是否匹配? |
---|
java.sun.com/people/gong/ | java.sun.com/people/gong | 是 |
java.sun.com/people/gong/ | java.sun.com/people/gong/ | 是 |
java.sun.com/people/gong/ | java.sun.com/people/gong/* | 是 |
java.sun.com/people/gong/ | java.sun.com/people/gong/- | 是 |
java.sun.com/people/gong/appl.jar | java.sun.com/people/gong/ | 否 |
java.sun.com/people/gong/appl.jar | java.sun.com/people/gong/- | 是 |
java.sun.com/people/gong/appl.jar | java.sun.com/people/gong/* | 是 |
java.sun.com/people/gong/appl.jar | java.sun.com/people/- | 是 |
java.sun.com/people/gong/appl.jar | java.sun.com/people/* | 否 |
java.sun.com/people/gong/ | java.sun.com/people/- | 是 |
java.sun.com/people/gong/ | java.sun.com/people/* | 否 |
权限项
权限项必须以 permission
开头。上述模板中的字 permission_class_name
的实际值可以是特定的权限类型(例如 java.io.FilePermission
或 java.lang.RuntimePermission
)。
"action" 对于许多权限类型而言都是必需的,例如 java.io.FilePermission
(指定允许何种类型的文件访问权限)。 对于诸如 java.lang.RuntimePermission
等权限类型则为可选项:既可以在 permission_class_name 之后的 "target_name"
值中指定权限,也可以不指定权限。
权限项的 signedBy
名字/值对为可选项。如果有名字/值对,则表示为已签名权限。意即必须由给定的别名对权限类签名,方可授予权限。例如,假定有以下授权项:
grant {
permission Foo "foobar", signedBy "FooSoft";
}
如果将 Foo.class
权限放到 JAR 文件中,且该 JAR 文件已由与 "FooSoft" 别名所指定的证书中的公钥相对应的私钥签名,或在 Foo.class
是系统类(因为系统类不受策略限制)的情况下,即可授予 Foo 权限类型。
权限项中出现的项目必须按指定顺序出现(permission
,permission_class_name,"target_name","action" 和 signedBy
"signer_names")。分号表示项终止。
大小写对于标识符(permission
、signedBy
、codeBase
等)来说并不重要,但对于 permission_class_name 或作为值传递过来的字符串而言就很重要了。
有关 Windows 系统上文件路径规范的注意事项
请注意:在指定 java.io.FilePermission
时,"target_name" 是文件路径。在 Windows 系统上,无论何时在字符串中(而不是在 codeBase URL 中)直接指定文件路径,路径中都需要两个反斜杠来代表一个实际的反斜杠,如下例所示:
grant {
permission java.io.FilePermission "C:\\users\\cathy\\foo.bat", "read";
};
原因在于:字符串是由符号处理器 (java.io.StreamTokenizer) 来处理的。符号处理器允许将“\”用作转义字符串(例如,“\n”表示换行),因此需要用两个反斜杠来表示一个反斜杠。符号处理器处理完以上文件路径字符串后,将把双反斜杠转换成单个反斜杠,其最终结果为:
"C:\users\cathy\foo.bat"
策略配置文件中两项的示例如下所示:
// 如果代码由 "Duke" 签字,则向 /tmp 中的所有文件
// 授予读/写访问权限:
grant signedBy "Duke" {
permission java.io.FilePermission "/tmp/*", "read,write";
};
// 授予所有用户以下权限:
grant { permission java.util.PropertyPermission "java.vendor"; };
另一个示例策略配置文件如下所示。
grant signedBy "sysadmin", codeBase "file:/home/sysadmin/*" {
permission java.security.SecurityPermission "Security.insertProvider.*";
permission java.security.SecurityPermission "Security.removeProvider.*";
permission java.security.SecurityPermission "Security.setProperty.*";
};
本示例规定:只有满足以下条件的代码才能调用 Security 类中的方法以添加或删除提供者或者设置 Security 属性:
- 代码将从位于本地文件系统上“/home/sysadmin/”目录下的签名 JAR 文件中加载。
- 可以用密钥仓库中别名“sysadmin”所引用的公钥来校验签名。
可以忽略代码源中两个组件的任何一个(或两者)。下面是忽略 codeBase
的示例:
grant signedBy "sysadmin" {
permission java.security.SecurityPermission "Security.insertProvider.*";
permission java.security.SecurityPermission "Security.removeProvider.*";
};
如果该策略生效,则来自 JAR 文件(由 "sysadmin" 签名)的代码可以添加/删除提供者,而不管 JAR 文件来源于何处。
下面是没有签名人的示例:
grant codeBase "file:/home/sysadmin/-" {
permission java.security.SecurityPermission "Security.insertProvider.*";
permission java.security.SecurityPermission "Security.removeProvider.*";
};
这里,来自本地文件系统“/home/sysadmin/”目录下任意位置的代码都可以添加/删除提供者。 该代码不必签名。
下面是既不含 codeBase
也不含 signedBy
的示例:
grant {
permission java.security.SecurityPermission "Security.insertProvider.*";
permission java.security.SecurityPermission "Security.removeProvider.*";
};
此处,由于两个代码源组件均被忽略,因此任何代码(不管来自于何处,是否已签名或由何人签名)都可添加/删除提供者。
策略文件和安全属性文件中可以进行属性扩展。
属性扩展类似于扩展 shell 中的变量。也就是说,当类似
${some.property}
的字符串出现在策略文件或安全属性文件中时,它将被扩展为系统属性的值。 例如,
permission java.io.FilePermission "${user.home}", "read";
将把 "${user.home}" 扩展为使用 "user.home" 系统属性的值。如果该属性的值是 "/home/cathy",则以上示例等价于:
permission java.io.FilePermission "/home/cathy", "read";
为了能在与平台无关的策略文件中使用,也可采用特殊记号 "${/}"。该记号是 "${file.separator}" 的简化表示。这种方式允许使用下列字符串:
permission java.io.FilePermission "${user.home}${/}*", "read";
如果 "user.home" 属性的值是 /home/cathy
,而且是在 Solaris 系统上,则以上字符串将转换为:
permission java.io.FilePermission "/home/cathy/*", "read";
如果 "user.home" 值是 C:\users\cathy
,而且是在 Windows 系统上,则以上字符串将转换为:
permission java.io.FilePermission "C:\users\cathy\*", "read";
同样,作为一种特例,如果扩展 codebase 中的属性,例如
grant codeBase "file:${java.home}/lib/ext/"
则任何文件分隔符都将自动转换为“/”。这样,在 Windows 系统上,以上字符串将转换为:
grant codeBase "file:C:/jdk1.2/lib/ext/"
即使 "java.home" 被设置为 C:\jdk1.2
。因此,用户就不必也不应该在 codeBase 字符串中使用 ${/}。
策略文件中允许使用双引号字符串的地方都可进行属性扩展。其中包括 "signer_names"、"URL"、"target_name" 和 "action" 域。
是否允许属性扩展由安全属性文件中的“policy.expandProperties”属性控制。如果该属性为真(缺省值),则允许扩展。
请注意:不能使用嵌套属性;嵌套属性将无效。 例如,
"${user.${foo}}"
是无效的,即使将“foo”属性设置为“home”。原因在于属性解析程序不能识别嵌套属性;解析程序只是简单地搜索第一个“${”,然后继续搜索直到找到第一个“}”为止,同时试图将搜索结果(这里是 "${user.$foo}")解释为属性。如果没有这种属性,则解析程序就会发生解释失败。
也请注意:如果在 grant 项、permission 项或 keystore 项中无法扩展某个属性,则该项将被忽略。例如,如果在没有定义系统属性“foo”的情况下使用语句:
grant codeBase "${foo}" {
permission ...;
permission ...;
};
则该 grant 项中的所有权限都将被忽略。如果使用语句:
grant {
permission Foo "${foo}";
permission Bar;
};
则将仅忽略“permission Foo...”项。最后,如果使用语句:
keystore "${foo}";
则将忽略 keystore 项。
Windows 系统、文件路径和属性的扩展
如上所述,在 Windows 系统上,当直接在字符串中(而不是在 codeBase URL 中)指定文件路径时,用户需要用两个反斜杠来代表文件路径中一个实际的反斜杠,如下例所示:
grant {
permission java.io.FilePermission "C:\\users\\cathy\\foo.bat", "read";
};
原因在于:字符串是由符号处理器 (java.io.StreamTokenizer) 来处理的。符号处理器允许将“\”用作转义字符串(例如,“\n”表示换行),因此需要用两个反斜杠来表示一个反斜杠。符号处理器处理完以上文件路径字符串后,将把双反斜杠转换成单个反斜杠,其最终结果为:
"C:\users\cathy\foo.bat"
符号处理器处理完字符串后,即进行字符串中的属性扩展。因此,如果使用字符串:
"${user.home}\\foo.bat"
则符号处理器首先处理字符串,即将双反斜杠转换成单个反斜杠,其结果为:
"${user.home}\foo.bat"
随即扩展 ${user.home} 属性,其最终结果为:
"C:\users\cathy\foo.bat"
以上假定 "user.home" 的值是 C:\users\cathy
。当然,为实现与平台无关,最好在开始指定字符串时不要显式带上斜杠,即可以用 ${/} 属性来代替,如下例所示:
"${user.home}${/}foo.bat"