引數化查詢為什麼能夠防止SQL注入
轉自:http://www.cnblogs.com/LoveJenny/archive/2013/01/15/2860553.html
很多人都知道SQL注入,也知道SQL引數化查詢可以防止SQL注入,可為什麼能防止注入卻並不是很多人都知道的。
本文主要講述的是這個問題,也許你在部分文章中看到過這塊內容,當然了看看也無妨。
首先:我們要了解SQL收到一個指令後所做的事情:
在這裡,我簡單的表示為: 收到指令 -> 編譯SQL生成執行計劃 ->選擇執行計劃 ->執行執行計劃。
具體可能有點不一樣,但大致的步驟如上所示。
接著我們來分析為什麼拼接SQL 字串會導致SQL注入的風險呢?
首先建立一張表Users:
CREATE TABLE [dbo].[Users](
[Id] [uniqueidentifier] NOT NULL,
[UserId] [int] NOT NULL,
[UserName] [varchar](50) NULL,
[Password] [varchar](50) NOT NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
插入一些資料:
INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),1,'name1','pwd1');
INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),2,'name2','pwd2');
INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),3,'name3','pwd3');
INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),4,'name4','pwd4');
INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),5,'name5','pwd5');
假設我們有個使用者登入的頁面,程式碼如下:
驗證使用者登入的sql 如下:
select COUNT(*) from Users where Password = 'a' and UserName = 'b'
這段程式碼返回Password 和UserName都匹配的使用者數量,如果大於1的話,那麼就代表使用者存在。
本文不討論SQL 中的密碼策略,也不討論程式碼規範,主要是講為什麼能夠防止SQL注入,請一些同學不要糾結與某些程式碼,或者和SQL注入無關的主題。
可以看到執行結果:
這個是SQL profile 跟蹤的SQL 語句。
注入的程式碼如下:
select COUNT(*) from Users where Password = 'a' and UserName = 'b' or 1=1—'
這裡有人將UserName設定為了 “b' or 1=1 –”.
實際執行的SQL就變成了如下:
可以很明顯的看到SQL注入成功了。
很多人都知道引數化查詢可以避免上面出現的注入問題,比如下面的程式碼:
class Program
{
private static string connectionString = "Data Source=.;Initial Catalog=Test;Integrated Security=True";
static void Main(string[] args)
{
Login("b", "a");
Login("b' or 1=1--", "a");
}
private static void Login(string userName, string password)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand comm = new SqlCommand();
comm.Connection = conn;
//為每一條資料新增一個引數
comm.CommandText = "select COUNT(*) from Users where Password = @Password and UserName = @UserName";
comm.Parameters.AddRange(
new SqlParameter[]{
new SqlParameter("@Password", SqlDbType.VarChar) { Value = password},
new SqlParameter("@UserName", SqlDbType.VarChar) { Value = userName},
});
comm.ExecuteNonQuery();
}
}
}
實際執行的SQL 如下所示:
exec sp_executesql N'select COUNT(*) from Users where Password = @Password and UserName = @UserName',N'@Password varchar(1),@UserName varchar(1)',@Password='a',@UserName='b'
exec sp_executesql N'select COUNT(*) from Users where Password = @Password and UserName = @UserName',N'@Password varchar(1),@UserName varchar(11)',@Password='a',@UserName='b'' or 1=1—'
可以看到引數化查詢主要做了這些事情:
1:引數過濾,可以看到 @UserName='b'' or 1=1—'
2:執行計劃重用
因為執行計劃被重用,所以可以防止SQL注入。
首先分析SQL注入的本質,
使用者寫了一段SQL 用來表示查詢密碼是a的,使用者名稱是b的所有使用者的數量。
通過注入SQL,這段SQL現在表示的含義是查詢(密碼是a的,並且使用者名稱是b的,) 或者1=1 的所有使用者的數量。
可以看到SQL的語意發生了改變,為什麼發生了改變呢?,因為沒有重用以前的執行計劃,因為對注入後的SQL語句重新進行了編譯,因為重新執行了語法解析。所以要保證SQL語義不變,即我想要表達SQL就是我想表達的意思,不是別的注入後的意思,就應該重用執行計劃。
如果不能夠重用執行計劃,那麼就有SQL注入的風險,因為SQL的語意有可能會變化,所表達的查詢就可能變化。
在SQL Server 中查詢執行計劃可以使用下面的指令碼:
DBCC FreeProccache
select total_elapsed_time / execution_count 平均時間,total_logical_reads/execution_count 邏輯讀,
usecounts 重用次數,SUBSTRING(d.text, (statement_start_offset/2) + 1,
((CASE statement_end_offset
WHEN -1 THEN DATALENGTH(text)
ELSE statement_end_offset END
- statement_start_offset)/2) + 1) 語句執行 from sys.dm_exec_cached_plans a
cross apply sys.dm_exec_query_plan(a.plan_handle) c
,sys.dm_exec_query_stats b
cross apply sys.dm_exec_sql_text(b.sql_handle) d
--where a.plan_handle=b.plan_handle and total_logical_reads/execution_count>4000
ORDER BY total_elapsed_time / execution_count DESC;
在這篇文章中有這麼一段:
這裡作者有一句話:”不過這種寫法和直接拼SQL執行沒啥實質性的區別”
任何拼接SQL的方式都有SQL注入的風險,所以如果沒有實質性的區別的話,那麼使用exec 動態執行SQL是不能防止SQL注入的。
比如下面的程式碼:
private static void TestMethod()
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand comm = new SqlCommand();
comm.Connection = conn;
//使用exec動態執行SQL
//實際執行的查詢計劃為(@UserID varchar(max))select * from Users(nolock) where UserID in (1,2,3,4)
//不是預期的(@UserID varchar(max))exec('select * from Users(nolock) where UserID in ('[email protected]+')')
comm.CommandText = "exec('select * from Users(nolock) where UserID in ('[email protected]+')')";
comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" });
//comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4); delete from Users;--" });
comm.ExecuteNonQuery();
}
}
執行的SQL 如下:
exec sp_executesql N'exec(''select * from Users(nolock) where UserID in (''[email protected]+'')'')',N'@UserID varchar(max) ',@UserID='1,2,3,4'
可以看到SQL語句並沒有引數化查詢。
如果你將UserID設定為”
1,2,3,4); delete from Users;—-
”,那麼執行的SQL就是下面這樣:
exec sp_executesql N'exec(''select * from Users(nolock) where UserID in (''[email protected]+'')'')',N'@UserID varchar(max) ',@UserID='1,2,3,4); delete from Users;--'
不要以為加了個@UserID 就代表能夠防止SQL注入,實際執行的SQL 如下:
任何動態的執行SQL 都有注入的風險,因為動態意味著不重用執行計劃,而如果不重用執行計劃的話,那麼就基本上無法保證你寫的SQL所表示的意思就是你要表達的意思。
這就好像小時候的填空題,查詢密碼是(____) 並且使用者名稱是(____)的使用者。
不管你填的是什麼值,我所表達的就是這個意思。
最後再總結一句:因為引數化查詢可以重用執行計劃,並且如果重用執行計劃的話,SQL所要表達的語義就不會變化,所以就可以防止SQL注入,如果不能重用執行計劃,就有可能出現SQL注入,
儲存過程也是一樣的道理,因為可以重用執行計劃。
相關推薦
引數化查詢為什麼能夠防止SQL注入
轉自:http://www.cnblogs.com/LoveJenny/archive/2013/01/15/2860553.html 很多人都知道SQL注入,也知道SQL引數化查詢可以防止SQL注入,可為什麼能防止注入卻並不是很多人都知道的。 本文主要講述的是這個問題,也許你在部分文章中看到過這塊內容,
nodejs中查詢mysql防止SQL注入
Performing queries The most basic way to perform a query is to call the .query() method on an object (like a Connection, Pool, or PoolNamespace inst
玩轉JDBC打造資料庫操作萬能工具類JDBCUtil,加入了高效的資料庫連線池,利用了引數繫結有效防止SQL注入
SELECT * FROM emp_test 成功查詢到了14行資料 第1行:{DEPT_TEST_ID=10, EMP_ID=1001, SALARY=10000, HIRE_DATE=2010-01-12, BONUS=2000, MANAGER=1005, JOB=Manager, NAME=張無忌}
SQL或HQL預編譯語句,能夠防止SQL注入,但是不能處理%和_特殊字元
最近專案在做整改,將所有DAO層的直接拼接SQL字串的程式碼,轉換成使用預編譯語句的方式。個人通過寫dao層的單元測試,有以下幾點收穫。 dao層程式碼如下 //使用了預編譯sql public Li
sql注入與防止SQL注入之引數化處理
sql注入的兩種情況: 操作程式碼: import pymysql user = input('使用者名稱: ').strip() pwd = input('密碼: ').strip() # 連結 conn = pymysql.connect(host='localhost', user='ro
C#引數化(防止SQL注入)
/* * C#防止SQL注入式攻擊 * Author:ICE FROG * TIME:2016/4/20 */ /* * SQL注入式攻擊就是值通過SQL執行語句的漏洞進行百分百匹
MySQL— pymysql模組(防止sql注入),視覺化軟體Navicat
一.Pymysql import pymysql #python2.X 中是 mysqldb 和 pythonmysql 用法是一模一樣的 #pymysql可以偽裝成上面這兩個模組 user = input('username: ') pwd = input('passwo
mybatis模糊查詢防止sql注入
SQL注入,大家都不陌生,是一種常見的攻擊方式。攻擊者在介面的表單資訊或URL上輸入一些奇怪的SQL片段(例如“or ‘1’=’1’”這樣的語句),有可能入侵引數檢驗不足的應用程式。所以,在我們的應用中需要做一些工作,來防備這樣的攻擊方式。在一些安全性要求很高的應用中(比如銀行軟體),經常使用將SQ
AOP實踐--ASP.NET MVC 5使用Filter過濾Action引數防止sql注入,讓你程式碼安全簡潔
在開發程式的過程中,稍微不注意就會隱含有sql注入的危險。今天我就來說下,ASP.NET mvc 5使用Filter過濾Action引數防止sql注入,讓你程式碼安全簡潔。不用每下地方對引數的值都進行檢查,看是使用者輸入的內容是否有危險的sql。如果沒個地方都要加有幾個缺
【Jmeter】——sql引數化查詢測試
前言 現在大家寫的sql語句基本都是引數化的 當然在jmeter中也是可以測試引數化的sql語句 但是我始終都是有些疑問 不知道為什麼要測引數化的,我感覺沒什麼區別呢
sql引數化查詢
//在ASP.NET程式中使用引數化查詢 //ASP.NET環境下的查詢化查詢也是通過Connection物件和Command物件完成。如果資料庫是SQL Server,就可以用有名字的引數了,格式是“@”字元加上引數名。 SqlConnection conn = new SqlCon
Mybatis like 查詢 防止SQL注入方法相關原理和解決方法整理
SQL注入:引自百度百科: 所謂SQL注入,就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字串,最終達到欺騙伺服器執行惡意的SQL命令。具體來說,它是利用現有應用程式,將(惡意)的
淺析Sql Server引數化查詢
錯誤認識1.不需要防止sql注入的地方無需引數化 引數化查詢就是為了防止SQL注入用的,其它還有什麼用途不知道、也不關心,原則上是能不用引數就不用引數,為啥?多麻煩,我只是做公司內部系統不用擔心SQL注入風險,使用引數化查詢不是給自己找麻煩,簡簡單單拼SQL,萬事OK 錯誤認識2.引數化查詢時是否指定
Mabatis中模糊查詢防止sql注入
#{xxx},使用的是PreparedStatement,會有型別轉換,所以比較安全; ${xxx},使用字串拼接,可以SQL注入; like查詢不小心會有漏洞,正確寫法如下: Mysql: select * from user where name like
【PHP】使用引數繫結防止SQL注入
<html> <head> <title>Sql注入演示</title> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <
命名引數,防止sql注入,使用named parameter
使用named parameter String hql = "from BuildHibernate p where p.beimId= :beimId "; Query query = getSession().createQuery(hql);
wireshark抓包分析mybatis的sql引數化查詢
我們使用jdbc操作資料庫的時候,都習慣性地使用引數化的sql與資料庫互動。因為引數化的sql有兩大有點,其一,防止sql注入;其二,提高sql的執行效能(同一個connection共用一個的sql編譯結果)。下面我們就通過mybatis來分析一下引數化sql的過程,以及和非引數化sql的不同。 注意
Mybatis防止sql注入原理
SQL 注入是一種程式碼注入技術,用於攻擊資料驅動的應用,惡意的SQL 語句被插入到執行的實體欄位中(例如,為了轉儲資料庫內容給攻擊者)。[摘自] SQL注入 - 維基百科SQL注入,大家都不陌生,是一種常見的攻擊方式。攻擊者在介面的表單資訊或UR
iBatis解決自動防止sql注入
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
sql注入與防止sql注入
資料庫中的資料 sql程式碼 package com.zjw.jdbc2; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.