文章目錄
- JDBC 注入
- 語句拼接(Statement)
- 修復方案
- 語句拼接(PrepareStatement)
- 修復方案 預編譯
- JdbcTemplate
- 修復方案
- MyBatis
- Like 注入
- Order By 注入
- In 注入
寒假學了一個月 pwn,真心感覺這玩意太底層學的我生理不適應了,接下來學一段時間 java 安全緩一緩吧。
靶場來源:j3ers3/Hello-Java-Sec: ?? Java Security,安全編碼和代碼審計 (github.com)
bewhale/JavaSec: Java安全 學習記錄 (github.com)
JDBC 注入
語句拼接(Statement)
// 采用Statement方法拼接SQL語句,導致注入產生public String vul1(String id) {Class.forName("com.mysql.cj.jdbc.Driver");Connection conn = DriverManager.getConnection(db_url, db_user, db_pass);Statement stmt = conn.createStatement();// 拼接語句產生SQL注入String sql = "select * from users where id = '" + id + "'";ResultSet rs = stmt.executeQuery(sql);...
}
這就是原始人漏洞了,這邊我們報錯注入。
payload
1' and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1)-- +
修復方案
// 采用黑名單過濾危險字符,同時也容易誤傷(次方案)public static boolean checkSql(String content) {String[] black_list = {"'", ";", "--", "+", ",", "%", "=", ">", "*", "(", ")", "and", "or", "exec", "insert", "select", "delete", "update", "count", "drop", "chr", "mid", "master", "truncate", "char", "declare"};for (String s : black_list) {if (content.toLowerCase().contains(s)) {return true;}}return false;
}
語句拼接(PrepareStatement)
// PrepareStatement會對SQL語句進行預編譯,但如果直接采取拼接的方式構造SQL,此時進行預編譯也無用。public String vul2(String id) {Class.forName("com.mysql.cj.jdbc.Driver");Connection conn = DriverManager.getConnection(db_url, db_user, db_pass);String sql = "select * from users where id = " + id;PreparedStatement st = conn.prepareStatement(sql);ResultSet rs = st.executeQuery();
}
雖然是預編譯吧,但是沒用占位符 ?,和字符拼接沒什么區別
payload
id=2 or 1=1
修復方案 預編譯
// 正確的使用PrepareStatement可以有效避免SQL注入,使用?作為占位符,進行參數化查詢public String safe1(String id) {String sql = "select * from users where id = ?";PreparedStatement st = conn.prepareStatement(sql);st.setString(1, id);ResultSet rs = st.executeQuery();
}
JdbcTemplate
// JDBCTemplate是Spring對JDBC的封裝,如果使用拼接語句便會產生注入public Map<String, Object> vul3(String id) {DriverManagerDataSource dataSource = new DriverManagerDataSource();...JdbcTemplate jdbctemplate = new JdbcTemplate(dataSource);String sql_vul = "select * from users where id = " + id;// 安全語句// String sql_safe = "select * from users where id = ?";return jdbctemplate.queryForMap(sql_vul);
}
化石拼接語句的鍋
修復方案
// ESAPI 是一個免費、開源的、網頁應用程序安全控件庫,它使程序員能夠更容易寫出更低風險的程序
// 官網:https://owasp.org/www-project-enterprise-security-api/public String safe3(String id) {Codec<Character> oracleCodec = new OracleCodec();Statement stmt = conn.createStatement();String sql = "select * from users where id = '" + ESAPI.encoder().encodeForSQL(oracleCodec, id) + "'";ResultSet rs = stmt.executeQuery(sql);
}
MyBatis
Mybatis的SQL語句可以基于注解的方式寫在類方法上面,更多的是以xml的方式寫到xml文件。
Mybatis中SQL語句需要我們自己手動編寫或者用generator自動生成。編寫xml文件時,Mybatis支持兩種參數符號,一種是#,另一種是$。比如:
<select id="queryAll" resultMap="resultMap"> SELECT * FROM NEWS WHERE ID = #{id}</select>
使用預編譯,$使用拼接 SQL。Mybatis框架下易產生SQL注入漏洞的情況主要分為以下三種:
Like 注入
Mybatis模糊查詢:
Select * from users where username like '%#{username}%'
在這種情況下使用 # 程序會報錯,把 # 號改成 $ 可以解決
但是如果java代碼層面沒有對用戶輸入的內容做處理,那么將會產生SQL注入漏洞。
正確寫法:
Select * from users where username like concat('%',#{username}, '%')
Order By 注入
由于使用 #{} 會將對象轉成字符串(因為預編譯機制,系統將我們輸入的字符當作了一個字符串)根據字符串排序是不生效的
因此很多研發會采用${}來解決,從而造成SQL注入
POC:
id and (updatexml(1,concat(0x7e,(select user())),0))-- -
因此,此種情況下,安全的做法應當在 Java 代碼層面來進行解決。可以設置一個字段值的白名單,僅允許用戶傳入白名單內的字段。
String sort = request.getParameter("sort");
String[] sortWhiteList = {"id", "username", "password"};
if(!Arrays.asList(sortWhiteList).contains(sort)){sort = "id";
}
In 注入
在 IN 關鍵字之后使用 #{}
查詢多個參數:
<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">select * from user_table where username in (#{usernames})
</select>
in之后多個username查詢時使用 # 同樣會報錯(因為預編譯機制,系統將我們輸入的字符當作了一個字符串,因此查詢結果為空,不能滿足業務功能需求),因此很多研發會采用${}來解決,從而造成SQL注入
select * from user_table where username in (${usernames})
POC:
1,2,3) and (updatexml(1,concat(0x7e,(select user())),0))-- -
此種情況下,安全的做法應當使用 foreach 標簽:
<select id="getUserFromList" resultType="user.NewUserDO">select * from user_table where username in<foreach collection="list" item="username" open="(" separator="," close=")">#{username}</foreach>
</select>
總結一下,碰到 jdbc 的 預編譯 + 占位符 或者 mabatis 的預編譯 #{} 就不用深挖了。如果碰到 jdbc 不帶 預編譯占位符 或者 mybatis 拼接字符 ${} 的話,值得 sql 注入一試。