API安全(二)-SQL注入与防范

时间:2022-03-29 01:25:45

1、什么是注入攻击

  使用了用户输入的但是我们没有校验过的数据,来拼装一个可以行的指令,交给系统去执行,结果导致执行了我们不希望发生的命令。注入攻击用很多种,最常见的是SQL注入。

2、SQL注入攻击

  Java程序员知道,使用Statement进行查询时会造成SQL注入攻击,从而使用PreparedStatement来进行SQL预编译,从而有效的防止SQL注入攻击。但是日常开发中,我们一般多使用框架来进行数据库操作,如JdbcTemplate、Spring-Data-Jpa、Mybatis等,但是使用起来,我们也要注意,避免写出可注入程序。

3、JdbcTemplate注入代码示例

  3.1、UserDO实体类与UserDTO

/**
 * @author caofanqi
 * @date 2020/1/20 13:08
 */
@Data
@Entity
@Table(name = "user")
public class UserDO {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;


    public UserDTO buildUserDTO(){
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(this,userDTO);
        return userDTO;
    }

}


/**
 * @author caofanqi
 * @date 2020/1/20 13:08
 */
@Data
public class UserDTO {


    private Long id;

    private String name;

}

  3.2、UserController,提供一个根据用户名称进行查询用户的API接口

/**
 * 用户控制层
 *
 * @author caofanqi
 * @date 2020/1/20 13:05
 */
@RestController
@RequestMapping("/users")
public class UserController {

    @Resource
    private UserService userService;


    @GetMapping
    public List<UserDTO> query(String name) {
        return userService.query(name);
    }

    
}

  3.3、UserService实现类,使用JdbcTemplate进行条件拼接查询,会产生SQL注入问题

/**
 * 用户业务层实现类
 *
 * @author caofanqi
 * @date 2020/1/20 13:52
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {


    @Resource
    private JdbcTemplate jdbcTemplate;


    @Override
    public List<UserDTO> query(String name) {

        String sql = "SELECT * FROM user WHERE name = ‘"   name   "‘";
        log.info("执行的SQL为:{}",sql);
        List<UserDO> queryResult = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(UserDO.class));

        List<UserDTO> result = queryResult.stream().map(UserDO::buildUserDTO).collect(Collectors.toList());

        return result;
    }

}

  3.4、启动项目,向数据库中插入3条数据,如下:

  API安全(二)-SQL注入与防范

  3.5、访问http://127.0.0.1:9090/users?name=zhangsan 可以正常查询到结果

  API安全(二)-SQL注入与防范

  3.6、但是执行http://127.0.0.1:9090/users?name=‘ or ‘1‘=‘1 时,就会把数据库中所有的用户都查询出来,这样就会导致信息泄漏。

  API安全(二)-SQL注入与防范

  控制台打印日志如下

2020-01-20 21:48:07.263  INFO 18540 --- [nio-9090-exec-3] c.c.s.service.impl.UserServiceImpl       : 执行的SQL为:SELECT * FROM user WHERE name = ‘‘ or ‘1‘=‘1‘

  往下追溯源码可以发现,该query方法,底层使用的是Statement

  API安全(二)-SQL注入与防范

  3.7、解决这个问题,我们修改query代码如下,使用了预编译SQL。

    @Override
    public List<UserDTO> query(String name) {

        String sql = "SELECT * FROM user WHERE name = ? ";
        List<UserDO> queryResult = jdbcTemplate.query(sql, new Object[]{name}, BeanPropertyRowMapper.newInstance(UserDO.class));
        List<UserDTO> result = queryResult.stream().map(UserDO::buildUserDTO).collect(Collectors.toList());

        return result;
    }

  3.8、http://127.0.0.1:9090/users?name=‘ or ‘1‘=‘1 时,查询不到结果,预防了SQL注入攻击

  API安全(二)-SQL注入与防范

   往下追溯源码可以发现,该query方法,底层使用的是PreparedStatement

  API安全(二)-SQL注入与防范

4、EntityManager注入代码示例

  在使用JPA的EntityManager执行拼接SQL时,将参数拼接到SQL中,一样会有SQL注入问题

@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<UserDTO> query(String name) {

        String sql = "SELECT * FROM user WHERE name = ‘"   name   "‘";

        Query nativeQuery = entityManager.createNativeQuery(sql, UserDO.class);
        List<UserDO> queryResult = nativeQuery.getResultList();

        List<UserDTO> result = queryResult.stream().map(UserDO::buildUserDTO).collect(Collectors.toList());

        return result;
    }

}

  需要修改成如下:

        String sql = "SELECT * FROM user WHERE name = ? ";
        Query nativeQuery = entityManager.createNativeQuery(sql, UserDO.class);
        nativeQuery.setParameter(1, name);
        List<UserDO> queryResult = nativeQuery.getResultList();

5、如何防止SQL注入

  上面的情况只是针对与查询,多查出来数据,更危险的攻击可能会修改或删除数据,甚至调用存储过程,删表等。我们如何防止SQL注入呢?

  5.1、控制数据库用户权限(对分配给应用程序的用户权限进行限定,一般只有对数据的CRUD权限)

  5.2、对传入的参数进行校验

  5.3、使用一些高级的框架进行数据库操作,对于一些复杂的情况,需要进行SQL拼接时,不要将参数拼接到SQL中,要使用预编译SQL。

 

项目源码:https://github.com/caofanqi/study-security/tree/dev-SQL-injection