👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 贈書福利
全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 : http://116.62.199.48/ , 新計畫正在醞釀中 。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了239小節,累計38w+字,講解圖:1645張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,
1. XSS跨站指令碼攻擊
① XSS漏洞介紹
跨站指令碼攻擊XSS是指攻擊者往Web頁面裏插入惡意Script程式碼,當使用者瀏覽該頁之時,嵌入其中Web裏面的Script程式碼會被解析執行,從而達到惡意攻擊使用者的目的。XSS攻擊針對的是使用者層面的攻擊!
② XSS漏洞分類
儲存型XSS: 儲存型XSS,持久化,程式碼是儲存在伺服器中的,如在個人資訊或發表文章等地方,插入程式碼,如果沒有過濾或過濾不嚴,那麽這些程式碼將儲存到伺服器中,使用者存取該頁面的時候觸發程式碼執行。這種XSS比較危險,容易造成蠕蟲,盜竊cookie
反射型XSS: 非持久化,需要欺騙使用者自己去點選連結才能觸發XSS程式碼(伺服器中沒有這樣的頁面和內容),一般容易出現在搜尋頁面
DOM型XSS:
不經過後端,DOM-XSS漏洞是基於文件物件模型(
Document Objeet Model
,DOM)的一種漏洞,DOM-XSS是透過url傳入參數去控制觸發的,其實也屬於反射型XSS。
③ 防護建議
限制使用者輸入,表單數據規定值得型別,例如年齡只能是int,name為字母數位組合。
對數據進行html encode處理。
過濾或移除特殊的html標簽。
過濾javascript事件的標簽。
2. SQL資料隱碼攻擊
① SQL註入漏洞介紹
SQL註入(SQLi)是一種隱碼攻擊,可以執行惡意SQL語句。它透過將任意SQL程式碼插入資料庫查詢,使攻擊者能夠完全控制Web應用程式後面的資料庫伺服器。攻擊者可以使用SQL註入漏洞繞過應用程式安全措施;可以繞過網頁或Web應用程式的身份驗證和授權,並檢索整個SQL資料庫的內容;還可以使用SQL註入來添加,修改和刪除資料庫中的記錄;
SQL註入漏洞可能會影響使用SQL資料庫(如MySQL,Oracle,SQL Server或其他)的任何網站或Web應用程式。犯罪分子可能會利用它來未經授權存取使用者的敏感數據:客戶資訊,個人數據,商業機密,智慧財產權等。SQL資料隱碼攻擊是最古老,最流行,最危險的Web應用程式漏洞之一。
②防護建議
使用mybatis中
#{}
可以有效防止sql註入。
使用
#{}
時:
<select id="getBlogById" resultType="Blog" parameterType=」int」>
select id,title,author,content
from blog where id=#{id}
</select>
打印出執行的sql語句,會看到sql是這樣的:
select id,title,author,content from blog where id = ?
不管輸入什麽參數,打印出的sql都是這樣的。這是因為mybatis啟用了預編譯功能,在sql執行前,會先將上面的sql發送給資料庫進行編譯,執行時,直接使用編譯好的sql,替換占位符「?」就可以了。因為sql註入只能對編譯過程起作用,所以像#{}這樣預編譯成?的方式就很好地避免了sql註入的問題。
mybatis是如何做到sql預編譯的呢?
其實在框架底層,是jdbc中的
PreparedStatement
類在起作用,
PreparedStatement
是我們很熟悉的Statement的子類別,它的物件包含了編譯好的sql語句。這種「準備好」的方式不僅能提高安全性,而且在多次執行一個sql時,能夠提高效率,原因是sql已編譯好,再次執行時無需再編譯。
使用
${}
時
<select id="orderBlog" resultType="Blog" parameterType=」map」>
select id,title,author,content
from blog order by ${orderParam}
</select>
仔細觀察,行內參數的格式由「
#{xxx}
」變為了
${xxx}
。如果我們給參數「
orderParam
」賦值為」id」,將sql打印出來,是這樣的:
select id,title,author,contet from blog order by id
顯然,這樣是無法阻止sql註入的,參數會直接參與sql編譯,從而不能避免隱碼攻擊。但涉及到動態表名和列名時,只能使用「
${}
」這樣的參數格式,所以,這樣的參數需要我們在程式碼中手工進行處理來防止註入。
其實,這些都在Java面試庫小程式上都有答案,如果你近期準備面試跳槽,建議在上面刷題,涵蓋 2000+ 道 Java 面試題,幾乎覆蓋了所有主流技術面試題。
3. SpringBoot中如何防止XSS攻擊和sql註入
對於Xss攻擊和Sql註入,我們可以透過過濾器來搞定,可根據業務需要排除部份請求
① 建立Xss請求過濾類
XssHttpServletRequestWraper
![圖片](https://mmbiz.qpic.cn/mmbiz_png/19cc2hfD2rDk4RBjVAbd2MVMGXnZEaSpOsT5Rv3V5mGYAUH8ic8vEmV4AewXdbeSsO8lhwX7DcA0s69WtHPeTxw/640?wx_fmt=png&tp=wxpic&wxfrom=5&wx_lazy=1&wx_co=1)
程式碼如下:
public class XssHttpServletRequestWraper extends HttpServletRequestWrapper {
Logger log = LoggerFactory.getLogger(this.get class());
public XssHttpServletRequestWraper() {
super(null);
}
public XssHttpServletRequestWraper(HttpServletRequest httpservletrequest) {
super(httpservletrequest);
}
//過濾springmvc中的 @RequestParam 註解中的參數
public String[] getParameterValues(String s) {
String str[] = super.getParameterValues(s);
if (str == null) {
return null;
}
int i = str.length;
String as1[] = new String[i];
for (int j = 0; j < i; j++) {
//System.out.println("getParameterValues:"+str[j]);
as1[j] = cleanXSS(cleanSQLInject(str[j]));
}
log.info("XssHttpServletRequestWraper凈化後的請求為:==========" + as1);
return as1;
}
//過濾request.getParameter的參數
public String getParameter(String s) {
String s1 = super.getParameter(s);
if (s1 == null) {
return null;
} else {
String s2 = cleanXSS(cleanSQLInject(s1));
log.info("XssHttpServletRequestWraper凈化後的請求為:==========" + s2);
return s2;
}
}
//過濾請求體 json 格式的
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(inputHandlers(super.getInputStream ()).getBytes ());
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public boolean isFinished() {
returnfalse;
}
@Override
public boolean isReady() {
returnfalse;
}
@Override
public void setReadListener(ReadListener readListener) { }
};
}
public String inputHandlers(ServletInputStream servletInputStream){
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8")));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (servletInputStream != null) {
try {
servletInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return cleanXSS(sb.toString ());
}
public String cleanXSS(String src) {
String temp = src;
src = src.replaceAll("<", "<").replaceAll(">", ">");
src = src.replaceAll("\\(", "(").replaceAll("\\)", ")");
src = src.replaceAll("'", "'");
src = src.replaceAll(";", ";");
//bgh 2018/05/30 新增
/**-----------------------start--------------------------*/
src = src.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
src = src.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41");
src = src.replaceAll("eval\\((.*)\\)", "");
src = src.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
src = src.replaceAll("script", "");
src = src.replaceAll("link", "");
src = src.replaceAll("frame", "");
/**-----------------------end--------------------------*/
Pattern pattern = Pattern.compile("(eval\\((.*)\\)|script)",
Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(src);
src = matcher.replaceAll("");
pattern = Pattern.compile("[\\\"\\'][\\s]*javascript:(.*)[\\\"\\']",
Pattern.CASE_INSENSITIVE);
matcher = pattern.matcher(src);
src = matcher.replaceAll("\"\"");
// 增加指令碼
src = src.replaceAll("script", "").replaceAll(";", "")
/*.replaceAll("\"", "").replaceAll("@", "")*/
.replaceAll("0x0d", "").replaceAll("0x0a", "");
if (!temp.equals(src)) {
// System.out.println("輸入資訊存在xss攻擊!");
// System.out.println("原始輸入資訊-->" + temp);
// System.out.println("處理後資訊-->" + src);
log.error("xss攻擊檢查:參數含有非法攻擊字元,已禁止繼續存取!!");
log.error("原始輸入資訊-->" + temp);
throw new CustomerException("xss攻擊檢查:參數含有非法攻擊字元,已禁止繼續存取!!");
}
return src;
}
//輸出
public void outputMsgByOutputStream(HttpServletResponse response, String msg) throws IOException {
ServletOutputStream outputStream = response.getOutputStream(); //獲取輸出流
response.setHeader("content-type", "text/html;charset=UTF-8"); //透過設定響應頭控制瀏覽器以UTF-8的編碼顯示數據,如果不加這句話,那麽瀏覽器顯示的將是亂碼
byte[] dataByteArr = msg.getBytes("UTF-8");// 將字元轉換成字節陣列,指定以UTF-8編碼進行轉換
outputStream.write(dataByteArr);// 使用OutputStream流向客戶端輸出字節陣列
}
// 需要增加通配,過濾大小寫組合
public String cleanSQLInject(String src) {
String lowSrc = src.toLowerCase();
String temp = src;
String lowSrcAfter = lowSrc.replaceAll("insert", "forbidI")
.replaceAll("select", "forbidS")
.replaceAll("update", "forbidU")
.replaceAll("delete", "forbidD").replaceAll("and", "forbidA")
.replaceAll("or", "forbidO");
if (!lowSrcAfter.equals(lowSrc)) {
log.error("sql註入檢查:輸入資訊存在SQL攻擊!");
log.error("原始輸入資訊-->" + temp);
log.error("處理後資訊-->" + lowSrc);
throw new CustomerException("sql註入檢查:參數含有非法攻擊字元,已禁止繼續存取!!");
}
return src;
}
}
② 把請求過濾類
XssHttpServletRequestWraper
添加到Filter中,註入容器
@Component
public class XssFilter implements Filter {
Logger log = LoggerFactory.getLogger(this.get class());
// 忽略許可權檢查的url地址
private final String[] excludeUrls = new String[]{
"null"
};
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) arg0;
HttpServletResponse response = (HttpServletResponse) arg1;
String pathInfo = req.getPathInfo() == null ? "" : req.getPathInfo();
//獲取請求url的後兩層
String url = req.getServletPath() + pathInfo;
//獲取請求你ip後的全部路徑
String uri = req.getRequestURI();
//註入xss過濾器例項
XssHttpServletRequestWraper reqW = new XssHttpServletRequestWraper(req);
//過濾掉不需要的Xss校驗的地址
for (String str : excludeUrls) {
if (uri.indexOf(str) >= 0) {
arg2.doFilter(arg0, response);
return;
}
}
//過濾
arg2.doFilter(reqW, response);
}
public void destroy() {
}
public void init(FilterConfig filterconfig1) throws ServletException {
}
}
上述程式碼已經可以完成 請求參數、JSON請求體 的過濾,但對於json請求體還有其他的方式實作,有興趣的請看下面的擴充套件!
擴充套件:還可以重寫spring中的
MappingJackson2HttpMessageConverter
來過濾Json請求體
因為請求體在進出Contoroller時,會經過
MappingJackson2HttpMessageConverter
的一個轉換,把請求體轉換成我們需要的json格式,所以可以在這裏邊做一些修改!
@Configuration
public class MyConfiguration {
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
//自訂轉換器
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//轉換器日期格式設定
ObjectMapper objectMapper = new ObjectMapper();
SimpleDateFormat smt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
objectMapper.setDateFormat(smt);
converter.setObjectMapper(objectMapper);
//轉換器添加自訂Module擴充套件,主要是在這裏做XSS過濾的!!,其他的是其他業務,不用看
SimpleModule simpleModule = new SimpleModule();
//添加過濾邏輯類!
simpleModule.addDeserializer(String. class,new StringDeserializer());
converter.getObjectMapper().registerModule(simpleModule);
//設定中文編碼格式
List<MediaType> list = new ArrayList<>();
list.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(list);
return converter;
}
}
真正的過濾邏輯類
StringDeserializer
:
//檢驗請求體的參數
@Component
public class StringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String str = jsonParser.getText().trim();
//sql註入攔截
if (sqlInject(str)) {
throw new CustomerException("參數含有非法攻擊字元,已禁止繼續存取!");
}
return xssClean(str);
}
public boolean sqlInject(String str) {
if (StringUtils.isEmpty(str)) {
returnfalse;
}
//去掉'|"|;|\字元
str = org.apache.commons.lang3.StringUtils.replace(str, "'", "");
str = org.apache.commons.lang3.StringUtils.replace(str, "\"", "");
str = org.apache.commons.lang3.StringUtils.replace(str, ";", "");
str = org.apache.commons.lang3.StringUtils.replace(str, "\\", "");
//轉換成小寫
str = str.toLowerCase();
//非法字元
String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alert","alter", "drop"};
//判斷是否包含非法字元
for (String keyword : keywords) {
if (str.indexOf(keyword) != -1) {
returntrue;
}
}
returnfalse;
}
//xss攻擊攔截
public String xssClean(String value) {
if (value == null || "".equals(value)) {
return value;
}
//非法字元
String[] keywords = {"<", ">", "<>", "()", ")", "(", "javascript:", "script","alter", "''","'"};
//判斷是否包含非法字元
for (String keyword : keywords) {
if (value.indexOf(keyword) != -1) {
throw new CustomerException("參數含有非法攻擊字元,已禁止繼續存取!");
}
}
return value;
}
}
使用這種形式也可以完成json請求體的過濾,但個人更推薦使用
XssHttpServletRequestWraper
的形式來完成xss過濾!!
👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 贈書福利
全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 : http://116.62.199.48/ , 新計畫正在醞釀中 。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了239小節,累計38w+字,講解圖:1645張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,
1.
2.
3.
4.
最近面試BAT,整理一份面試資料【Java面試BATJ通關手冊】,覆蓋了Java核心技術、JVM、Java並行、SSM、微服務、資料庫、數據結構等等。
獲取方式:點「在看」,關註公眾號並回復 Java 領取,更多內容陸續奉上。
PS:因公眾號平台更改了推播規則,如果不想錯過內容,記得讀完點一下「在看」,加個「星標」,這樣每次新文章推播才會第一時間出現在你的訂閱列表裏。
點「在看」支持小哈呀,謝謝啦