漏洞英文bug/漏洞文库-小白网安指南

漏洞英文bug/漏洞文库-小白网安指南

本文章记录自己在web开发过程中遇到了常见漏洞并提供对应的解决方案。

所有数据都是有害的

后端95%以上的漏洞都是因为对用户的输入没有做严格的校验,从而产生一系列问题。

所以无论是用户手动填写的数据或是客户端浏览器或操作系统自动填写的数据,都可能产生安全问题,需要进行严格的安全性检查。

SQL注入漏洞SQL注入漏洞分析

什么是SQL注入漏洞?

攻击者通过浏览器或者其他客户端将恶意SQL 语句插入到网站参数中,网站应用程序未经过滤,便将恶意SQL语句带入数据库执行。

通过以上分析可以知道要想完成SQL注入攻击,需要满足以下2个条件:

而在Java中执行SQL语句一般有如下方式:

是Java JDBC下执行SQL语句的一种原生方式,执行语句时是通过拼接来执行, 所以它存在将出现SQL注入漏洞风险。

预编译的方式执行SQL语句,可以有效地防止 SQL注入攻击

这里重点说下使用执行SQL语句,毕竟对于Java开发人员绝大多数情况使用的是。

执行SQL语句

对于执行SQL语句:

那是不是就意味着我们在使用,都直接使用 #{} ?其实也不全是,只能说能用#{} 就用#{},但是对于有些情况我们不得不使用${}进行拼接。

必须使用${}情况

一句话概括:当接收的参数是sql关键字。需要进行sql语句关键字拼接的时候。必须使用${}

案例1:order by语句

需求:通过向sql语句中注入asc或desc关键字,来完成数据的升序或降序排列。

<select id="selectAll" resultType="car">
  select
  id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
  from
  t_car
  order by carNum #{key}
select>

当我们直接测试执行上面的SQL语句时,会报错,因为采用#{}这种方式传值,最终sql语句会是这样:

id, as ,brand, as , as , as from t_car order by 'desc'

desc是一个关键字,不能带单引号的,所以在进行sql语句关键字拼接的时候,必须使用${}

使用${} 改造

<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
  select
  id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
  from
  t_car
  
  order by carNum ${key}
select>

再次执行测试上面的SQL语句就没有问题了。

案例2:拼接表名

在实际的开发中,有的表数据量非常庞大,可能会采用分表方式进行存储,比如每天生成一张表,表的名字与日期挂钩,例如:2022年8月1日生成的表:。2000年1月1日生成的表:。此时前端在进行查询的时候会提交一个具体的日期,比如前端提交的日期为:2000年1月1日,那么后端就会根据这个日期动态拼接表名为:。有了这个表名之后,将表名拼接到sql语句当中,返回查询结果。

那么拼接表名到sql语句当中应该使用#{} 还是 ${} 呢?

使用#{}会是这样: * from 't_car'

使用${}会是这样: * from t_car

所以在拼接表名时也必须使用 ${}

案例3:批量删除

如果我们是通过id来批量删除多条记录,而id的类型是字符串,那这个时候也不得不使用 ${}

如果id类型Long类型,则可以用 #{}

比如根据id来删除用户:

假设现在使用in的方式处理,前端传过来的字符串:1, 2, 3

使用#{} : from where id in('1,2,3') 执行错误:1292 - value: '1,2,3'

SQL注入漏洞修复最佳实践

能使用就使用

只能使用的情况下:

如果能明确参数类型是数值类型,先强制转换成数值类型,可以避免SQL注入

如果参数是字符串类型,则对参数进行过滤校验

下面提供一个参数过滤校验的模板代码:

/**
 * 判单是否存在SQL注入过滤
 *
 * @param str 待验证的字符串
 */
public static boolean sqlInject(String str) {
    if (StringUtils.isBlank(str)) {
        return true;
    }
    //去掉'|"|;|\字符
    str = StringUtils.replace(str, "'", "");
    str = StringUtils.replace(str, """, "");
    str = StringUtils.replace(str, ";", "");
    str = StringUtils.replace(str, "\", "");
    //转换成小写
    str = str.toLowerCase();
    //非法字符
    String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"};
    //判断是否包含非法字符
    for (String keyword : keywords) {
        if (str.contains(keyword)) {
            return false;
        }
    }
    return true;
}

文件上传漏洞文件上传漏洞分析

文件上传漏洞指的是客户端在进行文件上传时,服务端没有进行严格的限制过滤,导致攻击者将可执行脚本()上传至目标服务器,以达到控制目标服务器的目的。

文件上传的各种方式以及其它的绕过一些校验的手段这里不讲,一句话概括:文件上传漏洞的本质还是未对文件名做严格校验,常见的主要有如下几种情况:未对文件做任何过滤,仅在前端通过js检验,只判断了-Type,后缀过滤不全,读取后缀方式错误等。

例如:下面代码尝试获取文件的后缀名

suffixName=fileName.substring(fileName.indexOf("."));

上面这种写法是有问题的,当文件名为abc.jpg.jsp时将等于.jpg.jsp,这明显是不会和黑名单中的后缀相等的

文件上传漏洞修复最佳实践

需求:客户端现在有一个上传图片的功能,服务端的代码该如何写才能避免文件上传漏洞呢?

下面给出代码示例:

@PostMapping("/uplod")
public String uplod(@RequestParam(value = "file") MultipartFile file) {
    if (file.isEmpty()) {
        return "fail";
    }
    String fileName = file.getOriginalFilename();
    String contentType = file.getContentType();
    // 校验文件后缀
    if (fileName != null) {
        String suffix = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
        String[] picWhite = {".jpg", ".jpeg", ".png", ".gif", ".bmp"};
        for (String pic : picWhite) {
            if (!suffix.equals(pic)) {
                return "fail";
            }
        }
    }
    // 校验contentType
    String[] typeWhite = {"image/gif", "image/jpeg", "image/jpg", "image/png"};
    for (String type : typeWhite) {
        if (!type.equals(contentType)) {
            return "fail";
        }
    }
    // ...其它业务逻辑
    // 最后一步 存储文件 需要对文件进行随机重命名,避免上传的文件被恶意利用
    
    // 代码省略
    return "success";
}

目录穿越漏洞目录穿越漏洞分析

目录穿越漏洞主要出现在当需要用户提供路径或文件名时,如文件下载。在访问者提供需要下载的文件后,Web应用程序没有去检验文件名中是否存在“../”等特殊字符,没有对访问的文件进行限制,导致目录穿越,读取到本不应读取到的内容。

目录穿越漏洞修复最佳实践

对于目录穿越漏洞的防御相对简单,一般有以下方法:

比如对文件名进行过滤,通过 if���.("..")!=-1||.(0)=='/')”判断是否存在“..”和“/”字符,如果存在就返回空,这样便能在很大程度上避免目录穿越攻击。

XSS漏洞XSS漏洞分析

跨站脚本(Cross-Site ,XSS):由于 WEB 应用程序对用户的输入过滤不足而产生的。攻击者利用网站漏洞把恶意的脚本代码注入到网页中,当其他用户浏览这些网页时,就会执行其中的恶意代码,对受害用户可能采取 资料窃取、会话劫持、钓鱼欺骗等各种攻击。

XSS漏洞修复最佳实践

主要是对可能存在XSS漏洞的地方,比如个人信息、发表文章、留言板等,对用户填写的信息进行html过滤。

1、使用ESAPI

ESAPI是owasp提供的一套API级别的web应用解决方案。简单的说,ESAPI就是为了编写出更加安全的代码而设计出来的一些API,方便使用者调用,从而方便的编写安全的代码

项目中maven引入


<dependency>
    <groupId>org.owasp.esapigroupId>
    <artifactId>esapiartifactId>
    <version>2.5.2.0version>
dependency>

api使用:

//对用户输入“input”进行HTML编码,防止XSS
String safe = input = ESAPI.encoder().encodeForHTML(input);
//根据自己不同的需要可以选用以下方法
//input = ESAPI.encoder().encodeForHTMLAttribute(input);
//input = ESAPI.encoder().encodeForJavaScript(input);
//input = ESAPI.encoder().encodeForCSS(input);
//input = ESAPI.encoder().encodeForURL(input);
//针对富文本进行html编码

2、使用框架自带的.编码输出到html实体:

String safe = HtmlUtils.htmlEscape(input)

3、使用-lang库的.编码输出到html实体

String safe = StringEscapeUtils.escapeHtml(input)

CSRF漏洞CSRF漏洞分析

CSRF 的全名是 Cross Site ,翻译成中文就是跨站点请求伪造

CSRF 利用的是网站对用户网页浏览器的信任,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站,用户无意去访问的时候攻���者再添加一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。 导致用户在不知情的情况下执行了某些操作。

由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了 web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的

案例:

假如一家银行用以运行转账操作的 URL 地址如下:

那么,一个恶意攻击者可以在另一个网站上放置如下代码:

如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 资金

CSRF漏洞修复最佳实践

1、CSRF为什么能够攻击成功?

其本质原因是重要操作的所有参数都是可以被攻击者猜测到的。攻击者只有预测出 URL 的所有参数与参数值,才能成功地构造一个伪造的请求;反之,攻击者将无法攻击成功。

2、解决方案

所以我们可以对一些重要的操作随机生成一个token,当用户请求执该操作时需要携带该token并以post方式提交。然后后端对token进行校验是否正确。

比如我们对于某些重要的操作随机生成一个token放到中,前端在请求接口时从中获取token值然后放请求体中以post方式提交。

前端代码示例:

function getCookie() {
    var value = "; " + document.cookie;
    var parts = value.split("; csrf_token=");
    if (parts.length == 2) 
        return parts.pop().split(";").shift();
}
$.ajax({
    type: "post",
    url: "/xxxx",
    data: {csrf_token:getCookie()},
    dataType: "json",
    success: function (data) {
        if (data.ec == 200) {
         //do something
        }
    }
});

后端应从POST请求体中提取参数值,进行校验。

中的CSRF

在框架中也提供了CSRF 保护,以防止 CSRF 攻击应用程序, 会针对 PATCH,POST,PUT 和 方法进行防护。

框架的CSRF实现原理和我们上一小节提到的基本一致,就是后端生成token,前端请求接口时携带token,后端再对token进行校验。

下面是框架中的源码实现。

@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		request.setAttribute(HttpServletResponse.class.getName(), response);
            // 从session中加载 Token (tokenRepository 定义了Token的生成,存储和获取的相关API)
		CsrfToken csrfToken = this.tokenRepository.loadToken(request);
		boolean missingToken = (csrfToken == null);
// 如果是第一次访问就生成Token信息
		if (missingToken) {
			csrfToken = this.tokenRepository.generateToken(request);
// 把生成的Token信息存储在Session中
			this.tokenRepository.saveToken(csrfToken, request, response);
		}
		request.setAttribute(CsrfToken.class.getName(), csrfToken);
		request.setAttribute(csrfToken.getParameterName(), csrfToken);
// 匹配是否是需要做CSRF防御的相关请求
		if (!this.requireCsrfProtectionMatcher.matches(request)) {
			if (this.logger.isTraceEnabled()) {
				this.logger.trace("Did not protect against CSRF since request did not match "
						+ this.requireCsrfProtectionMatcher);
			}
			filterChain.doFilter(request, response);
			return;
		}
// 获取请求携带在header中的Token信息
		String actualToken = request.getHeader(csrfToken.getHeaderName());
		if (actualToken == null) {
// 从请求参数中获取Token信息
			actualToken = request.getParameter(csrfToken.getParameterName());
		}
// 判断请求中的Token是否和Session中存储的Token相等
		if (!equalsConstantTime(csrfToken.getToken(), actualToken)) {
			this.logger.debug(
					LogMessage.of(() -> "Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request)));
// Token不相等,说明是CSRF攻击,抛出访问拒绝的异常
			AccessDeniedException exception = (!missingToken) ? new InvalidCsrfTokenException(csrfToken, actualToken)
					: new MissingCsrfTokenException(actualToken);
			this.accessDeniedHandler.handle(request, response, exception);
			return;
		}
// 说明是正常的访问,放过
		filterChain.doFilter(request, response);
	}

~

网络安全学习,我们一起交流

~

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值