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"