常见的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}