Tomcat6安全管理SecurityManager介紹
試想一下,如果你的JSP頁面中包含一句代碼“System.exit(1);”,你的web應用訪問到該JSP時,會發生什么?
一般使用tomcat可能都沒有注意到這個問題,本篇主要講述tomcat 6中SecurityManager的管理機制,盡量使用簡單明了的圖片表示其中關系。
其他知識參考tomcat文檔翻譯。如有錯誤,請予指正。
理解java.policy
Java是一門安全性很高的語言,因此也會考慮到用戶代碼對整個系統的侵入性。 試想一下,如果你引用了一個jar包,里面包含了依據system.exit(),每次執行到這里都直接退出,會不會很蛋疼!
Java開發者肯定想過如此的問題,所以引入了java安全策略機制,利用一個配置文件來管理所有的代碼權限。
JDK中就有這樣的文件,就是 jre/lib/security/java.policy ,參考下該文件,就能理解其中的關系:
// default permissions granted to all domains grant { // Allows any thread to stop itself using the java.lang.Thread.stop() // method that takes no argument. // Note that this permission is granted by default only to remain // backwards compatible. // It is strongly recommended that you either remove this permission // from this policy file or further restrict it to code sources // that you specify, because Thread.stop() is potentially unsafe. // See the API specification of java.lang.Thread.stop() for more // information. permission java.lang.RuntimePermission "stopThread"; // allows anyone to listen on dynamic ports permission java.net.SocketPermission "localhost:0", "listen"; // "standard" properies that can be read by anyone permission java.util.PropertyPermission "java.version", "read"; permission java.util.PropertyPermission "java.vendor", "read"; permission java.util.PropertyPermission "java.vendor.url", "read"; permission java.util.PropertyPermission "java.class.version", "read"; permission java.util.PropertyPermission "os.name", "read"; permission java.util.PropertyPermission "os.version", "read"; permission java.util.PropertyPermission "os.arch", "read"; permission java.util.PropertyPermission "file.separator", "read"; permission java.util.PropertyPermission "path.separator", "read"; permission java.util.PropertyPermission "line.separator", "read"; permission java.util.PropertyPermission "java.specification.version", "read"; permission java.util.PropertyPermission "java.specification.vendor", "read"; permission java.util.PropertyPermission "java.specification.name", "read"; permission java.util.PropertyPermission "java.vm.specification.version", "read"; permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; permission java.util.PropertyPermission "java.vm.specification.name", "read"; permission java.util.PropertyPermission "java.vm.version", "read"; permission java.util.PropertyPermission "java.vm.vendor", "read"; permission java.util.PropertyPermission "java.vm.name", "read"; };
上面給出了基本的權限,例如任何人都可以監聽動態端口,以及一些讀操作。
基本過程如下面的圖所示:
用戶如果啟用了安全管理,即在執行時添加了 -Djava.security.manager , 就會 在執行某些操作前 先讀取 權限文件java.policy,檢查是否具體相應權限 。
當然也可以自己定義安全文件,一般有兩種方式:
一種是自己創建SecuirtyManager類,創建一些checkXXX的方法,進行驗證;
另一種就是創建my.policy文件(名字隨意),按照規定的語法配置權限,然后啟動時添加-Djava.security.manager-Djava.security.policy=xxxx/my.policy參數。
關于java本身的安全管理不是本篇的重點,下面介紹下tomcat中的安全策略。
理解tomcat中的Security
Tomcat中的安全管理原理基本與前面JDK中的security類似,只是啟動時需要在start后面添加-security參數,tomcat會自動讀取 conf/catalina.policy 文件中的權限配置。啟動命令如下:
F:apache-tomcat-xxx/bin/startup.bat -security
catalina.policy中默認已經配置了很多的安全策略,這里就不多說明了,下個部分會針對某一特定文件進行說明:
// Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ============================================================================ // catalina.policy - Security Policy Permissions for Tomcat 6 // // This file contains a default set of security policies to be enforced (by the // JVM) when Catalina is executed with the "-security" option. In addition // to the permissions granted here, the following additional permissions are // granted to the codebase specific to each web application: // // * Read access to its document root directory // * Read, write and delete access to its working directory // ============================================================================ // ========== SYSTEM CODE PERMISSIONS ========================================= // These permissions apply to javac grant codeBase "file:${java.home}/lib/-" { permission java.security.AllPermission; }; // These permissions apply to all shared system extensions grant codeBase "file:${java.home}/jre/lib/ext/-" { permission java.security.AllPermission; }; // These permissions apply to javac when ${java.home] points at $JAVA_HOME/jre grant codeBase "file:${java.home}/../lib/-" { permission java.security.AllPermission; }; // These permissions apply to all shared system extensions when // ${java.home} points at $JAVA_HOME/jre grant codeBase "file:${java.home}/lib/ext/-" { permission java.security.AllPermission; }; // ========== CATALINA CODE PERMISSIONS ======================================= // These permissions apply to the daemon code grant codeBase "file:${catalina.home}/bin/commons-daemon.jar" { permission java.security.AllPermission; }; // These permissions apply to the logging API // Note: If tomcat-juli.jar is in ${catalina.base} and not in ${catalina.home}, // update this section accordingly. // grant codeBase "file:${catalina.base}/bin/tomcat-juli.jar" {..} grant codeBase "file:${catalina.home}/bin/tomcat-juli.jar" { permission java.io.FilePermission "${java.home}${file.separator}lib${file.separator}logging.properties", "read"; permission java.io.FilePermission "${catalina.base}${file.separator}conf${file.separator}logging.properties", "read"; permission java.io.FilePermission "${catalina.base}${file.separator}logs", "read, write"; permission java.io.FilePermission "${catalina.base}${file.separator}logs${file.separator}*", "read, write"; permission java.lang.RuntimePermission "shutdownHooks"; permission java.lang.RuntimePermission "getClassLoader"; permission java.lang.RuntimePermission "setContextClassLoader"; permission java.util.logging.LoggingPermission "control"; permission java.util.PropertyPermission "java.util.logging.config.class", "read"; permission java.util.PropertyPermission "java.util.logging.config.file", "read"; permission java.util.PropertyPermission "catalina.base", "read"; // Note: To enable per context logging configuration, permit read access to // the appropriate file. Be sure that the logging configuration is // secure before enabling such access. // E.g. for the examples web application (uncomment and unwrap // the following to be on a single line): // permission java.io.FilePermission "${catalina.base}${file.separator} // webapps${file.separator}examples${file.separator}WEB-INF // ${file.separator}classes${file.separator}logging.properties", "read"; }; // These permissions apply to the server startup code grant codeBase "file:${catalina.home}/bin/bootstrap.jar" { permission java.security.AllPermission; }; // These permissions apply to the servlet API classes // and those that are shared across all class loaders // located in the "lib" directory grant codeBase "file:${catalina.home}/lib/-" { permission java.security.AllPermission; }; // If using a per instance lib directory, i.e. ${catalina.base}/lib, // then the following permission will need to be uncommented // grant codeBase "file:${catalina.base}/lib/-" { // permission java.security.AllPermission; // }; // ========== WEB APPLICATION PERMISSIONS ===================================== // These permissions are granted by default to all web applications // In addition, a web application will be given a read FilePermission // and JndiPermission for all files and directories in its document root. grant { // Required for JNDI lookup of named JDBC DataSource's and // javamail named MimePart DataSource used to send mail permission java.util.PropertyPermission "java.home", "read"; permission java.util.PropertyPermission "java.naming.*", "read"; permission java.util.PropertyPermission "javax.sql.*", "read"; // OS Specific properties to allow read access permission java.util.PropertyPermission "os.name", "read"; permission java.util.PropertyPermission "os.version", "read"; permission java.util.PropertyPermission "os.arch", "read"; permission java.util.PropertyPermission "file.separator", "read"; permission java.util.PropertyPermission "path.separator", "read"; permission java.util.PropertyPermission "line.separator", "read"; // JVM properties to allow read access permission java.util.PropertyPermission "java.version", "read"; permission java.util.PropertyPermission "java.vendor", "read"; permission java.util.PropertyPermission "java.vendor.url", "read"; permission java.util.PropertyPermission "java.class.version", "read"; permission java.util.PropertyPermission "java.specification.version", "read"; permission java.util.PropertyPermission "java.specification.vendor", "read"; permission java.util.PropertyPermission "java.specification.name", "read"; permission java.util.PropertyPermission "java.vm.specification.version", "read"; permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; permission java.util.PropertyPermission "java.vm.specification.name", "read"; permission java.util.PropertyPermission "java.vm.version", "read"; permission java.util.PropertyPermission "java.vm.vendor", "read"; permission java.util.PropertyPermission "java.vm.name", "read"; // Required for OpenJMX permission java.lang.RuntimePermission "getAttribute"; // Allow read of JAXP compliant XML parser debug permission java.util.PropertyPermission "jaxp.debug", "read"; // Precompiled JSPs need access to these packages. permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.el"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.runtime"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.runtime.*"; // Precompiled JSPs need access to these system properties. permission java.util.PropertyPermission "org.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER", "read"; permission java.util.PropertyPermission "org.apache.el.parser.COERCE_TO_ZERO", "read"; }; // The Manager application needs access to the following packages to support the // session display functionality. These settings support the following // configurations: // - default CATALINA_HOME == CATALINA_BASE // - CATALINA_HOME != CATALINA_BASE, per instance Manager in CATALINA_BASE // - CATALINA_HOME != CATALINA_BASE, shared Manager in CATALINA_HOME grant codeBase "file:${catalina.base}/webapps/manager/-" { permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager.util"; }; grant codeBase "file:${catalina.home}/webapps/manager/-" { permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager.util"; }; // You can assign additional permissions to particular web applications by // adding additional "grant" entries here, based on the code base for that // application, /WEB-INF/classes/, or /WEB-INF/lib/ jar files. // // Different permissions can be granted to JSP pages, classes loaded from // the /WEB-INF/classes/ directory, all jar files in the /WEB-INF/lib/ // directory, or even to individual jar files in the /WEB-INF/lib/ directory. // // For instance, assume that the standard "examples" application // included a JDBC driver that needed to establish a network connection to the // corresponding database and used the scrape taglib to get the weather from // the NOAA web server. You might create a "grant" entries like this: // // The permissions granted to the context root directory apply to JSP pages. // grant codeBase "file:${catalina.base}/webapps/examples/-" { // permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect"; // permission java.net.SocketPermission "*.noaa.gov:80", "connect"; // }; // // The permissions granted to the context WEB-INF/classes directory // grant codeBase "file:${catalina.base}/webapps/examples/WEB-INF/classes/-" { // }; // // The permission granted to your JDBC driver // grant codeBase "jar:file:${catalina.base}/webapps/examples/WEB-INF/lib/driver.jar!/-" { // permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect"; // }; // The permission granted to the scrape taglib // grant codeBase "jar:file:${catalina.base}/webapps/examples/WEB-INF/lib/scrape.jar!/-" { // permission java.net.SocketPermission "*.noaa.gov:80", "connect"; // };
這里需要注意的是其配置語法:
grant [signedBy <signer>,] [codeBase <code source>] { permission <class> [<name> [, <action list>]]; };
都要按照上面的格式進行配置。其中:
codeBase 是通過URL的方式指定文件,可以使用變量${java.home}或者${catalina.home}來表示JDK和tomcat的根目錄。
class 指定了相應的操作
[name,[,action]] name指定具體的操作或者文件,action指定可選的動作(比如read write等等)。
具體的配置樣例,可以參考上面的默認文件。
另外要說明的就是都可以配置哪些操作,也就是permission后面都可以跟哪些類,他們的作用都是什么?
上面列表中,最常用的java.io.FilePermission用于文件的操作、java.lang.RuntimePermission(可以通過禁用該權限達到防止system.exit(1)的目的)等等。
Security配置實戰
在tomcat中配置security,可以按照下面幾個步驟:
1 在樣例代碼中執行特殊權限操作:
<%@ page language="java" contentType="text/html; charset=utf-8" import="java.net.*,java.io.*" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>security Test</title> </head> <body> <h1>security Test</h1> <hr> <%! String txt2String(File file){ String result = ""; try{ BufferedReader br = new BufferedReader(new FileReader(file));//構造一個BufferedReader類來讀取文件 String s = null; while((s = br.readLine())!=null){//使用readLine方法,一次讀一行 result = result + "\n" +s; } br.close(); }catch(Exception e){ e.printStackTrace(); } return result; } %> <% //是否啟用了security,如果沒有啟用會輸入null。 System.out.println("SecurityManager: " + System.getSecurityManager()); File file = new File("C:/Users/Administrator/Desktop/test.txt"); //執行文件讀操作,即java.io.FilePermission System.out.println(txt2String(file)); //執行獲取文件屬性操作,即java.util.PropertyPermission System.out.println(System.getProperty("file.encoding")); %> </body> </html>
當訪問該頁面時,會自動執行下面的代碼,如果不具有相應的權限,會直接報錯:
//執行文件讀操作,即java.io.FilePermission System.out.println(txt2String(file)); //執行獲取文件屬性操作,即java.util.PropertyPermission System.out.println(System.getProperty("file.encoding"));
2 配置安全策略文件catalina.policy:
只需要在catalina.policy末尾添加如下的配置即可:
grant { permission java.io.FilePermission "C:/Users/Administrator/Desktop/test.txt", "read"; permission java.util.PropertyPermission "file.encoding", "read"; };
3 在命令行中添加-security啟動
訪問JSP執行代碼,樣例中訪問 http://localhost:8080/JSPTest/securityTest.jsp
可以看到控制臺正常輸出:
對比下正常啟動的輸出,SecurityManager會輸出null ( 此時,如果JSP中有system.exit(1);程序就會直接退出 ):
如果沒有配置讀寫文件的權限,會報錯(注釋掉安全配置的第一句):
grant { //permission java.io.FilePermission "C:/Users/Administrator/Desktop/test.txt", "read"; permission java.util.PropertyPermission "file.encoding", "read"; };
如果沒有配置獲取文件屬性權限,則會報錯:
grant { permission java.io.FilePermission "C:/Users/Administrator/Desktop/test.txt", "read"; //permission java.util.PropertyPermission "file.encoding", "read"; };
因此,如果在安全管理模式下,進行了越權的操作,就會報錯有的甚至直接導致程序退出。
通過報錯信息,可以快速的知道缺乏什么權限,根絕該報錯就可以方便的配置安全策略。
參考
【1】Java.security.policy文件:http://www.tmser.com/post-187.html
【2】Java安全管理器:http://bubuko.com/infodetail-306759.html
【3】tomcat 6.0 security manager:http://tomcat.apache.org/tomcat-6.0-doc/security-manager-howto.html