常見的Web安全漏洞
(1). XSS 攻擊
什麼是XSS攻擊手段
XSS攻擊使用Javascript腳本注入進行攻擊
例如在提交表單後,展示到另一個頁面,可能會受到XSS腳本注入,讀取本地cookie遠程發送給黑客服務器端。
// 重寫HttpServletRequestWrapper 防止XSS攻擊 public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { private HttpServletRequest request; /** * @param request */ public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); this.request = request; } @Override public String getParameter(String name) { // 過濾getParameter參數 檢查是否有特殊字符 String value = super.getParameter(name); System.out.println("value:" + value); if (!StringUtils.isEmpty(value)) { // 將中文轉換爲字符編碼格式,將特殊字符變爲html源代碼保存 value = StringEscapeUtils.escapeHtml(value); System.out.println("newValue:" + value); } return value; } }
|
@SpringBootApplication @ServletComponentScan public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
|
(2).SQL注入攻擊
@RestController public class LoginController { @Autowired private UserMapper userMapper; @RequestMapping("/login") public String login(UserEntity userEntity) { System.out.println("賬號密碼信息:userEntity:" + userEntity.toString()); UserEntity login = userMapper.login(userEntity); return login == null ? "登陸失敗!" : "登陸成功!"; } } public interface UserMapper { @Select(" SELECT * FROM user_info where userName=${userName} and password=${password}") public UserEntity login(UserEntity userEntity); }
|
@WebFilter(filterName = "imgFilter", urlPatterns = "/imgs/*") public class ImgFilter implements Filter { @Value("${domain.name}") private String domainName; public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String referer = req.getHeader("Referer"); if (StringUtils.isEmpty(referer)) { request.getRequestDispatcher("/imgs/error.png").forward(request, response); return; } String domain = getDomain(referer); if (!domain.equals(domainName)) { request.getRequestDispatcher("/imgs/error.png").forward(request, response); return; } chain.doFilter(request, response); } /** * 獲取url對應的域名 * * @param url * @return */ public String getDomain(String url) { String result = ""; int j = 0, startIndex = 0, endIndex = 0; for (int i = 0; i < url.length(); i++) { if (url.charAt(i) == '/') { j++; if (j == 2) startIndex = i; else if (j == 3) endIndex = i; } } result = url.substring(startIndex + 1, endIndex); return result; } public void destroy() { } }
|
public class TokenUtils { private static Map<String, Object> tokenMap = new ConcurrentHashMap<String, Object>(); // 獲取token public static synchronized String getToken() { // 1.生成令牌 String token = "token-" + System.currentTimeMillis(); // 2.存入tokenMap tokenMap.put(token, token); return token; } // 驗證token,並且刪除對應的token public static Boolean exisToken(String token) { // 1.從集合中獲取token Object result = tokenMap.get(token); if (result == null) { return false; } // 2.刪除對應的token tokenMap.remove(token); return true; } }
|
@RestController public class OrderController { @Autowired private OrderMapper orderMapper; // 獲取Token @RequestMapping("/getToken") public String getToken() { return TokenUtils.getToken(); } // 驗證Token @RequestMapping(value = "/addOrder", produces = "application/json; charset=utf-8") public String addOrder(@RequestBody OrderEntity orderEntity, HttpServletRequest request) { String token = request.getHeader("token"); if (StringUtils.isEmpty(token)) { return "參數錯誤!"; } if (!TokenUtils.exisToken(token)) { return "請勿重複提交!"; } int result = orderMapper.addOrder(orderEntity); return result > 0 ? "添加成功" : "添加失敗" + ""; } }
|
<dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> </dependencies>
|
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <form action="/load/UploadServlet" method="post" enctype="multipart/form-data"> <input type="file" name="file" /> <input type="submit" value="submit" /> </form> </body> </html>
|
public class UploadServletextends HttpServlet { /** * 文件上傳 */ protected void doPost(HttpServletRequest request, HttpServletResponse response) { String root = request.getServletContext().getRealPath("/upload"); DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); try { List<FileItem> list = upload.parseRequest(request); for (FileItem it : list) { // 如果是file文件類型 if (!it.isFormField()) { it.write(new File(root + "/" + it.getName())); response.getWriter().write("success"); } } } catch (Exception e) { try { response.getWriter().write("exception"); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } }
|
<%@page import="java.io.File"%> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <% String root ="F:\\itmayiedu"; File file = new File(root); file.delete(); %>
|
/** * 文件上傳 */ protected void doPost(HttpServletRequest request, HttpServletResponse response) { String root = request.getServletContext().getRealPath("/upload"); DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); try { List<FileItem> list = upload.parseRequest(request); for (FileItem it : list) { // 如果是file文件類型 if (!it.isFormField()) { FileType fileType = getFileType(it.getInputStream()); if (fileType == null) { // 非圖片格式 response.getWriter().write("fail"); return; } String imgValue = fileType.getValue(); System.out.println("imgValue:" + imgValue); // 是圖片格式 it.write(new File(root + "/" + it.getName())); response.getWriter().write("success"); } } } catch (Exception e) { try { response.getWriter().write("exception"); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } // 判斷文件是圖片格式 public static FileType getFileType(InputStream is) throws IOException { byte[] src = new byte[28]; is.read(src, 0, 28); StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v).toUpperCase(); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } FileType[] fileTypes = FileType.values(); for (FileType fileType : fileTypes) { if (stringBuilder.toString().startsWith(fileType.getValue())) { return fileType; } } return null; }
|
(4).基於令牌方式實現接口參數安全傳輸
請求參數示例:
字段 數據 類型 描述
phone 158112341234 String 手機號
timestamp 1538990392850 long 時間戳
deviceId mo-231odvepqd String 設備id
sign 158112341234 String 接口簽名,
md5(參數字典排序後加鹽)
注:
1.參數排序時,不包括sign參數
2.加鹽參數使用desKey,放在參數字典排序後的開頭位置
3.遇到數值拼接時,需統一轉換成字符串類型。如:123 需轉換成 “123”
4.爲避免客戶端和服務器端簽名對null處理不一致,若參數值爲null時,請轉換成空字符串("")
簽名步驟
1.字段字典排序:
deviceId phone timestamp
2.參數拼接:
mo-231odvepqd1581123412341538990392850
3.加鹽:
hechaojie.commo-231odvepqd1581123412341538990392850
這裏加鹽參數爲:hechaojie.com,請注意安全存儲。
4.md5簽名:
21bf08491dbf4013a8e9275f1d603e4a
客戶端發送數據示例:
{"phone":"158112341234","sign":"21bf08491dbf4013a8e9275f1d603e4a","deviceId":"mo-231odvepqd","timestamp":1538990392850}