代码审查

代码审查

输入验证

路径遍历

​ 应用程序对用户可控制的输入未经合理校验,就传送给一个文件API。攻击者可能会使用一些特殊的字符(如../)摆脱受保护的限制,访问一些受保护的文件或目录。

缺陷代码:

@GET
@Path("/images/{image}")
@Produces("images/*")
public Response getImage(@javax.ws.rs.PathParam("image") String image, MultipartFile file) {
    File fileTest1 = new File("resources/images/", image);
    String fileName = file.getOriginalFilename();

    if (!fileTest1.exists()) {
        return Response.status(Status.NOT_FOUND).build();
    }

    return Response.ok().entity(new FileInputStream(fileTest1)).build();
}

缺陷原因: 第五行中文件的名字由用户输入,未经过合法性验证,用户可以通过输入特殊字符(…/file.text)获取系统资源,获取resources下的file.test文件


正确示范:

# 引入文件名过滤/处理方法
import org.apache.commons.io.FilenameUtils;

@GET
@Path("/images/{image}")
@Produces("images/*")
public Response getImage(@javax.ws.rs.PathParam("image") String image, MultipartFile file) {
	# 读取文件之前,对文件名进行过滤处理
    File fileTest = new File("resources/images/", FilenameUtils.getName(image)); 
    String fileName = FilenameUtils.getName(file.getOriginalFilename());

    if (!fileTest.exists()) {
        return Response.status(Status.NOT_FOUND).build();
    }

    return Response.ok().entity(new FileInputStream(fileTest)).build();
}

代码注入

SQL注入:Mybatis

​ SQL注入是一种数据库攻击手段。攻击者通过向应用程序提交恶意代码来改变原SQL语句的含义,进而执行任意SQL命令,达到入侵数据库乃至操作系统的目的。在Mybatis Mapper Xml中,#变量名称创建参数化查询SQL语句,不会导致SQL注入。而$变量名称直接使用SQL指令,会导致SQL注入攻击。

缺陷代码:

<!--select user information by name-->
<select id="queryByUserName" resultMap="userResultMap" parameterType="String">
select * from db_user where user_name=${username}
</select>

缺陷原因: 在第三行中使用了${},mybatis注入中¥{}是先取值,再编译,传入参数是什么直接填入{}内,不做任何改变,如果传入'username' or 1=1,则可以跳过验证,直接将数据库中信息返回。应替换为#{},mybatis中#{}会做预编译处理,用占位符?预留位置,传入参数会当作字符串进行处理如之前传入'username' or 1=1,会被转译为“'username' or 1=1”,判断是否有username=“'username' or 1=1”,更加安全。


正确示范:

<!--select user information by name-->
<select id="queryByUserName" resultMap="userResultMap" parameterType="String">
select * from db_user where user_name=#{username}
</select>

跨站脚本攻击

转存型XSS

​ 存储型XSS是指应用程序通过Web请求获取不可信赖的数据,并且在未检验数据是否存在XSS代码的情况下,将其存入数据库。当程序下一次从数据库中获取该数据时,致使页面再次执行XSS代码。存储型XSS可以持续攻击用户,在用户提交了包含XSS代码的数据存储到数据库后,每当用户在浏览网页查询对应数据库中的数据时,那些包含XSS代码的数据就会在服务器解析并加载,当浏览器读到XSS代码后,会当做正常的HTML和JS解析并执行,于是发生存储型XSS攻击。


转存型XSS攻击图示:

image-20220826105238438


反射型XSS

​ 反射型XSS是指应用程序通过Web请求获取不可信赖的数据,并在未检验数据是否存在恶意代码的情况下,将其发送给用户。反射型XSS一般可以由攻击者构造带有恶意代码参数的URL来实现,在构造的URL地址被打开后,其中包含的恶意代码参数被浏览器解析和执行。这种攻击的特点是非持久化,必须用户点击包含恶意代码参数的链接时才会触发。


反射型XSS攻击图示:

image-20220826110258035


解决方法

  1. 对用户的输入进行合理验证(如年龄只能是数字),对特殊字符(如<、>、'、"以及<script>、javascript等进行过滤。

  2. 根据数据将要置于HTML上下文中的不同位置(HTML标签、HTML属性、JavaScript脚本、CSS、URL),对所有不可信数据进行恰当的输出编码。

  3. 设置HttpOnly属性,避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击。在Java EE中,给Cookie添加HttpOnly的代码如下:

response.setHeader("Set-Cookie","cookiename=cookievalue; path=/; Domain=domainvaule; Max-age=seconds; HttpOnly");

继承HttpServletRequestWrapper方法,将请求中的参数替换掉

import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
/**
 * 请求参数替换,防止XSS攻击
 */
public class XSSHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] requestBody;//将流保存下来
    
    public XSSHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        
        //修改并存储流信息
        String str = scriptXSS(IOUtils.toString(request.getInputStream()));
        requestBody = str.getBytes();
        
        
    }

    @Override
    public String[] getParameterValues(String parameter) {
        String[] values = super.getParameterValues(parameter);
        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = scriptXSS(values[i]);
        }
        return encodedValues;
    }

    @Override
    public String getParameter(String parameter) {
        String value = super.getParameter(parameter);
        return scriptXSS(value);
    }

    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        return scriptXSS(value);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> map1 = super.getParameterMap();
        Map<String, String[]> escapeMap = new HashMap<>();
        Set<String> keys = map1.keySet();
        for (String key : keys) {
            String[] valArr = map1.get(key);
            if (valArr != null && valArr.length > 0) {
                String[] escapeValArr = new String[valArr.length];
                for (int i = 0; i < valArr.length; i++) {
                    String escapeVal = scriptXSS(valArr[i]);
                    escapeValArr[i] = escapeVal;
                }
                escapeMap.put(key, escapeValArr);
            }
        }
        return escapeMap;
    }
    
    @Override
    public ServletInputStream getInputStream(){
        // 返回处理之后的数据
        return new ServletInputStream() {
            final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
            @Override
            public int read(){
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }

    public static String scriptXSS(String value) {
        if (value != null) {
            value = value.replaceAll("", "");
            Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            value = scriptPattern.matcher(value).replaceAll("");
            scriptPattern = Pattern.compile(".*<.*", Pattern.CASE_INSENSITIVE);
            value = scriptPattern.matcher(value).replaceAll("");
        }
        return value;
    }

}

为了防止xss攻击过滤器

import com.abc.iotplatform.config.XSSHttpServletRequestWrapper;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 防止xss攻击过滤器
 */
public class XSSFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse, FilterChain filterChain)
            throws ServletException, IOException {
        filterChain.doFilter(new XSSHttpServletRequestWrapper(httpServletRequest), httpServletResponse);
    }
}

最后在文件配置中将过滤器加入就可以了

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import javax.servlet.DispatcherType;

@Configuration
public class FilterConfig {
    
    @Bean
    public FilterRegistrationBean<XSSFilter> XSSFilter() {
        FilterRegistrationBean<XSSFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new XSSFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(1); // 设置排序
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST);
        return registrationBean;
    }
}

注:目前项目代码审查就这么多问题,后续有待跟进!

posted @   芏筄  阅读(231)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示