XSS案例分析和规避
1. 什么是XSS
跨站脚本(XSS)是一种安全漏洞,允许攻击者向网站注入恶意客户端代码。该代码由受害者执行从而让攻击者绕过访问控制并冒充用户。XSS可能导致cookie、token等用户敏感信息被窃取,或者HTML被重写。
下面是一个极简易的新闻发布功能的案例,前端获取用户输入的标题和新闻内容,服务端接收并保存。用户点击新闻标题列表时将id传给服务端,服务端根据id找到标题和内容,再拼接成html格式的内容发给前端,前端直接输出到页面。
2. 案例代码
前端代码,从input获取新闻标题和内容通过ajax发给服务端
<head> <meta charset="UTF-8"> <title>发布新闻</title> <script> function add() { var title = document.getElementById("txtTitle").value; var content = document.getElementById("txtContent").value; var httpRequest = new XMLHttpRequest() httpRequest.onreadystatechange = function () { if (httpRequest.readyState == 4 && httpRequest.status == 200) { document.getElementById("txtTitle").value = '' document.getElementById("txtContent").value = '' alert('添加成功') } } httpRequest.open('POST', 'http://localhost:8093/news', true) httpRequest.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' ) var str = `title=${title}&content=${content}` httpRequest.send(str) } </script> </head> <body> <div> 标题:<input style="width: 280px;" type="text" name="title" id="txtTitle"> <br> 内容:<textarea name="content" id="txtContent" cols="40" rows="6"></textarea><br> <input type="button" onclick="add()" value="新增"> </div>
服务端保存(java),问题点1:保存没有检查数据格式
@PostMapping public int add(News news) { int num = jdbcTemplate.update("insert into tb_news(id,title,content) values(?,?,?)", new Object[]{UUID.randomUUID().toString(), news.getTitle(), news.getContent()}); return num; }
服务端生成html返回
@GetMapping("/{id}") public String getNews(@PathVariable String id) { News news = jdbcTemplate.queryForObject("select * from tb_news where id = ?", new BeanPropertyRowMapper<>(News.class), id); String html = getTemplate(); html = html.replace("@title", news.getTitle()); html = html.replace("@content", news.getContent()); return html; } private String getTemplate() { String template = "<html>\n" + "<head>\n" + " <title>news</title>\n" + " <meta charset=\"utf-8\">\n" + "</head>\n" + "<body>\n" + "<div style=\"margin: 100px 100px 100px 100px\">\n" + " <h2 style=\"text-align: center;\">@title</h2>\n" + " <p>@content</p>\n" + "</div>\n" + "</body>\n" + "</html>"; return template; }
前端展示新闻列表
window.onload = function () { var httpRequest = new XMLHttpRequest() httpRequest.onreadystatechange = function () { if (httpRequest.readyState == 4 && httpRequest.status == 200) { var news = JSON.parse(httpRequest.responseText) for (var i = 0; i < news.length; i++) { showNews(news[i]) } } } httpRequest.open('GET', 'http://localhost:8093/news', true) httpRequest.send() } //新闻列表 function showNews(news) { var li = document.createElement("li"); li.innerHTML = `<a href="javascript:view('${news.id}')">${news.title}</a>`; document.getElementById("news").append(li); }
前端展示新闻详情,问题点2:用document.write输出
//从服务端获取新闻内容展示到页面
function view(id) {
var httpRequest = new XMLHttpRequest()
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
//演示xss
document.write(httpRequest.responseText)
}
httpRequest.open('GET', `http://localhost:8093/news/${id}`, true)
httpRequest.send()
}
演示弹出alter消息
演示效跳转网站