1. 程式人生 > >關於ESAPI獲取資原始檔問題

關於ESAPI獲取資原始檔問題

近期專案中需要使用到元件包ESAPI(ESAPI是owasp提供的一套API級別的web應用解決方案),其官方網站為:https://www.owasp.org/,
有興趣的小夥伴可以瞭解一下。此處不是本文重點,本文重點記錄一下使用此元件時遇到的資源載入問題。

引入jar

<!-- https://mvnrepository.com/artifact/org.owasp.esapi/esapi -->
<dependency>
    <groupId>org.owasp.esapi</groupId>
    <artifactId>esapi</artifactId>
    <version>2.1.0.1</version>
</dependency>

問題

在使用此API時,需要引入三個資原始檔,分別是ESAPI.propertiesvalidation.propertiesantisamy-esapi.xml

so easy,直接放入專案resources目錄中的esapi下,單元測試跑起來

    @Test
    public void esapiTest() {
        String input = "<p>test <b>this</b> <script>alert(document.cookie)</script><i>right</i> now</p>"
; try { String safe2 = ESAPI.validator().getValidSafeHTML("get safe html", input, Integer.MAX_VALUE, true); logger.info("getValidSafeHTML:{}", safe2); } catch (ValidationException e) { logger.error("error", e); } }

執行結果

System property [org.owasp.esapi.opsteam] is not set
Attempting to load ESAPI.properties via file I/O.
Attempting to load ESAPI.properties as resource file via file I/O.
Not found in 'org.owasp.esapi.resources' directory or file not readable: E:\intelliGit\Tiffany\XSS\ESAPI.properties
Found in SystemResource Directory/resourceDirectory: E:\intelliGit\Tiffany\XSS\target\classes\esapi\ESAPI.properties
Loaded 'ESAPI.properties' properties file
SecurityConfiguration for Validator.ConfigurationFile.MultiValued not found in ESAPI.properties. Using default: false
Attempting to load validation.properties via file I/O.
Attempting to load validation.properties as resource file via file I/O.
Not found in 'org.owasp.esapi.resources' directory or file not readable: E:\intelliGit\Tiffany\XSS\validation.properties
Found in SystemResource Directory/resourceDirectory: E:\intelliGit\Tiffany\XSS\target\classes\esapi\validation.properties
Loaded 'validation.properties' properties file
System property [org.owasp.esapi.devteam] is not set
Attempting to load antisamy-esapi.xml as resource file via file I/O.
Not found in 'org.owasp.esapi.resources' directory or file not readable: E:\intelliGit\Tiffany\XSS\antisamy-esapi.xml
Found in SystemResource Directory/resourceDirectory: E:\intelliGit\Tiffany\XSS\target\classes\esapi\antisamy-esapi.xml
十一月 08, 2018 10:39:59 上午 org.owasp.esapi.reference.JavaLogFactory$JavaLogger log

2018-11-08 10:39:59 [main] INFO com.tiffany.xss.EsapiTest  - getValidSafeHTML:<p>test <b>this</b> <i>right</i> now</p>

ok, 大功告成,引入web專案,jetty啟動,訪問結果

Not found in 'org.owasp.esapi.resources' directory or file not readable: 
Not found in SystemResource Directory/resourceDirectory: .esapi\ESAPI.properties
Not found in 'user.home' (C:\Users\wangxinguo) directory: C:\Users\wangxinguo\esapi\ESAPI.properties

Not found in 'org.owasp.esapi.resources' directory or file not readable:
Not found in SystemResource Directory/resourceDirectory: .esapi\validation.properties
Not found in 'user.home' (C:\Users\wangxinguo) directory: C:\Users\wangxinguo\esapi\validation.properties

Not found in 'org.owasp.esapi.resources' directory or file not readable:
Not found in SystemResource Directory/resourceDirectory: .esapi\antisamy-esapi.xml
Not found in 'user.home' (C:\Users\wangxinguo) directory: C:\Users\wangxinguo\esapi\antisamy-esapi.xml


Caused by: org.owasp.esapi.errors.ConfigurationException: Couldn't find antisamy-esapi.xml
	at org.owasp.esapi.reference.validation.HTMLValidationRule.<clinit>(HTMLValidationRule.java:55)
	... 33 more
Caused by: java.io.FileNotFoundException
	at org.owasp.esapi.reference.DefaultSecurityConfiguration.getResourceStream(DefaultSecurityConfiguration.java:532)
	at org.owasp.esapi.reference.validation.HTMLValidationRule.<clinit>(HTMLValidationRule.java:53)
	... 33 more

什麼鬼?資原始檔找不到??不是已經放在classpath中了麼?

  • 難道沒打包?

    直接檢視打包war檔案,
    /webapp/WEB-INF/classes/esapi三個檔案的確存在呀,再說單元測試也可以通過呀,這不開玩笑麼?

  • 難道jetty容器解壓有毒?(不合理的懷疑)

    直接上伺服器jetty的work目錄中,/webapp/WEB-INF/classes/esapi三個檔案也的確存在

沒辦法,再看異常日誌

Not found in 'org.owasp.esapi.resources' directory or file not readable

看到可以通過指定jvm啟動環境變數來指定資原始檔的路徑

-Dorg.owasp.esapi.resources={path}

重新啟動,發現檔案解決

但是這個不是我想要的結果,我是想讓程式直接載入我classpath路徑下的資原始檔,
這樣不用在容器啟動的時候還要強制宣告resource path

那就直接看原始碼唄,直接debug走一波
org.owasp.esapi.reference.DefaultSecurityConfigurationgetResourceFile方法


public File getResourceFile(String filename) {
		logSpecial("Attempting to load " + filename + " as resource file via file I/O.");

		if (filename == null) {
			logSpecial("Failed to load properties via FileIO. Filename is null.");
			return null; // not found.
		}

		File f = null;

		// first, allow command line overrides. -Dorg.owasp.esapi.resources
		// directory
		f = new File(customDirectory, filename);
		if (customDirectory != null && f.canRead()) {
			logSpecial("Found in 'org.owasp.esapi.resources' directory: " + f.getAbsolutePath());
			return f;
		} else {
			logSpecial("Not found in 'org.owasp.esapi.resources' directory or file not readable: " + f.getAbsolutePath());
		}

        // if not found, then try the programmatically set resource directory
        // (this defaults to SystemResource directory/resourceFile
        URL fileUrl = ClassLoader.getSystemResource(resourceDirectory + "/" + filename);
        if ( fileUrl == null ) {
            fileUrl = ClassLoader.getSystemResource("esapi/" + filename);
        }

		if (fileUrl != null) {
			String fileLocation = fileUrl.getFile();
			f = new File(fileLocation);
			if (f.exists()) {
				logSpecial("Found in SystemResource Directory/resourceDirectory: " + f.getAbsolutePath());
				return f;
			} else {
				logSpecial("Not found in SystemResource Directory/resourceDirectory (this should never happen): " + f.getAbsolutePath());
			}
		} else {
			logSpecial("Not found in SystemResource Directory/resourceDirectory: " + resourceDirectory + File.separator + filename);
		}

		// If not found, then try immediately under user's home directory first in
		//		userHome + "/.esapi"		and secondly under
		//		userHome + "/esapi"
		// We look in that order because of backward compatibility issues.
		String homeDir = userHome;
		if ( homeDir == null ) {
			homeDir = "";	// Without this,	homeDir + "/.esapi"	would produce
							// the string		"null/.esapi"		which surely is not intended.
		}
		// First look under ".esapi" (for reasons of backward compatibility).
		f = new File(homeDir + "/.esapi", filename);
		if ( f.canRead() ) {
			logSpecial("[Compatibility] Found in 'user.home' directory: " + f.getAbsolutePath());
			return f;
		} else {
			// Didn't find it under old directory ".esapi" so now look under the "esapi" directory.
			f = new File(homeDir + "/esapi", filename);
			if ( f.canRead() ) {
				logSpecial("Found in 'user.home' directory: " + f.getAbsolutePath());
				return f;
			} else {
				logSpecial("Not found in 'user.home' (" + homeDir + ") directory: " + f.getAbsolutePath());
			}
		}

		// return null if not found
		return null;
	}

原始碼流程也很簡單

  • 首先通過 -Dorg.owasp.esapi.resources指定路徑獲取,如果存在,直接返回

  • 上面過程獲取不到,通過ClassLoader.getSystemResource("esapi/" + filename);來獲取,如果存在,直接返回

  • classloader也獲取不到,查詢userHome中是否存在

除錯分析

  • 單元測試啟動

ClassLoader.getSystemClassLoader()

image

Thread.currentThread().getContextClassLoader()

image

使用的都是jvm的ClassLoader,並且資原始檔都可以找到

  • jetty容器啟動

ClassLoader.getSystemClassLoader()

image

image

Thread.currentThread().getContextClassLoader()

image

image

發現當前執行緒的classloader是通過jetty容器WebAppClassLoader獲取,而並非jvm的classloader

至於此次問題怎麼發現,是源於之前隱約記得在看 JAVA類載入器
有提到過雙親模式的破壞,Tomcat的WebappClassLoader 就會先載入自己的Class,找不到再委託parent

當時看到這裡的時候也是大概看了一下,也沒深入去理解,只在腦海中停留了一下,

藉此在網上找到有位大神的講解正符合此情景,於是就仔細去拜讀了一下,加深容器類載入器的理解,避免以後再出現此類詭異的問題
Jetty原始碼閱讀—Jetty的類載入器WebAppClassLoader

另外還有一位小夥伴的分享也是不錯的,故此處記錄一下關於getSystemResource, getResource 的總結