一文詳解應用安全防護ESAPI

本文分享自華爲雲社區《應用安全防護ESAPI》,作者: Uncle_Tom。

1. ESAPI 簡介

OWASP Enterprise Security API (ESAPI)是一個免費、開源的web應用程序安全控制庫,使程序員更容易編寫風險較低的應用程序。ESAPI庫旨在使程序員更容易對現有應用程序進行安全性改造。ESAPI庫也是新開發的堅實基礎。

考慮到特定語言的差異,所有OWASP ESAPI版本都有相同的基本設計:

  • 有一組安全控制接口。例如,定義了傳遞給安全控件類型的參數類型。
  • 每個安全控制都有一個參考實現。例如:基於字符串的輸入驗證。例如,Java 的 org.owasp.ESAPI.reference.FileBasedAuthenticator 的 ESAPI,而其他參考實現則是成熟的企業級參考實現,例如,org.oasp.ESAPI.reference.DefaultEncoder 或 org.owasp.ESAPI.reference.DefaultValidator。
  • 每個安全控件都有自己的實現(可選)。這些類中可能包含應用程序邏輯,這些邏輯可能由您的組織開發或爲您的組織而開發。例如:企業身份驗證。
  • 爲使本項目儘可能易於傳播並使更多人能夠自由自用,本項目的源代碼使用了 BSD 許可證。本項目的文檔使用了知識共享署名許可證。你可以隨意使用、修改ESAPI,甚至將它包含在商業產品中。

2. ESAPI 框架

OWASP ESAPI 已經實現下面安全控件

  • 身份認證
  • 訪問控制
  • 輸入驗證
  • 輸出編碼/轉義
  • 密碼
  • 錯誤處理和日誌
  • 通信安全
  • HTTP 安全
  • 安全配置

ESAPI 框架

ESAPI 覆蓋的OWASP Top 10

3. ESAPI 的使用

3.1. ESAPI 在 pom.xml 中的配置

目前最新的版本是: 2.5.3.1, 可以直接在Maven 庫中找到。

<!-- https://mvnrepository.com/artifact/org.owasp.esapi/esapi -->
<dependency>
  <groupId>org.owasp.esapi</groupId>
  <artifactId>esapi</artifactId>
  <version>2.5.3.1</version>
  <exclusions>
    <exclusion>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
    </exclusion>
  </exclusions>
</dependency>

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>

3.2. 注入類問題的防護

網絡安全問題很大一部分是由於注入類問題造成的。這類問題主要是由於對外部輸入使用的過程中轉碼不當造成的,例如:SQL 注入、命令注入、跨站腳本等等。

Encode(編碼器接口)包含了許多解碼輸入和編碼輸出的方法,這樣處理過的字符對於各種解釋器都是安全的。

3.2.1. 防護:XSS 跨站腳本攻擊

HTML編碼器(encodeForHTML)

@Test
void testEncodeForHTML() {
    String input = "<a href='sdfs'></a> < script > alert('xss'); </ script >";
    String encodedString = ESAPI.encoder().encodeForHTML(input);

    LOG.info("EncodeForHTML: {}", encodedString);
}

輸出:

EncodeForHTML: &lt;a href&#x3d;&#x27;xss&#x27;&gt;&lt;&#x2f;a&gt; &lt; script &gt; alert&#x28;&#x27;xss&#x27;&#x29;&#x3b; &lt;&#x2f; script &gt;

URL編碼器(encodeForURL)

@Test
void testEncodeForURL() {
    String input = "/?callback=<script>alert('xss')</script>";
    String encodedString;
    try {
        encodedString = ESAPI.encoder().encodeForURL(input);
        LOG.info("EncodeForURL: {}", encodedString);
    } catch (EncodingException e) {
        fail("Should not get exception:" + e.getMessage());
    }
}

輸出:

EncodeForURL: %2F%3Fcallback%3D%3Cscript%3Ealert%28%27xss%27%29%3C%2Fscript%3E

3.2.2. 防護:SQL 注入

@Test
void testEncodeForSQL() {
    String userId = "tom' or '1=1'";
    String sql = "select * from user where user='"
            + ESAPI.encoder().encodeForSQL(new MySQLCodec(MySQLCodec.Mode.STANDARD), userId) + "'";

    LOG.info("sql = {}", sql);
}

輸出:

sql = select * from user where user='tom\' or \'1\=1\''

3.2.3. 防護:命令注入

@Test
void testEncodeForOS() {
    String input = "dir & dir /s";
    String cmd = ESAPI.encoder().encodeForOS(new WindowsCodec(), input);

    LOG.info("cmd = {}", cmd);
}

輸出:

cmd = dir^ ^&^ dir^ ^/s

3.3. 輸入校驗問題的防護

網絡安全最大的威脅是外部輸入,所以對外部輸入的校驗對應用安全起着最大的防護作用。

3.3.1. ESAPI的輸入校驗

ESAPI有一個輸入校驗配置: validation.properties 給出了常用的校驗。

validation.properties

Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&;%\\$#_]*)?$
Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$

校驗接口

/**
  * 輸入校驗
  * 
  * @param context 校驗內容
  * @param input 校驗輸入  
  * @param type 校驗類型,對應到 validation.properties 中的類型
  * @param maxLength 輸入字符最大長度校驗
  * @param allowNull 輸入字符Null值校驗,false - 不允許;true - 允許
  * @return 校驗失敗返回 false,校驗成功返回 true
  */
boolean ESAPI.validator().getValidInput(String context, String input, String type, int maxLength, boolean allowNull);
@Test
void testValidatorEmail() {
    String input = "xxxx.com";
    if (!ESAPI.validator().isValidInput("", input, "Email", 11, false)) {
        LOG.error("Email validate fail!");
    } else {
        LOG.info("Email is validate.");
    }
}

輸出:

Email validate fail!

4. ESAPI 使用和升級過程中常遇到的問題

4.1. ExceptionInInitializerError 造成的 org.owasp.esapi.reference.DefaultEncoder CTOR threw exception

org.owasp.esapi.errors.ConfigurationException: java.lang.reflect.InvocationTargetException Encoder class (org.owasp.esapi.reference.DefaultEncoder) CTOR threw exception.
 at org.owasp.esapi.util.ObjFactory.make(ObjFactory.java:129)
 at org.owasp.esapi.ESAPI.encoder(ESAPI.java:101)
 at com.test.esapi.EsapiTest.testUpdateJulietInfo_good(EsapiTest.java:19)
 at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
 at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.lang.reflect.InvocationTargetException

 at org.owasp.esapi.util.ObjFactory.make(ObjFactory.java:86)
 ... 71 more
Caused by: java.lang.ExceptionInInitializerError
 at java.base/java.lang.Class.forName0(Native Method)
 at java.base/java.lang.Class.forName(Class.java:375)
 at org.owasp.esapi.util.ObjFactory.loadClassByStringName(ObjFactory.java:158)
 at org.owasp.esapi.util.ObjFactory.make(ObjFactory.java:81)
 at org.owasp.esapi.ESAPI.logFactory(ESAPI.java:139)
 at org.owasp.esapi.ESAPI.getLogger(ESAPI.java:155)
 at org.owasp.esapi.reference.DefaultEncoder.(DefaultEncoder.java:85)
 at org.owasp.esapi.reference.DefaultEncoder.(DefaultEncoder.java:109)
 at org.owasp.esapi.reference.DefaultEncoder.getInstance(DefaultEncoder.java:68)
 ... 76 more
Caused by: org.owasp.esapi.errors.ConfigurationException: Unable to locate resource: esapi-java-logging.properties
 at org.owasp.esapi.logging.java.JavaLogFactory.readLoggerConfiguration(JavaLogFactory.java:128)
 at org.owasp.esapi.logging.java.JavaLogFactory.(JavaLogFactory.java:96)
 ... 85 more

這個是 ESAPI 升級到 2.5.0.0 之後的版本時,最常碰到的問題。

2.5.0.0 是 ESAPI 一個重要的變動版本,我們從版本變更信息中可以看到:

  • 此版本 ESAPI 全面放棄了被 Log4J 不斷的漏洞困擾的 Log4J 的支持,轉而使用 SLF4J。如果您的 ESAPI.Logger 屬性設置爲使用 Log4J,如果不更改它,將引發模糊的 Exceptions 或 Errors,通常爲 ExceptionInInitializerError。
  • 對 AntiSamy 升級到了 1.7.0,並支持了 AntiSamy 自定義的 AntiSamy-sapi.xml 文件。
  • 從上一個版本開始,此版本僅支持 Java 8 或更高版本。

從這裏可以看到 ExceptionInInitializerError 應該是變更日誌組件造成的,熟悉的朋友會立刻想到 ESAPI 的配置文件 ESAPI.properties 裏面給出所有組件的配置。

從圖中 69 到 72 行可以看到,69 行的:

ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory

還是指向 org.owasp.esapi.logging.java.JavaLogFactory, 後面的註釋也提醒我們需要修改成 71 行的:

ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory

修改後問題解決。

4.2. java.lang.NoClassDefFoundError: javax/servlet/http/HttpServletRequest

java.lang.NoClassDefFoundError: javax/servlet/http/HttpServletRequest
 at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
 at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3402)
 at java.base/java.lang.Class.getMethodsRecursive(Class.java:3543)
 at java.base/java.lang.Class.getMethod0(Class.java:3529)
 at java.base/java.lang.Class.getMethod(Class.java:2225)
 at org.owasp.esapi.util.ObjFactory.loadMethodByStringName(ObjFactory.java:196)
 at org.owasp.esapi.util.ObjFactory.findSingletonCreateMethod(ObjFactory.java:173)
 at org.owasp.esapi.util.ObjFactory.make(ObjFactory.java:84)
 at org.owasp.esapi.ESAPI.validator(ESAPI.java:192)
 at com.huawei.hwe.esapi.EsapiTest.testEsapi_encodeForURL(EsapiTest.java:23)
 at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
 at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

Caused by: java.lang.ClassNotFoundException: javax.servlet.http.HttpServletRequest
 at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
 at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
 at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
 ... 79 more

這個問題明顯是缺少了依賴庫。我們可以查看 Maven 庫關於 ESAPI 的定義和依賴信息。
https://mvnrepository.com/artifact/org.owasp.esapi/esapi/2.5.3.1

編譯依賴

運行依賴

從運行依賴可以看到需要:javax.servlet » javax.servlet-api
點開後面的版本號:3.1.0,就可以得到 javax.servlet-api 3.1.0 的 mvn 依賴配置,將這個配置加到工程的 pom.xml 文件中就可以了。

在 pom.xml 中加入 javax.servlet-api 配置後,問題解決。

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

點擊關注,第一時間瞭解華爲雲新鮮技術~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章