精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

SQL注入到底是什么詳細資料講解

Wildesbeast ? 來源:今日頭條 ? 作者:網絡安全晴雨表 ? 2020-04-06 12:22 ? 次閱讀

前言

先來看一副很有意思的漫畫:

相信大家對于學校們糟糕的網絡環境和運維手段都早有體會,在此就不多做吐槽了。今天我們來聊一聊SQL注入相關的內容。

1 何謂SQL注入?

SQL注入是一種非常常見的數據庫攻擊手段,SQL注入漏洞也是網絡世界中最普遍的漏洞之一。大家也許都聽過某某學長通過攻擊學校數據庫修改自己成績的事情,這些學長們一般用的就是SQL注入方法。

SQL注入其實就是惡意用戶通過在表單中填寫包含SQL關鍵字的數據來使數據庫執行非常規代碼的過程。簡單來說,就是數據「越俎代庖」做了代碼才能干的事情。

這個問題的來源是,SQL數據庫的操作是通過SQL語句來執行的,而無論是執行代碼還是數據項都必須寫在SQL語句之中,這就導致如果我們在數據項中加入了某些SQL語句關鍵字(比如說SELECT、DROP等等),這些關鍵字就很可能在數據庫寫入或讀取數據時得到執行。

多言無益,我們拿真實的案例來說話。下面我們先使用SQLite建立一個學生檔案表。

SQL數據庫操作示例:

import sqlite3

連接數據庫:

conn = sqlite3.connect('test.db')

建立新的數據表:

conn.executescript('''DROP TABLE IF EXISTS students; CREATE TABLE students (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL);''')

插入學生信息

students = ['Paul','Tom','Tracy','Lily']for name in students: query = "INSERT INTO students (name) VALUES ('%s')" % (name) conn.executescript(query);

檢視已有的學生信息:

cursor = conn.execute("SELECT id, name from students")print('IDName')for row in cursor: print('{0}{1}'.format(row[0], row[1]))conn.close()

點擊運行按鈕將會打印目前表中的內容。上述程序中我們建立了一個test.db數據庫以及一個students數據表,并向表中寫入了四條學生信息。

那么SQL注入又是怎么一回事呢?我們嘗試再插入一條惡意數據,數據內容就是漫畫中的"Robert');DROP TABLE students;--",看看會發生什么情況。

SQL數據庫注入示例:

conn = sqlite3.connect('test.db')

插入包含注入代碼的信息:

name = "Robert');DROP TABLE students;--"query = "INSERT INTO students (name) VALUES ('%s')" % (name)conn.executescript(query)

檢視已有的學生信息:

cursor = conn.execute("SELECT id, name from students")print('IDName')for row in cursor: print('{0}{1}'.format(row[0], row[1]))conn.close()

你將會發現,運行后,程序沒有輸出任何數據內容,而是返回一條錯誤信息:表單students無法找到!

這是為什么呢?問題就在于我們所插入的數據項中包含SQL關鍵字DROP TABLE,這兩個關鍵字的意義是從數據庫中清除一個表單。

而關鍵字之前的Robert');使得SQL執行器認為上一命令已經結束,從而使得危險指令DROP TABLE得到執行。

也就是說,這段包含DROP TABLE關鍵字的數據項使得原有的簡單的插入姓名信息的SQL語句:

INSERT INTO students (name) VALUES ('Robert')

變為了同時包含另外一條清除表單命令的語句:

INSERT INTO students (name) VALUES ('Robert');DROP TABLE students;

而SQL數據庫執行上述操作后,students表單被清除,因而表單無法找到,所有數據項丟失。

2 如何防止SQL注入問題呢?

大家也許都想到了,注入問題都是因為執行了數據項中的SQL關鍵字,那么,只要檢查數據項中是否存在SQL關鍵字不就可以了么?

的確是這樣,很多數據庫管理系統都是采取了這種看似『方便快捷』的過濾手法,但是這并不是一種根本上的解決辦法,如果有個美國人真的就叫做『Drop Table』呢?你總不能逼人家改名字吧。

合理的防護辦法有很多。首先,盡量避免使用常見的數據庫名和數據庫結構。在上面的案例中,如果表單名字并不是students,則注入代碼將會在執行過程中報錯,也就不會發生數據丟失的情況——SQL注入并不像大家想象得那么簡單,它需要攻擊者本身對于數據庫的結構有足夠的了解才能成功,因而在構建數據庫時盡量使用較為復雜的結構和命名方式將會極大地減少被成功攻擊的概率。

使用正則表達式等字符串過濾手段限制數據項的格式、字符數目等也是一種很好的防護措施。理論上,只要避免數據項中存在引號、分號等特殊字符就能很大程度上避免SQL注入的發生。

另外,就是使用各類程序文檔所推薦的數據庫操作方式來執行數據項的查詢與寫入操作,比如在上述的案例中,如果我們稍加修改,首先使用execute()方法來保證每次執行僅能執行一條語句,然后將數據項以參數的方式與SQL執行語句分離開來,就可以完全避免SQL注入的問題,如下SQL數據庫反注入示例。

conn = sqlite3.connect('test.db')

以安全方式插入包含注入代碼的信息:

name = "Robert');DROP TABLE students;--"query="INSERTINTOstudents(name)VALUES(?)"conn.execute(query, [name])

檢視已有的學生信息:

cursor = conn.execute("SELECT id, name from students")print('IDName')for row in cursor:print('{0}{1}'.format(row[0],row[1]))conn.close()

而對于PHP而言,則可以通過mysql_real_escape_string等方法對SQL關鍵字進行轉義,必要時審查數據項目是否安全來防治SQL注入。

當然,做好數據庫的備份,同時對敏感內容進行加密永遠是最重要的。某些安全性問題可能永遠不會有完美的解決方案,只有我們做好最基本的防護措施,才能在發生問題的時候亡羊補牢,保證最小程度的損失。

注意:但凡有SQL注入漏洞的程序,都是因為程序要接受來自客戶端用戶輸入的變量或URL傳遞的參數,并且這個變量或參數是組成SQL語句的一部分,對于用戶輸入的內容或傳遞的參數,我們應該要時刻保持警惕,這是安全領域里的「外部數據不可信任」的原則,縱觀Web安全領域的各種攻擊方式,大多數都是因為開發者違反了這個原則而導致的,所以自然能想到的,就是從變量的檢測、過濾、驗證下手,確保變量是開發者所預想的。

1、檢查變量數據類型和格式

如果你的SQL語句是類似where id={$id}這種形式,數據庫里所有的id都是數字,那么就應該在SQL被執行前,檢查確保變量id是int類型;如果是接受郵箱,那就應該檢查并嚴格確保變量一定是郵箱的格式,其他的類型比如日期、時間等也是一個道理。總結起來:只要是有固定格式的變量,在SQL語句執行前,應該嚴格按照固定格式去檢查,確保變量是我們預想的格式,這樣很大程度上可以避免SQL注入攻擊。

比如,我們前面接受username參數例子中,我們的產品設計應該是在用戶注冊的一開始,就有一個用戶名的規則,比如5-20個字符,只能由大小寫字母、數字以及一些安全的符號組成,不包含特殊字符。此時我們應該有一個check_username的函數來進行統一的檢查。不過,仍然有很多例外情況并不能應用到這一準則,比如文章發布系統,評論系統等必須要允許用戶提交任意字符串的場景,這就需要采用過濾等其他方案了。

2、過濾特殊符號

對于無法確定固定格式的變量,一定要進行特殊符號過濾或轉義處理。

3、綁定變量,使用預編譯語句

MySQL的mysqli驅動提供了預編譯語句的支持,不同的程序語言,都分別有使用預編譯語句的方法

實際上,綁定變量使用預編譯語句是預防SQL注入的最佳方式,使用預編譯的SQL語句語義不會發生改變,在SQL語句中,變量用問號?表示,黑客即使本事再大,也無法改變SQL語句的結構

3 什么是sql預編譯

1.1:預編譯語句是什么

通常我們的一條sql在db接收到最終執行完畢返回可以分為下面三個過程:

詞法和語義解析、優化sql語句,制定執行計劃、執行并返回結果

我們把這種普通語句稱作Immediate Statements。

但是很多情況,我們的一條sql語句可能會反復執行,或者每次執行的時候只有個別的值不同(比如query的where子句值不同,update的set子句值不同,insert的values值不同)。

如果每次都需要經過上面的詞法語義解析、語句優化、制定執行計劃等,則效率就明顯不行了。

所謂預編譯語句就是將這類語句中的值用占位符替代,可以視為將sql語句模板化或者說參數化,一般稱這類語句叫Prepared Statements或者Parameterized Statements

預編譯語句的優勢在于歸納為:一次編譯、多次運行,省去了解析優化等過程;此外預編譯語句能防止sql注入。

當然就優化來說,很多時候最優的執行計劃不是光靠知道sql語句的模板就能決定了,往往就是需要通過具體值來預估出成本代價。

1.2:MySQL的預編譯功能

注意MySQL的老版本(4.1之前)是不支持服務端預編譯的,但基于目前業界生產環境普遍情況,基本可以認為MySQL支持服務端預編譯。

下面我們來看一下MySQL中預編譯語句的使用。

(1)建表 首先我們有一張測試表t,結構如下所示:

mysql> show create table tG*************************** 1. row *************************** Table: tCreate Table: CREATE TABLE `t` ( `a` int(11) DEFAULT NULL, `b` varchar(20) DEFAULT NULL, UNIQUE KEY `ab` (`a`,`b`)) ENGINE=InnoDB DEFAULT CHARSET=utf8

(2)編譯

我們接下來通過 PREPARE stmt_name FROM preparable_stm的語法來預編譯一條sql語句

mysql> prepare ins from 'insert into t select ?,?';Query OK, 0 rows affected (0.00 sec)Statement prepared

(3)執行

我們通過EXECUTE stmt_name [USING @var_name [, @var_name] ...]的語法來執行預編譯語句

mysql> set @a=999,@b='hello';Query OK, 0 rows affected (0.00 sec) mysql> execute ins using @a,@b;Query OK, 1 row affected (0.01 sec)Records: 1 Duplicates: 0 Warnings: 0 mysql> select * from t;+------+-------+| a | b |+------+-------+| 999 | hello |+------+-------+1 row in set (0.00 sec)

可以看到,數據已經被成功插入表中。

MySQL中的預編譯語句作用域是session級,但我們可以通過max_prepared_stmt_count變量來控制全局最大的存儲的預編譯語句。

mysql> set @@global.max_prepared_stmt_count=1;Query OK, 0 rows affected (0.00 sec) mysql> prepare sel from 'select * from t';ERROR 1461 (42000): Can't create more than max_prepared_stmt_count statements (current value: 1)

當預編譯條數已經達到閾值時可以看到MySQL會報如上所示的錯誤。

(4)釋放

如果我們想要釋放一條預編譯語句,則可以使用{DEALLOCATE | DROP} PREPARE stmt_name的語法進行操作:

mysql> deallocate prepare ins;Query OK, 0 rows affected (0.00 sec)

4 為什么PrepareStatement可以防止sql注入

原理是采用了預編譯的方法,先將SQL語句中可被客戶端控制的參數集進行編譯,生成對應的臨時變量集,再使用對應的設置方法,為臨時變量集里面的元素進行賦值,賦值函數setString(),會對傳入的參數進行強制類型檢查和安全檢查,所以就避免了SQL注入的產生。下面具體分析

(1):為什么Statement會被sql注入

因為Statement之所以會被sql注入是因為SQL語句結構發生了變化。比如:

"select*from tablename where username='"+uesrname+ "'and password='"+password+"'"

在用戶輸入'or true or'之后sql語句結構改變。

select*from tablename where username=''or true or'' and password=''

這樣本來是判斷用戶名和密碼都匹配時才會計數,但是經過改變后變成了或的邏輯關系,不管用戶名和密碼是否匹配該式的返回值永遠為true;

(2)為什么Preparement可以防止SQL注入。

因為Preparement樣式為

select*from tablename where username=? and password=?

該SQL語句會在得到用戶的輸入之前先用數據庫進行預編譯,這樣的話不管用戶輸入什么用戶名和密碼的判斷始終都是并的邏輯關系,防止了SQL注入

簡單總結,參數化能防注入的原因在于,語句是語句,參數是參數,參數的值并不是語句的一部分,數據庫只按語句的語義跑,至于跑的時候是帶一個普通背包還是一個怪物,不會影響行進路線,無非跑的快點與慢點的區別。

5 mybatis是如何防止SQL注入的

1、首先看一下下面兩個sql語句的區別:

mybatis中的#和$的區別:

1、#將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號。

如:where username=#{username},如果傳入的值是111,那么解析成sql時的值為where username="111", 如果傳入的值是id,則解析成的sql為where username="id".

2、$將傳入的數據直接顯示生成在sql中。

如:where username=${username},如果傳入的值是111,那么解析成sql時的值為where username=111;

如果傳入的值是;drop table user;,則解析成的sql為:select id, username, password, role from user where username=;drop table user;

3、#方式能夠很大程度防止sql注入,$方式無法防止Sql注入。

4、$方式一般用于傳入數據庫對象,例如傳入表名.

5、一般能用#的就別用$,若不得不使用“${xxx}”這樣的參數,要手工地做好過濾工作,來防止sql注入攻擊。

6、在MyBatis中,“${xxx}”這樣格式的參數會直接參與SQL編譯,從而不能避免注入攻擊。但涉及到動態表名和列名時,只能使用“${xxx}”這樣的參數格式。所以,這樣的參數需要我們在代碼中手工進行處理來防止注入。

【結論】在編寫MyBatis的映射語句時,盡量采用“#{xxx}”這樣的格式。若不得不使用“${xxx}”這樣的參數,要手工地做好過濾工作,來防止SQL注入攻擊。

mybatis是如何做到防止sql注入的

MyBatis框架作為一款半自動化的持久層框架,其SQL語句都要我們自己手動編寫,這個時候當然需要防止SQL注入。其實,MyBatis的SQL是一個具有“輸入+輸出”的功能,類似于函數的結構,參考上面的兩個例子。其中,parameterType表示了輸入的參數類型,resultType表示了輸出的參數類型。回應上文,如果我們想防止SQL注入,理所當然地要在輸入參數上下功夫。上面代碼中使用#的即輸入參數在SQL中拼接的部分,傳入參數后,打印出執行的SQL語句,會看到SQL是這樣的:

select id, username, password, role from user where username=? and password=?

不管輸入什么參數,打印出的SQL都是這樣的。這是因為MyBatis啟用了預編譯功能,在SQL執行前,會先將上面的SQL發送給數據庫進行編譯;執行時,直接使用編譯好的SQL,替換占位符“?”就可以了。因為SQL注入只能對編譯過程起作用,所以這樣的方式就很好地避免了SQL注入的問題。

【底層實現原理】MyBatis是如何做到SQL預編譯的呢?其實在框架底層,是JDBC中的PreparedStatement類在起作用,PreparedStatement是我們很熟悉的Statement的子類,它的對象包含了編譯好的SQL語句。這種“準備好”的方式不僅能提高安全性,而且在多次執行同一個SQL時,能夠提高效率。原因是SQL已編譯好,再次執行時無需再編譯

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • SQL
    SQL
    +關注

    關注

    1

    文章

    760

    瀏覽量

    44082
  • 數據庫
    +關注

    關注

    7

    文章

    3767

    瀏覽量

    64280
收藏 人收藏

    評論

    相關推薦

    請問PCM2903C的溫度范圍到底是多少呢?

    如下圖,PCM2903C的溫度范圍到底是多少呢? 如果用在-25~85℃,是否會出問題?
    發表于 10-14 07:14

    放大器的共模輸入電壓到底是指什么?

    請問放大器的共模輸入電壓到底是指什么?
    發表于 09-19 07:17

    功放和運放到底是什么區別?

    想請問一下功放和運放到底是什么區別,感覺只要接一個小負載,運放的輸出電流也可以很大啊?到底有什么區別啊
    發表于 09-10 07:00

    請問LMV772到底是雙電源還是單電源啊?

    請問LMV772到底是雙電源還是單電源啊?手冊前面寫的太模糊了。求指教
    發表于 09-09 07:10

    運放的輸入電容到底是什么?

    我想請問一下運放的輸入電容到底是什么?
    發表于 09-04 06:52

    LMH6502的輸入電壓到底是多少?

    LMH6502的輸入電壓到底是多少,我稍微給如大一點點的信號,放大不行還能接受,我衰減都失真,
    發表于 08-27 07:02

    IP 地址在 SQL 注入攻擊中的作用及防范策略

    數據庫在各個領域的逐步應用,其安全性也備受關注。SQL 注入攻擊作為一種常見的數據庫攻擊手段,給網絡安全帶來了巨大威脅。今天我們來聊一聊SQL 注入攻擊的基本知識。
    的頭像 發表于 08-05 17:36 ?269次閱讀

    對于STM8的固件庫,到底是怎么對文件進行配置的?

    對于STM8的固件庫,到底是怎么對文件進行配置的?
    發表于 05-17 15:59

    共享單車到底是什么通信原理

    我們經常騎的共享單車到底是什么通信原理,有人了解過嗎? 一、智能車鎖 共享單車最核心的硬件是智能車鎖,主要用于實現控制和定位功能。
    發表于 04-09 10:33 ?814次閱讀
    共享單車<b class='flag-5'>到底是</b>什么通信原理

    SQL全外連接剖析

    SQL中的全外連接是什么? 在SQL中,FULLOUTERJOIN組合左外連接和右外連接的結果,并返回連接子句兩側表中的所有(匹配或不匹配)行。接下面sojson給大家詳細講解。 ?
    的頭像 發表于 03-19 18:28 ?2162次閱讀
    <b class='flag-5'>SQL</b>全外連接剖析

    共享單車到底是什么通信原理?

    我們經常騎的共享單車到底是什么通信原理,有人了解過嗎?下面寶藍小編就帶大家了解下。
    的頭像 發表于 02-25 10:32 ?1345次閱讀
    共享單車<b class='flag-5'>到底是</b>什么通信原理?

    查詢SQL在mysql內部是如何執行?

    我們知道在mySQL客戶端,輸入一條查詢SQL,然后看到返回查詢的結果。這條查詢語句在 MySQL 內部到底是如何執行的呢?本文跟大家探討一下哈,我們先來看下MySQL基本架構~
    的頭像 發表于 01-22 14:53 ?540次閱讀
    查詢<b class='flag-5'>SQL</b>在mysql內部是如何執行?

    請問M487KMCAN的SRAM到底是128KB還是160K?

    M487KMCAN的SRAM到底是128 KB 還是160K
    發表于 01-16 07:18

    去耦濾波電容怎么布局擺放,到底是先大后小還是先小后大?

    去耦濾波電容怎么布局擺放,到底是先大后小還是先小后大?
    的頭像 發表于 12-04 15:43 ?2304次閱讀
    去耦濾波電容怎么布局擺放,<b class='flag-5'>到底是</b>先大后小還是先小后大?

    請問AD9684最低采樣率到底是多少?

    關于AD9684最低采樣率,數據手冊有兩處描述,但是不一致。請問AD9684最低采樣率到底是多少?
    發表于 12-04 06:34