關於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.properties
、validation.properties
、antisamy-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.DefaultSecurityConfiguration
中getResourceFile
方法
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()
Thread.currentThread().getContextClassLoader()
使用的都是jvm的ClassLoader,並且資原始檔都可以找到
- jetty容器啟動
ClassLoader.getSystemClassLoader()
Thread.currentThread().getContextClassLoader()
發現當前執行緒的classloader是通過jetty容器WebAppClassLoader
獲取,而並非jvm的classloader
至於此次問題怎麼發現,是源於之前隱約記得在看 JAVA類載入器
有提到過雙親模式的破壞,Tomcat的WebappClassLoader 就會先載入自己的Class,找不到再委託parent
當時看到這裡的時候也是大概看了一下,也沒深入去理解,只在腦海中停留了一下,
藉此在網上找到有位大神的講解正符合此情景,於是就仔細去拜讀了一下,加深容器類載入器的理解,避免以後再出現此類詭異的問題
Jetty原始碼閱讀—Jetty的類載入器WebAppClassLoader
另外還有一位小夥伴的分享也是不錯的,故此處記錄一下關於getSystemResource, getResource 的總結