WEB服務端安全---註入攻擊
註入攻擊是web領域最為常見的攻擊方式,其本質是把用戶輸入的數據當做代碼執行,主要原因是違背了數據與代碼分離原則,其發生的兩個條件:用戶可以控制數據輸入;代碼拼接了用戶輸入的數據,把數據當做代碼執行了。
下面是幾種常見註入攻擊及其防禦方法:
SQL註入及常見攻擊技巧
經典註入 如:
$username = $_POST[‘username‘];
$sql = "select * from usertable where username="."‘".$username."‘"
正常情況下用戶輸入 ‘Tom’ ,sql為
"select * from usertable where username=‘Tom‘";
但如果用戶輸入 ”Tom‘ ; drop table usertable--“,sql如下:
"select * from usertable where username=‘Tom‘;drop table usertable--";
如果我們的服務端代碼,數據庫權限沒做任何處理,那麽結果傷不起。
並且當用戶輸入了可以導致拼接後sql有語法錯的時候,如果服務器開啟了錯誤顯示,那麽將會是攻擊更加便利容易,因為錯誤信息可能會暴露我們的數據庫信息或者sql語句的雛形等等。
盲註:
大多數情況下服務器是關閉錯誤回顯的,沒有提示的情況下,攻擊者又總結出了盲註:sql註入過程中,sql語句執行的選擇後,選擇的數據不能回顯到前端頁面。此時,攻擊者需要利用一些方法進行判斷或者嘗試,這個過程稱之為盲註。
攻擊者通過真假命題的拼接根據頁面返回情況判斷sql漏洞是否存在。
比如:http://news.com/newsIndo.php?id=1,執行的sql為 select * from newstable where id =1;
攻擊者先是http://news.com/newsIndo.php?id=1 and 1=2 然後 http://news.com/newsIndo.php?id=1 and 1=1
and 1=2 時 通常是空頁面 或者錯誤信息,如果 and 1=1的時候頁面正常返回,就說明,and語句成立,也就說明sql漏洞存在,攻擊者不知道會拼接什麽樣的壞心思...
盲註高級技巧Timing Attack 利用數據庫自身函數的的執行造成的返回變化來判斷漏洞是否存在,比如 MySQL BENCHMARK()函數。
數據庫攻擊機巧
常見Payload
SQL註入可以猜解出數據庫的對應版本,比如下面這段Payload,如果MySQL的版本是4,則會返回true。
http://www.site.com/news.php?id=5 and substring(@@version,1,1)=4
下面這段Payload,則是利用union select來分別確認表名admin是否存在,列名passwd是否存在:
id=5 union all select 1,2,3 from admin
id=5 union all select 1,2,passwd from admin
進一步,想要猜解出username和password具體的值,可以通過判斷字符的範圍,一步步讀出來。
id=5 and ascii(substring((select concat(username,0x3a,passwd) from users limit 0,1),1,1))>64
這個過程非常的繁瑣,所以非常有必要使用一個自動化工具來幫助完成整個過程。
sqlmap.py就是一個非常好的自動化註入工具。
在註入攻擊的過程中,常常會用到一些讀寫文件的技巧。比如在MySQL中,就可以通過LOAD_FILE()讀取系統文件,並通過INTO DUMPFILE寫入本地文件。當然這要求當前數據庫用戶有讀寫系統相應文件或目錄的權限。
命令執行
在MySQL中,除了可以通過導出webshell間接地執行命令外,還可以利用“用戶自定義函數”的技巧,即UDF(user-defined functions)來執行命令。
攻擊存儲過程
一些存儲過程也是對攻擊過程有幫助的,比如:SQL Server 中的xp_cmdshell,註入時可以利用其執行系統命令。存儲過程本省也可能給存在註入漏洞。
編碼問題
有時候不同的字符編碼也可能導致註入漏洞的出現,比如 轉義符 ‘\’ 可能會因為字符編碼的不同被省略掉,保證數據庫,操作系統,web應用的字符編碼統一可以避免此類問題。
防禦SQL註入
使用預編譯語句
一般來說,防禦sql註入的最佳方式就是使用預編譯語句,綁定變量。使用預編譯的sql語句,語句的語義不會發生改變。在sql中變量用?表示,攻擊者無法改變sql的結構,比如下面PHP綁定變量,使用預編譯語句的示例:
1 $sql = "insert into usertable (username,password,phone) value(?,?,?)";
2 $stmt = $mysqli->prepare($sql);
3 $stmt = bind_parm("sss",$username,$password,$phone);
4 $username = ‘Tom‘;
5 $password = md5(‘123456‘);
6 $phone = ‘13510345678‘;
7 $stmt->execute();
此時就算攻擊者插入類似於 Tom’ or ‘‘=‘1的字符串,也只會將其當做username來插入,不會改變語句的執行。
檢查數據類型
對輸入數據的數據類型進行檢查,可以很大程度上對抗sql註入,比如整形integer,亦或是郵箱,時間日期等特定格式數據。
使用安全函數
使用足夠安全的編碼函數,比如: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_CODE , req.getParameter("pwd")) +"‘";
最小權限及關閉錯誤回顯
關閉服務器錯誤回顯,避免數據庫信息的暴露,數據庫使用最小權限原則,避免root等高級賬號在web應用中的直接使用,數據庫賬號不應該有創建自定義函數,操作本地文件等權限。
XML註入
$doc = new DOMDocument(‘1.0‘,‘utf-8‘);
$doc -> formatOutput = true;
$user = $doc -> createElement(‘user‘);
$username = $doc -> createElement(‘username‘);
$namevalue = $doc -> createTextNode($_POST[‘username‘]);
$username -> appendChild($namevalue);//將標簽內容賦給標簽
$user -> appendChild($username);
$doc -> appendChild($user);
$doc -> save("user.xml");
正常情況下用戶輸入 Tom將保存下面xml文件
<?xml version="1.0" encoding="utf-8"?> <user> <username>Tom</username> </user>
但是如果用戶輸入惡意數據
Tom</username></user><user><username>Jack</username></user>
此時xml文件
<?xml version="1.0" encoding="utf-8"?> <user> <username>Tom</username> </user> <user> <username>Jack</username> </user>
這就是XML註入。XML註入,可以對用戶輸入數據中的包含”語言本身的保留字符“行進轉義即可。
代碼註入
代碼註入與命令註入都是由一些不安全函數或方法引起的如:eval()
$var = ‘varname‘;
$k = $_GET[‘tag‘];
eval("/$var = $k;");
攻擊者可以通過下面Payload實施代碼註入:
/indexPHP?tag=1;phpinfo()
又比如 system()這個函數,可以通過代碼註入導致命令註入
$error =system(‘cat ‘.$_GET[‘page_id‘],$return_var);
echo $error;
攻擊者通過 index.php?page_id=loquesea;ls利用system函數執行自己想要的系統命令。
防禦對抗代碼註入,命令註入,需要禁用一些像eval(),system()等可以執行系統命令的函數,非要用到就需要對用戶輸入的數據做處理,此外PHP中動態include遠程文件,也是能避免就避免。
CRLF(\r\n)註入
CRLF其實是兩個字符\n\r,常常被用作語義之間的分隔符,因此CRLF註入很可能改變原有的語義,如下
$log = fopen("login.log", "w");
$txt = "user login failed for: \n";
fwrite($log, $txt);
fclose($log);
正常情況會記錄如下在login.log日誌文件中,
user login failed for :user1
user login failed for :user2
但是因為沒有對換行 \n做處理,當攻擊者輸入 user2\nuser2 login succeeded for :user2
日誌中結果就會變為
user login failed for :user1
user login failed for :user2
user login succeeded for :user2
多了一條偽記錄,user2並沒有登錄成功。
CRLF註入不僅僅可以log註入,凡是利用CRLF作為分隔符的地方都可能存在這樣的註入,像是”註入HTTP頭“等。
對抗CRLF註入其實只需要處理好 \r \n這兩個保留字符即可。
寫在最後:註入攻擊主要是違背了數據與代碼分離原則導致的,其先決條件是用戶能控制數據輸入,並且代碼拼接了用戶輸入的數據,把數據也打個代碼執行了。所以,防禦遵從數據域代碼分離原則,在拼接的地方做好安全檢查和處理就能避免此類問題了。
WEB服務端安全---註入攻擊