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();