OK,本系列博客主要讲解互联网安全架构,包括互联网常见的漏洞分析以及如何防护。后续还有安全框架的学习。
Web 安全常见的攻击手段
XSS、SQL注入、盗取链接、CSRF、忘记密码漏洞、文件上传漏洞、DDOS攻击等
1、什么是XSS攻击手段
XSS攻击使用Javascript脚本注入进行攻击,例如在提交表单后,展示到另一个页面,可能会受到XSS脚本注入,读取本地cookie远程发送给黑客服务器端。
比如:<script>window.location.href='http://www.heike.com';</script>
XSS 攻击百科:https://baike.so.com/doc/6839031-7056294.html
如何防御XSS攻击
将脚本特殊字符,转换成 html 源代码进行展示。一般步骤:编写过滤器拦截所有 getParameter 参数,重写 httpservletwrapp 方法,将参数特殊字符转换成 html 源代码保存。
// 重写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;
}
}
SpringBoot启动加上注解 @ServletComponentScan
2、3、什么是SQL注入?
SQL注入:利用现有应用程序,将(恶意)的SQL命令注入到后台数据库执行一些恶意的操作。造成SQL注入的原因是因为程序没有有效过滤用户的输入,使攻击者成功的向服务器提交恶意的SQL查询代码,程序在接收后错误的将攻击者的输入作为查询语句的一部分执行,导致原始的查询逻辑被改变,额外的执行了攻击者精心构造的恶意代码。
SQL注入防攻击手段
不要使用拼接SQL语句方式。最好使用预编译方式,在 mybatis 编写sql语句的时候,最好使用 # 传参数方式,不要使用 $ 传参数,因为 $ 传参数方式,可能会受到 sql 语句攻击。
示例:http://127.0.0.1:8080/login?userName='zhangsan'&password='123' or 1=1
MyBatis #与$ 区别
#{}:解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个 #{ } 被解析为一个参数占位符,可以防止SQL注入问题。
${}:仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。
3、Http请求防盗链
什么是防盗链?
比如A网站有一张图片,被B网站直接通过 img 标签属性引入,直接盗用A网站图片展示。导致网络带宽被占用等风险。
如何实现防盗链?
判断 http 请求头 Referer 域中的记录来源的值,如果和当前访问的域名不一致的情况下,说明该图片可能被其他服务器盗用。
使用过滤器判断请求头 Referer 记录请求来源,如果不是指定域名,返回错误的图片:
@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() {
}
}
4、什么是CSRF攻击?
Cross Site Request Forgery,跨站域请求伪造,通过伪装来自受信任用户的请求来利用受信任的网站(简单来说就是伪造、篡改请求参数)。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
防御CSRF攻击手段
使用图形验证码防止机器模拟接口请求攻击,在调用核心业务接口时,比如支付、下单、等接口,增加数据的签名验证,或者使用手机短信验证验证或者是人脸识别,防止其他用户使用 Token 伪造请求。
5、忘记密码漏洞
很多网站都有忘记密码、重新找回密码的功能,其实也存在一定的漏洞。如果在找回密码的页面没有加控制,就容易被暴力破解:黑客使用抓包工具分析 Http 请求,在忘记密码找回时,需要发送一条短信验证码,如果验证码数字比较短的话,很容易使用暴力破解方式攻击破。
找回密码防止暴力破解的防御手段
忘记密码验证码最好在6-8位。一旦频繁调用接口验证超过一定的错误次数时,应该使用图形验证码拦截,防止机器模拟。还有看使用黑名单和白名单机制,防御攻击。
6、上传文件漏洞
漏洞描述
上传漏洞这个顾名思义,就是攻击者通过上传木马文件,直接得到 WEBSHELL,危害等级超级高,现在的入侵中上传漏洞也是常见的漏洞。
导致该漏洞的原因在于代码没有对访客提交的数据进行检验或者过滤不严,让用户可以直接提交修改过的数据绕过扩展名的检验。比如用户上传一个可执行文件 exe、或者 jsp、或者其它可执行的脚本文件。
修复方案
1、对文件格式限制,只允许某些格式上传(可以在前端限制)。
2、服务器端要通过读取文件流的形式判断文件的格式。
3、将上传目录放置到项目工程目录之外,当做静态资源文件路径,并且对文件的权限进行设定,禁止文件下的执行权限。
例如:判断文件流是否为图片格式
/**
* 文件上传
*/
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;
}
7、其他攻击和漏洞
①不要将错误信息、异常直接抛出给接口调用者,这样会给攻击者提示。需要封装成统一的错误提示给调用者。
②上线时,把前端的 html、js 等注释掉的功能全部去掉,避免攻击者拿到源代码后,把注释的内容拿去执行。
8、URL转码
什么是 URL 转码?
不管是以何种方式传递 url 时,如果要传递的url中包含特殊字符,如想要传递一个 +,但是这个 + 会被 url 会被编码成空格,想要传递&,被url处理成分隔符。
尤其是当传递的url是经过Base64加密或者RSA加密后的,存在特殊字符时,这里的特殊字符一旦被url处理,就不是原先你加密的结果了。
url特殊符号及对应的编码:
符号 | url中的含义 | 编码 |
+ | URL 中+号表示空格 | %2B |
空格 | URL中的空格可以用+号或者编码 | %20 |
/ | 分隔目录和子目录 | %2F |
? | 分隔实际的URL和参数 | %3F |
% | 指定特殊字符 | %25 |
# | 表示书签 | %23 |
& | URL中指定的参数间的分隔符 | %26 |
= | URL中指定参数的值 | %3D |
Java代码处理转码:URLEncoder.encode和decode
//转码
String encode = URLEncoder.encode("1+1", "UTF-8");
//解码
String decode = URLDecoder.decode(encode, "UTF-8");
例如(注意只需要转码参数即可,URL 地址不要转码,否则导致错误):
String url = "http://127.0.0.1:8080/getInfo?";
// 参数转码
String strParam = "username=" + URLEncoder.encode("1+1", "utf-8");
String newUrl = url + strParam;
String result = HttpClientUtils.httpGet(newUrl);
OK,关于 Web 的常见漏洞以及修复方案讲解到这。