<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          徹底干掉惡心的 SQL 注入漏洞, 一網(wǎng)打盡!

          共 5661字,需瀏覽 12分鐘

           ·

          2020-09-21 15:30

          點(diǎn)擊上方藍(lán)色“小哈學(xué)Java”,選擇“設(shè)為星標(biāo)

          回復(fù)“資源”獲取獨(dú)家整理的學(xué)習(xí)資料!

          來源:b1ngz.github.io/java-sql-injection-note/

          • 0x01簡介
          • 0x02 JDBC
            • 介紹
            • 說明
          • 0x03 Mybatis
            • 介紹
            • 說明
            • 更多場景
          • 0x04 JPA和休眠
            • 介紹
            • 說明
            • JPA

          0x01簡介

          文章主要內(nèi)容包括:

          • Java持久層技術(shù)/框架簡單介紹
          • 不同場景/框架下易導(dǎo)致SQL注入的寫法
          • 如何避免和修復(fù)SQL注入

          0x02 JDBC

          介紹

          JDBC:

          • 全稱Java數(shù)據(jù)庫連接
          • 是Java訪問數(shù)據(jù)庫的API,不依賴于特定數(shù)據(jù)庫(database-independent)
          • 所有Java持久層技術(shù)都基于JDBC

          更多請參考http://www.oracle.com/technetwork/java/javase/jdbc/index.html

          說明

          直接使用JDBC的場景,如果代碼中存在分解SQL語句,那么很有可能會(huì)產(chǎn)生注入,如

          //?concat?sql
          String?sql?=?"SELECT?*?FROM?users?WHERE?name?='"+?name?+?"'";
          Statement?stmt?=?connection.createStatement();
          ResultSet?rs?=?stmt.executeQuery(sql);

          安全的寫法是使用參數(shù)化查詢(參數(shù)化查詢),即SQL語句中使用參數(shù)綁定(?占位符)和PreparedStatement,如

          //?use???to?bind?variables
          String?sql?=?"SELECT?*?FROM?users?WHERE?name=???";
          PreparedStatement?ps?=?connection.prepareStatement(sql);
          //?參數(shù)?index?從?1?開始
          ps.setString(1,?name);

          還有一些情況,例如按名稱,列名稱排序,不能使用參數(shù)綁定,此時(shí)需要手工過濾,如通常按按順序排序,其名稱是有限的,因此可以使用白名單的方式來限制參數(shù)值

          這里需要注意的是,使用了PreparedStatement 并不意味著不會(huì)產(chǎn)生注入,如果在使用PreparedStatement之前,存在拆分sql語句,那么仍然會(huì)導(dǎo)致注入,如

          //?拼接?sql
          String?sql?=?"SELECT?*?FROM?users?WHERE?name?='"+?name?+?"'";
          PreparedStatement?ps?=?connection.prepareStatement(sql);

          看到這里,大家肯定會(huì)好奇PreparedStatement是如何防止SQL注入的,來了解一下

          正常情況下,用戶的輸入是作為參數(shù)值的,而在SQL注入中,用戶的輸入是作為SQL指令的一部分,會(huì)被數(shù)據(jù)庫進(jìn)行編譯/解釋執(zhí)行。當(dāng)使用了PreparedStatement,帶占位符(?)的sql語句只會(huì)被編譯一次,之后執(zhí)行只是將占位符替換為用戶輸入,并不會(huì)再次編譯/解釋,因此從根本上防止了SQL注入問題。

          更詳細(xì)和準(zhǔn)確的回答,請參考:

          • PreparedStatement如何避免或阻止SQL注入?
          • 如何使用Java PreparedStatement和CallableStatement修復(fù)SQL注入

          0x03 Mybatis

          介紹

          • 首個(gè)類持久性框架
          • 分為JDBC(原始SQL)和Hibernate(ORM)
          • 簡化絕大部分JDBC代碼,手工設(shè)置參數(shù)和獲取結(jié)果
          • 靈活,使用者能夠完全控制SQL,支持高級(jí)映射

          更多請參考http://www.mybatis.org/

          說明

          在MyBatis中,使用XML文件或注釋來進(jìn)行配置和映射,將接口和Java POJO(普通的舊Java對象)映射到數(shù)據(jù)庫記錄

          XML例子

          映射器界面

          @Mapper
          public?interface?UserMapper?{
          ????User?getById(int?id);
          }

          XML配置文件

          <select?id="getById"?resultType="org.example.User">
          ?SELECT?*?FROM?user?WHERE?id?=?#{id}
          select>

          注釋示例

          @Mapper
          public?interface?UserMapper?{
          ????@Select("SELECT?*?FROM?user?WHERE?id=?#{id}")
          ????User?getById(@Param("id")?int?id);
          }

          可以看到,使用者需要自己編寫SQL語句,因此當(dāng)使用不當(dāng)時(shí),會(huì)導(dǎo)致注入問題

          與使用JDBC不同的是,MyBatis使用#{}${}來進(jìn)行參數(shù)值替換

          使用#{}語法時(shí),MyBatis會(huì)自動(dòng)生成PreparedStatement,使用參數(shù)綁定(?)的方式來設(shè)置值,上述兩個(gè)示例等價(jià)的JDBC查詢代碼如下:

          String?sql?=?"SELECT?*?FROM?users?WHERE?id?=??";
          PreparedStatement?ps?=?connection.prepareStatement(sql);
          ps.setInt(1,?id);

          因此#{}可以有效防止SQL注入,詳細(xì)可參考http://www.mybatis.org/mybatis-3/sqlmap-xml.html 字符串替換部分

          而使用${}語法時(shí),MyBatis會(huì)直接注入原始字符串,即相當(dāng)于分段字符串,因此會(huì)導(dǎo)致SQL注入,如

          <select?id="getByName"?resultType="org.example.User">
          ?SELECT?*?FROM?user?WHERE?name?=?'${name}'?limit?1
          select>

          名稱估計(jì)' or '1'='1,實(shí)際執(zhí)行的語句為

          SELECT?*?FROM?user?WHERE?name?=?''?or?'1'='1'?limit?1

          因此建議盡量使用#{},但有些時(shí)候,如按語句排序,使用#{}會(huì)導(dǎo)致錯(cuò)誤,如

          ORDER?BY?#{sortBy}

          sortBy參數(shù)估計(jì)name,替換后會(huì)成為

          ORDER?BY?"name"

          即以字符串“ name”來排序,而不是按名稱排序,詳細(xì)可參考https://stackoverflow.com/a/32996866/6467552。

          這種情況就需要使用 ${}

          ORDER?BY?${sortBy}

          使用了${}后,使用者需要自行過濾輸入,方法有:

          • 代碼層使用白名單的方式,限制sortBy允許的值,如只能為nameemail變量,異常情況則設(shè)置為替換值name

          • 在XML配置文件中,使用if標(biāo)簽來進(jìn)行判斷

            Mapper接口方法

            List?getUserListSortBy(@Param("sortBy")?String?sortBy);

            xml配置文件

            <select?id="getUserListSortBy"?resultType="org.example.User">
            ??SELECT?*?FROM?user
            ??<if?test="sortBy?==?'name'?or?sortBy?==?'email'">
            ?order?by?${sortBy}
            ??if>
            select>

            因?yàn)镸ybatis不支持else,需要替換值的情況,可以使用 choose (when, otherwise)

            <select?id="getUserListSortBy"?resultType="org.example.User">
            ??SELECT?*?FROM?user
            ??<choose>
            ????<when?test="sortBy?==?'name'?or?sortBy?==?'email'">
            ??????order?by?${sortBy}
            ????when>
            ????
            ??????order?by?name
            ????

            ?choose>
            select>

          更多場景

          除了order by之外,還有一些可能會(huì)使用到${}情況,可以使用其他方法避免,如

          像語句

          如需要使用通配符(通配符%_),可以

          • 在代碼層,在參數(shù)值兩邊加上%,然后再使用#{}

          • 使用bind標(biāo)簽來構(gòu)造新參數(shù),然后再使用#{}

            Mapper接口方法

            List?getUserListLike(@Param("name")?String?name);

            xml配置文件

            <select?id="getUserListLike"?resultType="org.example.User">
            ????name="pattern"?value="'%'?+?name?+?'%'"?/>
            ????SELECT?*?FROM?user
            ????WHERE?name?LIKE?#{pattern}
            select>

            語句內(nèi)部的值為OGNL表達(dá)式,具體可參考http://www.mybatis.org/mybatis-3/dynamic-sql.html bind部分

          • 使用SQL concat()函數(shù)

            <select?id="getUserListLikeConcat"?resultType="org.example.User">
            ?SELECT?*?FROM?user?WHERE?name?LIKE?concat?('%',?#{name},?'%')
            select>

          除了注入問題之外,這里還需要對用戶的輸入進(jìn)行過濾,永久有通配符,否則在表中數(shù)據(jù)量中斷的時(shí)候,假設(shè)用戶輸入為%%,會(huì)進(jìn)行全表模糊查詢,嚴(yán)重情況下可導(dǎo)致DOS ,參考http://www.tothenew.com/blog/sql-wildcards-is-your-application-safe/

          IN條件

          使用#{}

          Mapper接口方法

          List?getUserListIn(@Param("nameList")?List?nameList);

          xml配置文件

          <select?id="selectUserIn"?resultType="com.example.User">
          ??SELECT?*?FROM?user?WHERE?name?in
          ??"name"?collection="nameList"
          ???????????open="("?separator=","?close=")">
          ????????#{name}
          ??
          select>

          具體可參考http://www.mybatis.org/mybatis-3/dynamic-sql.html foreach部分

          極限語句

          直接使用#{}即可

          Mapper接口方法

          List?getUserListLimit(@Param("offset")?int?offset,?@Param("limit")?int?limit);

          xml配置文件

          <select?id="getUserListLimit"?resultType="org.example.User">
          ?SELECT?*?FROM?user?limit?#{offset},?#{limit}
          select>

          0x04 JPA和休眠

          介紹

          JPA:

          • 全稱Java持久性API
          • ORM(對象關(guān)系映射)持久層API,需要有具體的實(shí)現(xiàn)

          更多請參考https://en.wikipedia.org/wiki/Java_Persistence_API

          休眠:

          • JPA ORM實(shí)現(xiàn)

          更多請參考http://hibernate.org/

          說明

          這里有一種錯(cuò)誤的認(rèn)識(shí),使用了ORM框架,就不會(huì)有SQL注入。而實(shí)際上,在Hibernate中,支持HQL(Hibernate查詢語言)和native sql查詢,前者存在HQL注入,封裝和之前JDBC存在相同的注入問題,來具體看一下

          高品質(zhì)

          HQL查詢例子

          Query?query?=?session.createQuery("from?User?where?name?=?'"?+?name?+?"'",?User.class);
          User?user?=?query.getSingleResult();

          這里的User為類名,和原生SQL類似,拼接會(huì)導(dǎo)致注入

          正確的用法:

          • 位置參數(shù)(位置參數(shù))
          Query?query?=?session.createQuery("from?User?where?name?=??",?User.class);
          query.setParameter(0,?name);
          • 命名參數(shù)(命名參數(shù))
          Query?query?=?session.createQuery("from?User?where?name?=?:name",?User.class);
          query.setParameter("name",?name);
          • 命名參數(shù)列表(命名參數(shù)列表)
          Query?query?=?session.createQuery("from?User?where?name?in?(:nameList)",?User.class);
          query.setParameterList("nameList",?Arrays.asList("lisi",?"zhaowu"));
          • 類實(shí)例(JavaBean)
          User?user?=?new?User();
          user.setName("zhaowu");
          Query?query?=?session.createQuery("from?User?where?name?=?:name",?User.class);
          //?User?類需要有?getName()?方法
          query.setProperties(user);

          本機(jī)SQL

          存在SQL注入

          String?sql?=?"select?*?from?user?where?name?=?'"?+?name?+?"'";
          //?deprecated
          //?Query?query?=?session.createSQLQuery(sql);
          Query?query?=?session.createNativeQuery(sql);

          使用參數(shù)綁定來設(shè)置參數(shù)值

          String?sql?=?"select?*?from?user?where?name?=?:name";
          //?deprecated
          //?Query?query?=?session.createSQLQuery(sql);
          Query?query?=?session.createNativeQuery(sql);
          query.setParameter("name",?name);

          JPA

          JPA中使用JPQL(Java持久性查詢語言),同時(shí)也支持本地sql,因此和Hibernate存在類似的問題,這里就不再細(xì)說,注意到的可以參考[如何使用Java Persistence API修復(fù)SQL注入( JPA)

          END


          有熱門推薦?

          1.?2020 年 9 月程序員工資統(tǒng)計(jì),我扯后腿了~

          2.?Spring 如何實(shí)現(xiàn) AOP,請不要再說 cglib 了!

          3.?中年架構(gòu)師,悲催的一天

          4.?在華為鴻蒙 OS 上嘗鮮,我的第一個(gè)“hello world”,起飛!

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù)?Java?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 61
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲一级黄色视频在线观看 | 最新黄色成人网站 | www.操逼 | 亚洲在线免费观看视频 | EvE依然私人订制 |