Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
ava識(shí)堂,一個(gè)高原創(chuàng),高收藏,有干貨的微信公眾號(hào),一起成長(zhǎng),一起進(jìn)步,歡迎關(guān)注
關(guān)系型數(shù)據(jù)庫(kù)最難的地方,就是建模(model)。
錯(cuò)綜復(fù)雜的數(shù)據(jù),需要建立模型,才能儲(chǔ)存在數(shù)據(jù)庫(kù)。所謂"模型"就是兩樣?xùn)|西:實(shí)體(entity)+ 關(guān)系(relationship)。
實(shí)體指的是那些實(shí)際的對(duì)象,帶有自己的屬性,可以理解成一組相關(guān)屬性的容器。關(guān)系就是實(shí)體之間的聯(lián)系,通常可以分成"一對(duì)一"、"一對(duì)多"和"多對(duì)多"等類型。
在關(guān)系型數(shù)據(jù)庫(kù)里面,每個(gè)實(shí)體有自己的一張表(table),所有屬性都是這張表的字段(field),表與表之間根據(jù)關(guān)聯(lián)字段"連接"(join)在一起。所以,表的連接是關(guān)系型數(shù)據(jù)庫(kù)的核心問(wèn)題。
表的連接分成好幾種類型。
內(nèi)連接(inner join)外連接(outer join)左連接(left join)右連接(right join)全連接(full join)
以前,很多文章采用維恩圖(兩個(gè)圓的集合運(yùn)算),解釋不同連接的差異。
上周,我讀到一篇文章,認(rèn)為還有比維恩圖更好的解釋方式。我發(fā)現(xiàn)確實(shí)如此,換一個(gè)角度解釋,更容易懂。
所謂"連接",就是兩張表根據(jù)關(guān)聯(lián)字段,組合成一個(gè)數(shù)據(jù)集。問(wèn)題是,兩張表的關(guān)聯(lián)字段的值往往是不一致的,如果關(guān)聯(lián)字段不匹配,怎么處理?比如,表 A 包含張三和李四,表 B 包含李四和王五,匹配的只有李四這一條記錄。
很容易看出,一共有四種處理方法。
只返回兩張表匹配的記錄,這叫內(nèi)連接(inner join)。返回匹配的記錄,以及表 A 多余的記錄,這叫左連接(left join)。返回匹配的記錄,以及表 B 多余的記錄,這叫右連接(right join)。返回匹配的記錄,以及表 A 和表 B 各自的多余記錄,這叫全連接(full join)。
下圖就是四種連接的圖示。我覺(jué)得,這張圖比維恩圖更易懂,也更準(zhǔn)確。
上圖中,表 A 的記錄是 123,表 B 的記錄是 ABC,顏色表示匹配關(guān)系。返回結(jié)果中,如果另一張表沒(méi)有匹配的記錄,則用 null 填充。
這四種連接,又可以分成兩大類:內(nèi)連接(inner join)表示只包含匹配的記錄,外連接(outer join)表示還包含不匹配的記錄。所以,左連接、右連接、全連接都屬于外連接。
這四種連接的 SQL 語(yǔ)句如下。
SELECT * FROM A INNER JOIN B ON A.book_id=B.book_id; SELECT * FROM A LEFT JOIN B ON A.book_id=B.book_id; SELECT * FROM A RIGHT JOIN B ON A.book_id=B.book_id; SELECT * FROM A FULL JOIN B ON A.book_id=B.book_id;
上面的 SQL 語(yǔ)句還可以加上where條件從句,對(duì)記錄進(jìn)行篩選,比如只返回表 A 里面不匹配表 B 的記錄。
SELECT * FROM A LEFT JOIN B ON A.book_id=B.book_id WHERE B.id IS null;
另一個(gè)例子,返回表 A 或表 B 所有不匹配的記錄。
SELECT * FROM A FULL JOIN B ON A.book_id=B.book_id WHERE A.id IS null OR B.id IS null;
此外,還存在一種特殊的連接,叫做"交叉連接"(cross join),指的是表 A 和表 B 不存在關(guān)聯(lián)字段,這時(shí)表 A(共有 n 條記錄)與表 B (共有 m 條記錄)連接后,會(huì)產(chǎn)生一張包含 n x m 條記錄的新表(見(jiàn)下圖)。
作者:阮一峰
鏈接:http://www.ruanyifeng.com/blog/2019/01/table-join.html
京東SRC(Security Response Center)收錄大量外部白帽子提交的sql注入漏洞,漏洞發(fā)生的原因多為sql語(yǔ)句拼接和Mybatis使用不當(dāng)導(dǎo)致。
mysql5.0以上版本中存在一個(gè)重要的系統(tǒng)數(shù)據(jù)庫(kù)information_schema,通過(guò)此數(shù)據(jù)庫(kù)可訪問(wèn)mysql中存在的數(shù)據(jù)庫(kù)名、表名、字段名等元數(shù)據(jù)。information_schema中有三個(gè)表成為了sql注入構(gòu)造的關(guān)鍵。
SQL注入常用SQL函數(shù)
// sqli vuln code
Statement statement = con.createStatement();
String sql = "select * from users where username = '" + username + "'";
logger.info(sql);
ResultSet rs = statement.executeQuery(sql);
// fix code 如果要使用原始jdbc,請(qǐng)采用預(yù)編譯執(zhí)行
String sql = "select * from users where username = ?";
PreparedStatement st = con.prepareStatement(sql);
使用未預(yù)編譯原始jdbc作為demo,注意此demo中sql語(yǔ)句參數(shù)采用單引號(hào)閉合。
對(duì)于字符類型注入,通常先嘗試單引號(hào),判斷單引號(hào)是否被拼接到SQL語(yǔ)句中。推薦使用瀏覽器擴(kuò)展harkbar作為手工測(cè)試工具。https://chrome.google.com/webstore/detail/hackbar/ginpbkfigcoaokgflihfhhmglmbchinc
正常頁(yè)面應(yīng)該顯示如下:
admin后加單引號(hào)導(dǎo)致無(wú)信息回顯,原因是后端sql執(zhí)行報(bào)錯(cuò),說(shuō)明引號(hào)被拼接至SQL語(yǔ)句中
select * from users where username = 'admin' #正常sql
select * from users where username = 'admin'' #admin'被帶入sql執(zhí)行導(dǎo)致報(bào)錯(cuò)無(wú)法顯示信息
mysql中使用order by 進(jìn)行排序,不僅可以是字段名也可以是字段序號(hào)。所以可以用來(lái)判斷表中字段數(shù),order by 超過(guò)字段個(gè)數(shù)的數(shù)字就會(huì)報(bào)錯(cuò)。
判斷字段數(shù)
當(dāng)order by 超過(guò)4時(shí)會(huì)報(bào)錯(cuò),所以此表共四個(gè)字段。
后端所執(zhí)行的sql語(yǔ)句
select * from users where username = 'admin' order by 1-- '
此處我們將原本username的值admin替換為admin’ order by 1 —+,其中admin后的單引號(hào)用于閉合原本sql語(yǔ)句中的前引號(hào),—+用于注釋sql語(yǔ)句中的后引號(hào)。—后的+號(hào)主要作用是提供一個(gè)空格,sql語(yǔ)句單行注釋后需有空格,+會(huì)被解碼為空格。
主要用于定位后端sql字段在前端顯示的位置,采用聯(lián)合查詢的方式確定。注意聯(lián)合查詢前后字段需一致,這也就是我們?yōu)槭裁醋龅诙降脑颉?/span>
通過(guò)下圖可知,后端查詢并回顯的字段位置為2,3位。
聯(lián)合查詢后的字段可以隨意,本次采用的是數(shù)字1到4直觀方便。
group_concat()函數(shù)用于將查詢結(jié)果拼接為字符串。
sqlmap兼容python2和python3,可以自動(dòng)化檢測(cè)各類注入和幾乎所有數(shù)據(jù)庫(kù)類型。
-u 可能存在注入的url鏈接
-r讀取http數(shù)據(jù)包
--data 指定post數(shù)據(jù)
--cookie 指定cookie
--headers 指定http頭 如采用token認(rèn)證的情況下
--threads 指定線程數(shù)
--dbms 指定后端的數(shù)據(jù)庫(kù)
--os 指定后端的操作系統(tǒng)類型
--current-user 當(dāng)前用戶
--users 所有用戶
--is-dba 是否是dba
--sql-shell 交互式的sqlshell
-p指定可能存在注入點(diǎn)的參數(shù)
--dbs 窮舉系統(tǒng)存在的數(shù)據(jù)庫(kù)
-D指定數(shù)據(jù)庫(kù)
--tables 窮舉存在的表
-T指定表
--column 窮舉字段
-C指定字段
--dump dump數(shù)據(jù)
直接檢測(cè)
其中—cookie用于指定cookie,—batch 自動(dòng)化執(zhí)行,—dbms指定數(shù)據(jù)庫(kù)類型
檢測(cè)結(jié)果
讀取系統(tǒng)中存在數(shù)據(jù)庫(kù)
—dbs讀取當(dāng)前用戶下的數(shù)據(jù)庫(kù)
讀取指定庫(kù)下的表
-D java_sec_code —tables
dump users表數(shù)據(jù)
-D java_sec_code -T users —dump
//采用#不會(huì)導(dǎo)致sql注入,mybatis會(huì)使用預(yù)編譯執(zhí)行
@Select("select * from users where username = #{username}")
User findByUserName(@Param("username") String username);
//采用$作為入?yún)⒖蓪?dǎo)致sql注入
@Select("select * from users where username = '${username}'")
List<User> findByUserNameVuln01(@Param("username") String username);
//錯(cuò)誤寫法
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like '%${_parameter}%'
</select>
//正確寫法
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like concat(‘%’,#{_parameter}, ‘%’)
</select>
order by 后若使用#{}會(huì)導(dǎo)致報(bào)錯(cuò),因?yàn)?{}默認(rèn)添加引號(hào)會(huì)導(dǎo)致找不到字段從而報(bào)錯(cuò)。
//錯(cuò)誤寫法
<select id="findByUserNameVuln03" parameterType="String" resultMap="User">
select * from users
<if test="order != null">
order by ${order} asc
</if>
</select>
//正確寫法 id指字段id 此表字段共四個(gè) 所以id為1-4
<select id="OrderByUsername" resultMap="User">
select * from users order by id asc limit 1
</select>
slqmap手冊(cè):https://octobug.gitbooks.io/sqlmap-wiki-zhcn/content/Users-manual/Introduction.html
sql注入詳解:http://sqlwiki.radare.cn/#/
之前的文章中介紹了使用ASP.NET MVC來(lái)開(kāi)發(fā)一個(gè)博客系統(tǒng),并且已將初具雛形,可以查看文章列表頁(yè)面,也可以點(diǎn)擊文章列表的其中一篇文章查看詳情,這已經(jīng)完成了最開(kāi)始需求分析的讀者的查看列表和查看文章兩個(gè)需求,但是現(xiàn)在最大的問(wèn)題是文章數(shù)據(jù)仍然是“靜態(tài)”的。
所有數(shù)據(jù)被保存在內(nèi)存中,系統(tǒng)在初始化時(shí)會(huì)自動(dòng)添加被硬編碼在代碼文件中的數(shù)據(jù),服務(wù)器每次重新啟動(dòng)都只會(huì)保留這些數(shù)據(jù),并且沒(méi)有提供“作者”的管理接口,“作者”沒(méi)法來(lái)管理這些數(shù)據(jù),哪怕是可以進(jìn)行管理也沒(méi)用,因?yàn)榉?wù)器重啟后數(shù)據(jù)就不存在了,這才是最糟糕的。最初使用HTML文件保存的文章雖然不易修改,但至少數(shù)據(jù)以文件的形式被保存在硬盤上,只要硬盤不壞,那么數(shù)據(jù)永遠(yuǎn)不會(huì)丟失,但是現(xiàn)在不一樣了,要如何對(duì)數(shù)據(jù)進(jìn)行持久化呢?
數(shù)據(jù)庫(kù)是最好的選擇(有的時(shí)候也會(huì)需要使用文件來(lái)管理數(shù)據(jù),需要根據(jù)實(shí)際的應(yīng)用場(chǎng)景來(lái)選擇,如配置文件),當(dāng)然也可以使用文件的方式來(lái)管理數(shù)據(jù),其實(shí)數(shù)據(jù)庫(kù)也是一種特殊的文件,只不過(guò)數(shù)據(jù)庫(kù)文件可以特有的訪問(wèn)方式來(lái)對(duì)數(shù)據(jù)進(jìn)行管理,如插入、刪除、更新和快速查找等等,這些功能都不需要自己編寫,它們是數(shù)據(jù)庫(kù)管理系統(tǒng)提供的(DBMS,Database Management System),數(shù)據(jù)庫(kù)又存在很多種類型,在一般企業(yè)級(jí)應(yīng)用中常用的是關(guān)系型數(shù)據(jù)庫(kù),主要有SQL Server、MySQL、Oracle、PostgreSQL等,它們都能夠?yàn)閼?yīng)用程序的數(shù)據(jù)存儲(chǔ)提供可靠保證。
對(duì)于ASP.NET來(lái)說(shuō)最常用且提供支持最多的當(dāng)然是SQL Server,但是由于MySQL和PostgreSQL都是開(kāi)源的,一定程度上可以對(duì)其免費(fèi)使用,所以也經(jīng)常被.NET開(kāi)發(fā)人員使用,本系列文章將使用SQL Server和MySQL兩種數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)數(shù)據(jù)存儲(chǔ)功能,同時(shí)也可以看出不同數(shù)據(jù)庫(kù)對(duì)一個(gè)應(yīng)用程序會(huì)有哪些影響。
本文通過(guò)以下兩點(diǎn)來(lái)介紹如何在ASP.NET中使用SQL Server數(shù)據(jù)庫(kù):
●使用SQL Server存儲(chǔ)數(shù)據(jù)
●在ASP.NET中訪問(wèn)SQL Server
使用SQL Server存儲(chǔ)數(shù)據(jù)
1. 使用SQL Server創(chuàng)建Blog數(shù)據(jù)庫(kù),以及Posts表,表字段對(duì)應(yīng)Post類(如何使用SQL Server不在本系列文章范圍內(nèi),所以會(huì)忽略很多細(xì)節(jié)):
2. 在表中添加數(shù)據(jù):
在ASP.NET中訪問(wèn)SQL Server
文章前面說(shuō)過(guò)數(shù)據(jù)庫(kù)其實(shí)也是一種特殊的文件,然后通過(guò)特有的訪問(wèn)方式來(lái)使用它,那么ASP.NET 要如何連接并使用SQL Server呢?ADO.NET。(ADO.NET更多信息參考:http://blog.csdn.net/dreamcatchergo/article/details/9729525)
現(xiàn)在修改原有通過(guò)靜態(tài)數(shù)組獲取數(shù)據(jù)的BlogRepository,使用ADO.NET來(lái)獲取數(shù)據(jù)庫(kù)中的數(shù)據(jù),ADO.NET相關(guān)的類在System.Data.dll程序集中,而ADO.NET對(duì)于不同的數(shù)據(jù)源提供了不同的數(shù)據(jù)提供器,它們用于連接不同的數(shù)據(jù)源:
SQL Server:System.Data.SqlClient
OleDb: System.Data.OleDb
Odbc: System.Data.Odbc
Oracle: System.Data.OracleClient
在本例中使用的是SQL Server,所以需要System.Data以及System.Data.SqlClient的支持,System.Data在創(chuàng)建項(xiàng)目時(shí)以及默認(rèn)存在。
1. 通過(guò)Nuget包管理器安裝System.Data.Sqlclient庫(kù):
2. 修改BlogRepository,讓其從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù):
上面代碼中有三個(gè)重要的對(duì)象分別是SqlConnection、SqlCommand、SqlDataReader,它們的作用分別是通過(guò)連接字符串連接數(shù)據(jù)庫(kù)、通過(guò)SQL語(yǔ)句及參數(shù)執(zhí)行SQL(也可以執(zhí)行存儲(chǔ)過(guò)程)、讀取執(zhí)行SQL返回的結(jié)果。
3. 程序執(zhí)行結(jié)果:
小結(jié)
本章介紹了如何使用數(shù)據(jù)庫(kù)來(lái)管理數(shù)據(jù),然后通過(guò)ADO.NET來(lái)連接數(shù)據(jù)庫(kù),從數(shù)據(jù)庫(kù)中獲取文章數(shù)據(jù),然后顯示到頁(yè)面上,現(xiàn)在這個(gè)應(yīng)用程序已經(jīng)“動(dòng)”起來(lái)了,僅需要更新數(shù)據(jù)庫(kù)的內(nèi)容,頁(yè)面內(nèi)容也將隨之而變。但存在一個(gè)問(wèn)題就是每一次從數(shù)據(jù)庫(kù)獲取數(shù)據(jù)都要寫SQL語(yǔ)句然后從返回的數(shù)據(jù)集中獲取相應(yīng)字段的數(shù)據(jù)用來(lái)創(chuàng)建實(shí)體對(duì)象。有沒(méi)有更好的方法來(lái)解決?
參考:
https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/
http://blog.csdn.net/dreamcatchergo/article/details/9729525
歡迎添加個(gè)人微信號(hào):Like若所思。
歡迎關(guān)注我的公眾號(hào),不僅為你推薦最新的博文,還有更多驚喜和資源在等著你!一起學(xué)習(xí)共同進(jìn)步!
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。