最新的Web開發教程
 

SQL注射


SQL注入可以摧毀你的數據庫。


SQL在網頁

在前面的章節中,你已經學會了檢索(和更新)數據庫中的數據,使用SQL。

當SQL是用於在網頁上顯示的數據,是很常見的,讓網絡用戶輸入自己的搜索值。

由於SQL語句是純文本,很容易,用少許一段計算機代碼,來動態改變SQL語句來提供選擇數據的用戶:

服務器代碼

txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;

上面的例子,加入一個變量(txtUserId)將選擇字符串創建一個select語句。 變量是來自用戶的輸入(請求)至頁面中取出。

本章的其餘部分將介紹在SQL語句中使用用戶輸入的潛在危險。


SQL注入

SQL注入是一個技術,其中惡意用戶可以SQL命令注入SQL語句,通過網頁輸入。

注入SQL命令可以改變SQL語句和妥協的Web應用程序的安全性。


基於1 = 1 SQL注入始終是真實的

看看上面的例子,一個更多的時間。

比方說,代碼的最初目的是創建一個SQL語句來選擇與給定用戶ID的用戶。

如果沒有什麼可以阻止用戶進入"wrong"輸入,用戶可以輸入一些"smart"輸入是這樣的:

用戶名:

服務器結果

SELECT * FROM Users WHERE UserId = 105 or 1=1

上述的SQL是有效的。 它將從表中的用戶返回所有行,因為1 = 1始終為true。

請問上面的例子似乎很危險嗎? 如果用戶表中包含名和密碼?

上述SQL語句是大致相同的,因為這:

SELECT UserId, Name, Password FROM Users WHERE UserId = 105 or 1=1

智能黑客可能通過簡單地插入105或1 = 1到輸入框獲得所有數據庫中的用戶名和密碼。


SQL注入基於""=""始終是真實的

這是一個常見的結構,用於驗證用戶登錄到一個網站:

用戶名:

密碼:

服務器代碼

uName = getRequestString("UserName");
uPass = getRequestString("UserPass");

sql = "SELECT * FROM Users WHERE Name ='" + uName + "' AND Pass ='" + uPass + "'"

一個聰明的黑客可能通過簡單地插入“或可以訪問用戶名和密碼在數據庫""="到用戶名或密碼文本框。

在服務器上的代碼將創建這樣一個有效的SQL語句:

結果

SELECT * FROM Users WHERE Name ="" or ""="" AND Pass ="" or ""=""

結果的SQL是有效的。 它將從表中的用戶返回所有行,因為WHERE“”=“”始終是真實的。


基於成批的SQL語句的SQL注入

大多數數據庫支持批處理SQL語句,用分號隔開。

SELECT * FROM Users; DROP TABLE Suppliers

在上面的SQL將返回用戶表中的所有行,然後刪除該表稱為供應商。

如果我們有以下服務器的代碼:

服務器代碼

txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;

而下面的輸入:

用戶名:

在服務器上的代碼將創建這樣一個有效的SQL語句:

結果

SELECT * FROM Users WHERE UserId = 105; DROP TABLE Suppliers

為保護參數

一些Web開發人員使用一個"blacklist"字或字符的搜索中輸入的SQL語句,防止SQL注入攻擊。

這不是一個很好的主意。 許多這樣的話(如刪除或下降)和字符(比如分號和引號),在共同的語言使用,並且應當在許多類型的輸入被允許。

(事實上,它應該是完全合法的輸入數據庫字段的SQL語句。)

以防止SQL注入攻擊一個網站的唯一行之有效的方法,是使用SQL參數。

SQL參數是添加到SQL查詢在執行時,以受控的方式值。

ASP.NET剃刀例

txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = @0";
db.Execute(txtSQL,txtUserId);

注意,參數在由一個@標記在SQL語句來表示。

SQL引擎檢查每個參數,以確保它是用於其列正確的和是從字面上處理,而不是作為要執行的SQL的一部分。

另一個例子

txtNam = getRequestString("CustomerName");
txtAdd = getRequestString("Address");
txtCit = getRequestString("City");
txtSQL = "INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)";
db.Execute(txtSQL,txtNam,txtAdd,txtCit);

你剛才教訓,避免SQL注入。 一個頂級網站的漏洞。


例子

下面的例子演示了如何在一些常見的網絡語言構建參數化查詢。

SELECT語句ASP.NET:

txtUserId = getRequestString("UserId");
sql = "SELECT * FROM Customers WHERE CustomerId = @0";
command = new SqlCommand(sql);
command.Parameters.AddWithValue("@0",txtUserID);
command.ExecuteReader();

INSERT INTO聲明在ASP.NET:

txtNam = getRequestString("CustomerName");
txtAdd = getRequestString("Address");
txtCit = getRequestString("City");
txtSQL = "INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)";
command = new SqlCommand(txtSQL);
command.Parameters.AddWithValue("@0",txtNam);
command.Parameters.AddWithValue("@1",txtAdd);
command.Parameters.AddWithValue("@2",txtCit);
command.ExecuteNonQuery();

INSERT INTO聲明在PHP中:

$stmt = $dbh->prepare("INSERT INTO Customers (CustomerName,Address,City)
VALUES (:nam, :add, :cit)");
$stmt->bindParam(':nam', $txtNam);
$stmt->bindParam(':add', $txtAdd);
$stmt->bindParam(':cit', $txtCit);
$stmt->execute();