简述
人们经常将跨站脚本攻击(Cross Site Scripting)缩写为CSS,但这会与层叠样式表(Cascading Style Sheets,CSS)的缩写混淆。因此,有人将跨站脚本攻击缩写为XSS。
跨站脚本攻击(XSS),是最普遍的Web应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。
攻击者可以使用户在浏览器中执行其预定义的恶意脚本,其导致的危害可想而知,如劫持用户会话,插入恶意内容、重定向用户、使用恶意软件劫持用户浏览器、繁殖XSS蠕虫,甚至破坏网站、修改路由器配置信息等。
XSS漏洞可以追溯到上世纪90年代。大量的网站曾遭受XSS漏洞攻击或被发现此类漏洞,如Twitter、Facebook、MySpace、Orkut、新浪微博和百度贴吧。研究表明,最近几年XSS已经超过缓冲区溢出成为最流行的攻击方式,有68%的网站可能遭受此类攻击。根据开放网页应用安全计划(Open Web Application Security Project)公布的2010年统计数据,在Web安全威胁前10位中,XSS排名第2,仅次于代码注入(Injection)。
正文
pom文件依赖
<!--jsoup--> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.13.1</version> </dependency>
工具类代码
import org.jsoup.Jsoup; import org.jsoup.safety.Whitelist; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Xss防御工具类 * * @author tarzan liu * @version V1.0 * @date 2021年7月11日 */ public class XssKillerUtil { private static final String[] WHITE_LIST = {"p", "strong", "pre", "code", "span", "blockquote", "em", "a"}; private static String reg = null; private static String legalTags = null; static { StringBuilder regSb = new StringBuilder("<"); StringBuilder tagsSb = new StringBuilder(); for (String s : WHITE_LIST) { regSb.append("(?!").append(s).append(" )"); tagsSb.append('<').append(s).append('>'); } regSb.append("(?!/)[^>]*>"); reg = regSb.toString(); legalTags = tagsSb.toString(); } /** * xss白名单验证 * * @param xssStr * @return */ public static boolean isValid(String xssStr) { if (null == xssStr || xssStr.isEmpty()) { return true; } Pattern pattern = Pattern.compile(reg); Matcher matcher = pattern.matcher(xssStr); while (matcher.find()) { String tag = matcher.group(); if (!legalTags.contains(tag.toLowerCase())) { return false; } } return true; } /** * 自定义的白名单 * * @return */ private static Whitelist custom() { return Whitelist.none().addTags("p", "strong", "pre", "code", "span", "blockquote", "br").addAttributes("span", "class"); } /** * 根据白名单,剔除多余的属性、标签 * * @param xssStr * @return */ public static String clean(String xssStr) { if (null == xssStr || xssStr.isEmpty()) { return ""; } return Jsoup.clean(xssStr, custom()); } }
使用示例代码
@PostMapping("comment/save") public ResponseVo saveComment(HttpServletRequest request, Comment comment) throws UnsupportedEncodingException { if (StringUtils.isEmpty(comment.getNickname())) { return ResultUtil.error("请输入昵称"); } String content = comment.getContent(); if (!XssKillerUtil.isValid(content)) { return ResultUtil.error("内容不合法"); } content = XssKillerUtil.clean(content.trim()).replaceAll("(<p><br></p>)|(<p></p>)", ""); Date date = new Date(); comment.setContent(content); comment.setIp(IpUtil.getIpAddr(request)); comment.setCreateTime(date); comment.setUpdateTime(date); if (StringUtils.isNotBlank(comment.getQq())) { comment.setAvatar("http://q1.qlogo.cn/g?b=qq&nk=" + comment.getQq() + "&s=100"); } else if (StringUtils.isNotBlank(comment.getEmail())) { String entry = null; try { entry = MD5Util.md5Hex(comment.getEmail()); } catch (NoSuchAlgorithmException e) { log.error("MD5出现异常{}", e.getMessage(), e); } comment.setAvatar("http://www.gravatar.com/avatar/" + entry + "?d=mp"); } boolean a = commentService.save(comment); if (a) { return ResultUtil.success("评论提交成功,系统正在审核"); } else { return ResultUtil.error("评论提交失败"); } }
上期文章