关于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 的总结

猜你喜欢

转载自blog.csdn.net/weixin_37195606/article/details/83858901