首页 运维干货什么是SQL注入?SQL注入详解(非常详细)零基础入门到精通,收藏这一篇就够了

什么是SQL注入?SQL注入详解(非常详细)零基础入门到精通,收藏这一篇就够了

运维派隶属马哥教育旗下专业运维社区,是国内成立最早的IT运维技术社区,欢迎关注公众号:yunweipai
领取学习更多免费Linux云计算、Python、Docker、K8s教程关注公众号:马哥linux运维

一、SQL注入简介

SQL注入就是指Web应用程序对用户输入数据的合理性没有进行判断,前端传入后端的参数是攻击者可控制的,并且根据参数带入数据库查询,攻击者可以通过构造不同的SQL语句来对数据库进行任意查询。下面以PHP语句为例作为展示:

q u e r y = " S E L E C T ∗ F R O M u s e r s W H E R E i d = query=" SELECT*FROM users WHERE id=query="SELECT∗FROMusersWHEREid=_GET [‘id’] ";

由于这里的参数ID可控,且带入数据库查询,所以非法用户可以任意拼接SQL语句进行攻击。

当然,SQL注入主要原因是程序员在开发用户和数据库的系统时没有对用户输入的字符串进行过滤、转义、限制或处理不严谨,导致攻击者可以通过精心构造的字符串去非法获取到数据库中的数据。

什么是SQL注入?SQL注入详解(非常详细)零基础入门到精通,收藏这一篇就够了插图

1.1 SQL注入的原理

SQL注入漏洞的产生需要满足以下两个条件:

(1)参数用户可控:前端传给后端的参数内容是用户可以控制的。

(2)参数带入数据库查询:传入的参数拼接到SQL语句,且带入参数库查询。“

数字型注入

当输入的参数为整形时,如果存在注入漏洞,可以认为是数字型注入。

测试步骤:

(1) 加单引号,URL:xxx.xxx.xxx/xxx.php?id=3’;

对应的sql:select * from table where id=3’ 这时sql语句出错,程序无法正常从数据库中查询出数据,就会抛出异常;

(2) 加and 1=1 ,URL:xxx.xxx.xxx/xxx.php?id=3 and 1=1;

对应的sql:select * from table where id=3’ and 1=1 语句执行正常,与原始页面没有差异;

(3) 加and 1=2,URL:xxx.xxx.xxx/xxx.php?id=3 and 1=2;

对应的sql:select * from table where id=3 and 1=2 语句可以正常执行,但是无法查询出结果,所以返回数据与原始网页存在差异;

如果满足以上三点,则可以判断该URL存在数字型注入。

字符型注入

当输入的参数为字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号来闭合,而字符串一般需要通过单引号来闭合的。

例如数字型语句:select * from table where id =3;

则字符型如下:select * from table where name=’admin’;

因此,在构造payload时通过闭合单引号可以成功执行语句。

测试步骤:

(1) 加单引号:select * from table where name=’admin’’;

由于加单引号后变成三个单引号,则无法执行,程序会报错;

(2) 加 ’and 1=1 此时sql 语句为:select * from table where name=’admin’ and 1=1’ ,也无法进行注入,还需要通过注释符号将其绕过;

因此,构造语句为:select * from table where name =’admin’ and 1=–’ 可成功执行返回结果正确;

(3) 加and 1=2— 此时sql语句为:select * from table where name=’admin’ and 1=2–’则会报错;

如果满足以上三点,可以判断该url为字符型注入。二、实例演示:

首先创建一个测试表,并插入简单的数据(MySQL数据库):

CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) DEFAULT NULL,
  `password` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `t_user` VALUES ('1', 'John', '111111');
INSERT INTO `t_user` VALUES ('2', 'Tom', '222222');

接下来是Java程序部分,采用Spring Boot加上MyBatis的方式

在pom.xml文件中添加依赖:

<!-- 连接Spring Boot和MyBatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

<!-- mysql 驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

然后是Java各层的代码,从controller一直到dao,为了完整起见,我把代码都贴出来。controller层有一个方法,参数为用户名,返回该用户的详细信息,为了演示方便,此处返回一个列表:

package com.fullstack.commerce.user.controller;

import com.fullstack.commerce.user.entity.User;
import com.fullstack.commerce.user.service.UserService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("user")
public class UserController {
    @Resource
    private UserService userService;

    @RequestMapping("getUserInfo")
    @ResponseBody
    // 根据用户姓名查询出用户列表信息
    public List<User> getUserInfo(@RequestParam("username")String username){
        List<User> result =  userService.getUserInfo(username);
        return result;
    }
}

service层比较简单,有一个接口和对应的实现类,而实现类就是调用dao的方法,把用户列表查询出来:

package com.fullstack.commerce.user.service;

import com.fullstack.commerce.user.entity.User;
import java.util.List;

public interface UserService {
    List<User> getUserInfo(String username);
}
package com.fullstack.commerce.user.service.impl;

import com.fullstack.commerce.user.dao.UserDao;
import com.fullstack.commerce.user.entity.User;
import com.fullstack.commerce.user.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {
    @Resource
    private UserDao userDao;
    @Override
    public List<User> getUserInfo(String username) {
        return userDao.getUserInfo(username);
    }
}

dao层就是一个接口,里面只有一个方法:

package com.fullstack.commerce.user.dao;

import com.fullstack.commerce.user.entity.User;
import java.util.List;

public interface UserDao {
    List<User> getUserInfo(String username);
}

除了上面的业务代码,还需要有一个启动类:

package com.fullstack.commerce.user;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = "com.fullstack.commerce.user.dao")
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

以下是对应的mapper文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fullstack.commerce.user.dao.UserDao">
    <resultMap id="UserMap" type="com.fullstack.commerce.user.entity.User">
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="username" column="username" jdbcType="VARCHAR"/>
        <result property="password" column="password" jdbcType="VARCHAR"/>
    </resultMap>

    <select id="getUserInfo" resultMap="UserMap">
        SELECT * FROM t_user WHERE username = ${username}
    </select>
</mapper>

注意,在上面这个mapper文件中,只有一个select语句,而这个语句传参使用了符号$,它会把参数值直接进行替换,而不会进行预编译(如果使用占位符#,就会进行预编译,从而可以防止SQL注入)。

当然,application.yml文件也需要配置一下,数据库用户名和密码替换成实际的:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: myuser
    password: myuser
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  mapper-locations: mapper/*.xml

代码就算写完了,接下来我们测试一下看看。三、测试验证:

启动程序,我们来进行测试,在浏览器里面输入以下地址,然后回车:

http://localhost:8080/user/getUserInfo?username='John'

没问题,我们是要查找用户名为John的数据,它返回了如下的结果,正是我们所期望的:

什么是SQL注入?SQL注入详解(非常详细)零基础入门到精通,收藏这一篇就够了插图1

但如果我们把参数重新设置一下,变成如下这样:

http://localhost:8080/user/getUserInfo?username='John' OR 1=1

那就相当于在数据库中执行下面的SQL了,也就是返回所有的用户数据:

SELECT * FROM t_user WHERE username = 'John' OR 1=1

看结果,确实是这样,把表中的两条记录都返回到了客户端(测试数据只插了两条记录,如果有多条,同样会把所有的数据都返回过来):

什么是SQL注入?SQL注入详解(非常详细)零基础入门到精通,收藏这一篇就够了插图2

这显然是不对的,接口把查询范围之外的数据都搜索出来了,如果还有一些修改数据或者操纵数据库的一些命令,那就更危险了。

当然,我们把mapper文件里面的参数部分改成占位符#,这样就会先进行预编译,就不会发生刚才的情况了。修改成#以后运行程序,再执行上面的url,就会返回空了,因为这个时候是去数据库中查询用户名为【’John’ OR 1=1】的记录,显然是不存在的。

什么是SQL注入?SQL注入详解(非常详细)零基础入门到精通,收藏这一篇就够了插图3

5 万字 160道MySQL经典面试题总结(2024修订版)

需要完整资料的看这里

扫码回复666 根据提示 添加小助理领取:160道MySQL面试题

5 万字 160道MySQL经典面试题总结(2024修订版)插图3

什么是MySQL?

MySQL是⼀个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流⾏的关系型数据库管理系统之⼀,在 WEB 应⽤⽅⾯,MySQL是最好的RDBMS (Relational Database Management System,关系数据库管理系统) 应⽤软件之⼀。在Java企业级开发中⾮常常⽤,因为 MySQL 是开源免费的,并且⽅便扩展。

MySql, Oracle,Sql Service的区别?

1. Sql Service只能在Windows上使⽤,⽽MySql和Oracle可以在其他系统上使⽤, ⽽且可以⽀持数据库不同系统之间的移植

2. MySql开源免费的,Sql Service和Oracle要钱。

3. 我从⼩到⼤排序哈,MySql很⼩,Sql Service居中,Oracle最⼤

4. Oracle⽀持⼤并发量,⼤访问量,Sql Service还⾏,⽽MySql的话压⼒没这么⼤,因此现在的MySql的话最好是要使⽤集群或者缓存来搭配使⽤

5. Oracle⽀持多⽤户不同权限来进⾏操作,⽽MySql只要有登录权限就可操作全部数据库

6. 安装所⽤的空间差别也是很⼤的,Mysql安装完后才⼏百M⽽Oracle有⼏G左右,且使⽤的时候Oracle占⽤特别⼤的内存空间和其他机器性能。

7. 做分⻚的话,MySql使⽤Limit,Sql Service使⽤top,Oracle使⽤row

8. Oracle没有⾃动增⻓类型,Mysql和Sql Service⼀般使⽤⾃动增⻓类型

什么是索引?

索引是一种特殊的文件(innoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针

索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。

更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录索引是一个文件,它是要占据物理空间的。

索引的使用场景?

1.当数据多且字段值有相同的值得时候用普通索引。

2.当字段多且字段值没有重复的时候用唯一索引。

3.当有多个字段名都经常被查询的话用复合索引。

4.普通索引不支持空值,唯一索引支持空值。

5.但是,若是这张表增删改多而查询较少的话,就不要创建索引了,因为如果你给一列创建了索引,那么对该列进行增删改的时候,都会先访问这一列的索引,

6.若是增,则在这一列的索引内以新填入的这个字段名的值为名创建索引的子集

7.若是改,则会把原来的删掉,再添入一个以这个字段名的新值为名创建索引的子集,

8.若是删,则会把索引中以这个字段为名的索引的子集删掉。

9.所以,会对增删改的执行减缓速度,

10.所以,若是这张表增删改多而查询较少的话,就不要创建索引了,

11.更新太频繁地字段不适合创建索引。

12.不会出现在where条件中的字段不该建立索引。

B树和B+树的区别?

在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。

B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。

5 万字 160道MySQL经典面试题总结(2024修订版)插图4

非聚簇索引一定会回表查询吗?

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。就像酒店的房间,如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他使用完毕才可以再次使用。

什么是死锁?怎么解决?

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。

常见的解决死锁的方法:

  1. 如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
  2. 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率
  3. 对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率

为什么要使用视图?什么是视图?

为了提高复杂SQL语句的复用性和表操作的安全性,MySQL数据库管理系统提供了视图特性。所谓视图,本质上是一种虚拟表,在物理上是不存在的,其内容与真实的表相似,包含一系列带有名称的列和行数据。但是,视图并不在数据库中以储存的数据值形式存在。行和列数据来自定义视图的查询所引用基本表,并且在具体引用视图时动态生成。

视图使开发者只关心感兴趣的某些特定数据和所负责的特定任务,只能看到视图中所定义的数据,而不是视图所引用表中的数据,从而提高了数据库中数据的安全性。

SQL语句主要分为哪几类?

  • 数据定义语言DDL(Data Ddefinition Language)CREATE,DROP,ALTER主要为以上操作 即对逻辑结构等有操作的,其中包括表结构,视图和索引。
  • 数据查询语言DQL(Data Query Language)SELECT这个较为好理解 即查询操作,以select关键字。各种简单查询,连接查询等 都属于DQL。
  • 数据操纵语言DML(Data Manipulation Language)INSERT,UPDATE,DELETE主要为以上操作 即对数据进行操作的,对应上面所说的查询操作 DQL与DML共同构建了多数初级程序员常用的增删改查操作。而查询是较为特殊的一种 被划分到DQL中。
  • 数据控制功能DCL(DataControlLanguage)GRANT,REVOKE,COMMIT,ROLLBACK主要为以上操作 即对数据库安全性完整性等有操作的,可以简单的理解为权限控制等。

资料整理自网络,仅作免费交流分享,侵权删!

需要完整资料的看这里

扫码回复666 根据提示 添加小助理领取:160道MySQL面试题

5 万字 160道MySQL经典面试题总结(2024修订版)插图3

本文链接:https://www.yunweipai.com/45863.html

网友评论comments

发表回复

您的电子邮箱地址不会被公开。

暂无评论

Copyright © 2012-2022 YUNWEIPAI.COM - 运维派 京ICP备16064699号-6
扫二维码
扫二维码
返回顶部