使用阿里 Druid 實現應用級waf

国产大熊猫~發表於2024-06-01

Druid內建了語義級的waf,為何我們要把waf能力整合到應用中呢?

1)流量waf還是存在各種解析差異導致的bypass,類似於該 例子

2)k8s的逐漸流行,在應用中實現waf能力能減少架構層設計負擔

缺點:

1)需要應用安全工程師不間斷配合

2)對程式碼質量較好的開發團隊會顯得多此一舉

本次使用springboot2、mysql5.7、Druid12.23

先看看輸入介面

@RequestMapping("list2")
public User getList2(String name) {
    if (name == null) {
        name = "dato";
    }
    String x = CheckSqlInjection(name);
    if (x != null) {
        User u = new User();
        u.setName(x);
        return u;
    }
    //@Select("select * from user where name='${name}'")
    return userMapper.getUserByName3(name);
}

其中 CheckSqlInjection 內容就是呼叫Druid進行語義分析的邏輯

public static String CheckSqlInjection(String sql) {
       if (sql == null || sql.length() < 6) {
           return null;
       }
       //首先開展基礎的sql正規表示式檢查
       //正規表示式檢查
       //.......正則檢查開始..........
       //正規表示式檢查結束

       //啟動druid檢查
       WallConfig config = new WallConfig("/");
       MySqlWallProvider newSqlWallProvider = new MySqlWallProvider(config);
       //入參型別包含無符號、單引號、雙引號
       //也隱藏包含了 SELECT `id`,`uid` from x WHERE `id` LIKE '%2%' 這種like注入
       List < String > quotes = Arrays.asList("", "'", "\"");
       String checksql;
       String re;
       for (String quote: quotes) {
           checksql = "select id from x where id = " + quote + sql + quote;
           WallCheckResult check_quote = newSqlWallProvider.check(checksql);
           re = Violation_Message(checksql, check_quote);
           if (re != null) {
               return re;
           }
       }

       //order by注入的情況下進行校驗,order by情況下無單引號或雙引號
       //似乎和上面的無符號情況一致,可以省略?
       /*
       checksql = "select id,uid from x order by " + sql;
       WallCheckResult check_order_by = newSqlWallProvider.check(checksql);
       re = Violation_Message(checksql, check_order_by);
       if (re != null) {
           return re;
       }
       */

       //sql的in語句也可以形成sql注入,也需要小心
       checksql = "Select id,uid,pid from x where id in (" + sql + ")";
       WallCheckResult check_in = newSqlWallProvider.check(checksql);
       re = Violation_Message(checksql, check_in);
       if (re != null) {
           return re;
       }
       return null;
   }

   private static String Violation_Message(String Sql, WallCheckResult check) {
       List < Violation > violations = check.getViolations();
       if (!violations.isEmpty()) {
           int firstViolation = violations.get(0).getErrorCode();
           //1001是sql語法不正確、未知錯誤為9999,它倆應忽略掉,因為正常的使用者輸入都不可能是sql注入
           //錯誤碼在druid/wall/violation/ErrorCode.java之中
           if (firstViolation != 1001 && firstViolation != 9999) {
               //System.out.println("sqlAtt&ck: " + violations.get(0).getMessage() + " : " + Sql + "\n---------");
               return violations.get(0).getMessage();
           }
       }
       return null;
   }

透過sqlmap不斷的測試發現Druid存在bug,無法解析RLIKE注入已經提交給官方,issues地址

所以也將sqlmap中涉及到root使用者注入的敏感庫和函式都放到了黑名單之中

使用阿里 Druid 實現應用級waf

總結:

1)這個庫很成熟了,除了RLIKE這個bug,已經可以完全在輸入時就完成語義sql注入校驗

2)整體沒有什麼難度,可以很快就復現傳達給同事

相關文章