前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >sql注入

sql注入

作者头像
用户5166330
发布2019-04-16 15:06:12
1.3K0
发布2019-04-16 15:06:12
举报
文章被收录于专栏:帅哥哥写代码
先看一段日常代码
代码语言:javascript
复制
@Autowired
private JdbcTemplate template;

private void buildYear(SearchDto s, StringBuilder sql) {
    sql.append(" SELECT ROUND(SUM(CASE (");
    sql.append(s.getYear());
    sql.append(" - substr(li.fztime, 1, 4))");
    sql.append(" WHEN 1 THEN (CASE diwact.sqoneyear WHEN '/' THEN 0 WHEN '' THEN 0 ELSE diwact.sqoneyear END)");
    sql.append(" WHEN 2 THEN (CASE diwact.sqtwoyear WHEN '/' THEN 0 WHEN '' THEN 0 ELSE diwact.sqtwoyear END)");
    sql.append(" WHEN 3 THEN (CASE diwact.sqthreeyear WHEN '/' THEN 0 WHEN '' THEN 0 ELSE diwact.sqthreeyear END)");
    sql.append(" WHEN 4 THEN (CASE diwact.sqfouryear WHEN '/' THEN 0 WHEN '' THEN 0 ELSE diwact.sqfouryear END)");
    sql.append(" WHEN 5 THEN (CASE diwact.sqfiveyear WHEN '/' THEN 0 WHEN '' THEN 0 ELSE diwact.sqfiveyear END)");
    sql.append(" ELSE 0 END),6) as num,");
}

private void getAreaStatistical(SearchDto s, StringBuilder sql) {
    if (StringUtils.isNotBlank(s.getCodeRegion())) {
        sql.append(" diwact.waterwrwname as one,bi.name_regiondetail as two");
        sql.append(" from license_info li");
        sql.append(" left join drain_info_water_apply_count_two diwact on li.dataid = diwact.dataid ");
        sql.append(" left join pw_base_info pbi on li.dataid = pbi.dataid ");
        sql.append(" left join base_info bi on pbi.enterid = bi.enterid");
        sql.append(" where bi.code_region ='");
        sql.append(s.getCodeRegion());
        sql.append("'");
        sql.append(" and diwact.waterwrwcode in ('");
        sql.append(PollutantEnum.CODCR.getCode());
        sql.append("','");
        sql.append(PollutantEnum.AD.getCode());
        sql.append("' )");
        sql.append(" group by diwact.waterwrwcode,diwact.waterwrwname ,bi.code_regiondetail,name_regiondetail");
        sql.append(" order by bi.code_regiondetail asc ,diwact.waterwrwcode asc ");
    } else {
        sql.append(" diwact.waterwrwname as one,bi.name_region as two");
        sql.append(" from license_info li");
        sql.append(" left join drain_info_water_apply_count_two diwact on li.dataid = diwact.dataid ");
        sql.append(" left join pw_base_info pbi on li.dataid = pbi.dataid");
        sql.append(" left join base_info bi on pbi.enterid = bi.enterid");
        sql.append(" where diwact.waterwrwcode in ('");
        sql.append(PollutantEnum.CODCR.getCode());
        sql.append("','");
        sql.append(PollutantEnum.AD.getCode());
        sql.append("' )");
        sql.append(" group by diwact.waterwrwcode,diwact.waterwrwname,bi.code_region,bi.name_region");
        sql.append(" order by bi.code_region asc ,diwact.waterwrwcode asc ");
    }
}
private ApesDto getResult(String sql) {
    log.info("----sql" + sql);
    List<Map<String, Object>> results = template.queryForList(sql);
    log.info("results" + results);
    // 名称集合
    Set<String> nameSet = new LinkedHashSet<String>();
    // 数据 集合
    List<ApeDto> datas = new ArrayList<ApeDto>();
    if (!results.isEmpty()) {
        results.forEach(t -> {
            nameSet.add(t.get("two").toString());
        });
        nameSet.forEach(t -> {
            ApeDto apeDto = new ApeDto();
            Map<String, Object> val = new HashMap<String, Object>();
            results.forEach(t2 -> {
                if (t.equals(t2.get("two"))) {
                    apeDto.setName(t);
                    val.put(t2.get("one").toString(), t2.get("num").toString());
                }
            });
            apeDto.setValues(val);
            datas.add(apeDto);
        });
    }
    ApesDto apesDto = new ApesDto();
    apesDto.setVal(datas);
    return apesDto;
}

这段代码功能很简单,就是根据前端传递的参数,构建sql查询语句。 代码经过测试满足了功能需求。

问题

项目经过第三方测评发现有sql注入漏洞。

正文

什么叫sql注入 所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。 [1] 比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击

原理 SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。 根据相关技术原理,SQL注入可以分为平台层注入和代码层注入。前者由不安全的数据库配置或数据库平台的漏洞所致;后者主要是由于程序员对输入未进行细致地过滤,从而执行了非法的数据查询。基于此,SQL注入的产生原因通常表现在以下几方面:①不当的类型处理;②不安全的数据库配置;③不合理的查询集处理;④不当的错误处理;⑤转义字符处理不合适;⑥多个提交处理不当。

攻击 当应用程序使用输入内容来构造动态sql语句以访问数据库时,会发生sql注入攻击。如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的字符串来传递,也会发生sql注入。sql注入可能导致攻击者使用应用程序登陆在数据库中执行命令。相关的SQL注入可以通过测试工具pangolin进行。如果应用程序使用特权过高的帐户连接到数据库,这种问题会变得很严重。在某些表单中,用户输入的内容直接用来构造动态sql命令,或者作为存储过程的输入参数,这些表单特别容易受到sql注入的攻击。而许多网站程序在编写时,没有对用户输入的合法性进行判断或者程序中本身的变量处理不当,使应用程序存在安全隐患。这样,用户就可以提交一段数据库查询的代码,根据程序返回的结果,获得一些敏感的信息或者控制整个服务器,于是sql注入就发生了。

防止 防止sql注入最简单的方法就是采用参数化的形式,不要直接拼接sql语句。

改版代码
代码语言:javascript
复制
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;

private void buildYear(SearchDto s, StringBuilder sql) {
    sql.append(" SELECT ROUND(SUM(CASE (:year");
    sql.append(" - substr(li.fztime, 1, 4))");
    sql.append(" WHEN 1 THEN (CASE diwact.sqoneyear WHEN '/' THEN 0 WHEN '' THEN 0 ELSE diwact.sqoneyear END)");
    sql.append(" WHEN 2 THEN (CASE diwact.sqtwoyear WHEN '/' THEN 0 WHEN '' THEN 0 ELSE diwact.sqtwoyear END)");
    sql.append(" WHEN 3 THEN (CASE diwact.sqthreeyear WHEN '/' THEN 0 WHEN '' THEN 0 ELSE diwact.sqthreeyear END)");
    sql.append(" WHEN 4 THEN (CASE diwact.sqfouryear WHEN '/' THEN 0 WHEN '' THEN 0 ELSE diwact.sqfouryear END)");
    sql.append(" WHEN 5 THEN (CASE diwact.sqfiveyear WHEN '/' THEN 0 WHEN '' THEN 0 ELSE diwact.sqfiveyear END)");
    sql.append(" ELSE 0 END),6) as num,");
}

private void getAreaStatistical(SearchDto s, StringBuilder sql) {
    if (StringUtils.isNotBlank(s.getCodeRegion())) {
        sql.append(" diwact.waterwrwname as one,bi.name_regiondetail as two");
        sql.append(" from license_info li");
        sql.append(" left join drain_info_water_apply_count_two diwact on li.dataid = diwact.dataid ");
        sql.append(" left join pw_base_info pbi on li.dataid = pbi.dataid ");
        sql.append(" left join base_info bi on pbi.enterid = bi.enterid");
        sql.append(" where bi.code_region = :code");
        sql.append(" and diwact.waterwrwcode in ('");
        sql.append(PollutantEnum.CODCR.getCode());
        sql.append("','");
        sql.append(PollutantEnum.AD.getCode());
        sql.append("' )");
        sql.append(" group by diwact.waterwrwcode,diwact.waterwrwname ,bi.code_regiondetail,name_regiondetail");
        sql.append(" order by bi.code_regiondetail asc ,diwact.waterwrwcode asc ");
    } else {
        sql.append(" diwact.waterwrwname as one,bi.name_region as two");
        sql.append(" from license_info li");
        sql.append(" left join drain_info_water_apply_count_two diwact on li.dataid = diwact.dataid ");
        sql.append(" left join pw_base_info pbi on li.dataid = pbi.dataid");
        sql.append(" left join base_info bi on pbi.enterid = bi.enterid");
        sql.append(" where diwact.waterwrwcode in ('");
        sql.append(PollutantEnum.CODCR.getCode());
        sql.append("','");
        sql.append(PollutantEnum.AD.getCode());
        sql.append("' )");
        sql.append(" group by diwact.waterwrwcode,diwact.waterwrwname,bi.code_region,bi.name_region");
        sql.append(" order by bi.code_region asc ,diwact.waterwrwcode asc ");
    }
}
private ApesDto getResult(String sql, SearchDto s) {
    log.info("----sql" + sql);
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("year", Integer.parseInt(s.getYear()));
    if (StringUtils.isNotBlank(s.getCodeRegion())) {
        params.put("code", s.getCodeRegion());
    }
    List<Map<String, Object>> results = jdbcTemplate.queryForList(sql, params);
    // 名称集合
    Set<String> nameSet = new LinkedHashSet<String>();
    // 数据 集合
    List<ApeDto> datas = new ArrayList<ApeDto>();
    if (!results.isEmpty()) {
        results.forEach(t -> {
            nameSet.add(t.get("two").toString());
        });
        for (String t : nameSet) {
            ApeDto apeDto = new ApeDto();
            Map<String, Object> val = new HashMap<String, Object>();
            results.forEach(t2 -> {
                if (t.equals(t2.get("two"))) {
                    apeDto.setName(t);
                    val.put(t2.get("one").toString(), t2.get("num").toString());
                }
            });
            apeDto.setValues(val);
            datas.add(apeDto);
        }
    }
    ApesDto apesDto = new ApesDto();
    apesDto.setVal(datas);
    return apesDto;
}

通过这种方式就可以防止sql注入漏洞了。

why

sql 执行过程简化版:收到指令 -> 编译SQL生成执行计划 ->选择执行计划 ->执行执行计划。 举例: 以往在Web应用程序访问数据库时一般是采取拼接字符串的形式,比如登录的时候就是根据用户名和密码去查询: string sql = "SELECT TOP 1 * FROM [User] WHERE UserName = '" + userName + "' AND Password = '" + password + "'"; 其中userName和password两个变量的值是由用户输入的。在userName和password都合法的情况下,这自然没有问题,但是用户输入是不可信的,一些恶意用户只要用一些技巧,就可以绕过用户名、密码登录。 假设password的值是"1' or '1' = '1",userName的值随便取,比如是"abc",那变量sql的值就是: "SELECT TOP 1 * FROM [User] WHERE UserName = 'abc' AND Password = '1' or '1' = '1'" 由于'1' = '1'恒为真,因此只要User表中有数据,不管UserName、Password的值是否匹配,这条SQL命令准能查出记录来。就这样,登录系统就被破解了。 防御方式 字符串检测:限定内容只能由英文、数字等常规字符,如果检查到用户输入有特殊字符,直接拒绝。但缺点是,系统中不可避免地会有些内容包含特殊字符,这时候总不能拒绝入库。 字符串替换:把危险字符替换成其他字符,缺点是危险字符可能有很多,一一枚举替换相当麻烦,也可能有漏网之鱼。 存储过程:把参数传到存储过程进行处理,但并不是所有数据库都支持存储过程。如果存储过程中执行的命令也是通过拼接字符串出来的,还是会有漏洞。 参数化查询   参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值。 在使用参数化查询的情况下,数据库服务器不会将参数的内容视为SQL指令的一部份来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有指令,也不会被数据库运行。

结语

大神之路需要一滴一滴的积累,尤其是基础。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.07.03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 先看一段日常代码
  • 问题
  • 正文
  • 改版代码
  • why
  • 结语
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档