「 典型安全漏洞系列 」02.SQL注入详解

发布时间:2024年01月11日

引言:SQL注入是一个老生常谈且又非常重要的漏洞,导致许多热点的数据泄露事件。尽管学习起来相对简单,但它可能用于某些高危漏洞的利用。这使得它成为初学者的兴趣点,甚至对于更有经验的用户来说,SQL注入也是基本知识。
在这里插入图片描述

1. 简介

SQL注入是一种利用应用程序对用户输入的处理不当,使恶意用户能够执行未经授权的SQL查询的行为。当应用程序从用户那里接收输入并将其直接用作SQL查询的一部分时,就可能发生SQL注入,使得攻击者能够干扰应用程序对数据库的正常查询,甚至可访问敏感数据,引起数据泄露。
在这里插入图片描述

SQL注入根源:

  • 执行外部参数拼接的SQL语句;
  • 执行外部传入的整条SQL语句;
  • 在配置文件中的SQL语句没有使用预编译方式占位符;
  • 校验函数有缺陷或占位符使用错误。

比如,通过输入"1 and version() > 0",若应用程序正常返回,说明version()被数据库识别并执行。因version()是Mysql特有的函数,所以可以推断出后台数据库为Mysql。

Note:不同数据库的注入点可以查阅https://portswigger.net/web-security/sql-injection/cheat-sheet

2. 注入步骤

2.1. 确定目标

首先,你需要确定你想要攻击的目标。它可能是一个公开的网站,或者是一个内部网络应用。简而言之,你需要先找到一个有用户输入并且包含数据库连接信息的应用。

2.2. 寻找注入点

你需要寻找应用程序中可能存在SQL注入的点。注入点通常在用户输入字段(如用户名、密码、搜索查询等)中。当应用程序将这些输入直接插入到SQL查询中时,就可能存在注入风险。

常见注入点分类:

    1. 按参数类型区分:
    • 数字型注入:比如and 1=1,
    • 字符型注入:比如and '1'='1
    1. 按请求方式区分:
    • GET注入
    • POST注入
    • HTTP注入
    1. 按执行效果区分:
    • 报错注入
    • UNION注入
    • 布尔盲注
    • 时间盲注
    • 二次注入

2.3. 构造恶意输入

基于你找到的注入点,你需要构造一个恶意输入。这个输入可能包含一些特殊字符或字符串,这些字符或字符串会被应用程序误解为SQL代码。例如,如果你正在寻找用户名和密码字段,一个可能的恶意输入可能是“admin; SELECT * FROM users”,这个输入会触发一个查询,返回所有用户的信息。

2.4. 执行攻击

当你的恶意输入被提交到应用程序时,如果应用程序没有正确地验证和清理用户输入,那么它就会执行你构造的SQL代码。这可能导致数据库被篡改,或者敏感信息被泄露。

Note:通过构造特殊的SQL语句可猜测类似表名、字段名、用户名及密码信息。

2.5. 分析结果

一旦攻击成功,你需要分析数据库中的数据,以确认你的攻击是否成功。你可能需要查看特定的表或列,或者查看数据库的整体结构。

3. 常见攻击

在不同的情况下会发生许多SQL注入漏洞、攻击和技术。一些常见的SQL注入示例包括:

  • 检索隐藏数据:可以在其中修改SQL查询以返回其他结果,比如文章开关介绍的探索数据库版本信息;
  • 颠覆应用程序逻辑:中可以更改查询以干扰应用程序的逻辑;
  • UNION攻击:可以从不同的数据库表中检索数据;
  • SQL盲注:攻击者可以操控查询语句的参数,且查询的结果不会在应用程序的响应中返回,比如注入xxx' and '1'='1并不影响程序正常响应,但这可以确认此处存在注入点。

4. 防御

主要有以下4种防御方法:参数化查询、存储过程、输入数据校验及最小化权限配置。

4.1. 参数化查询,使用预编译语句

比如如下代码,直接通过拼接SQL方式执行查询,存储注入问题

String query = "SELECT * FROM products WHERE category = '"+ input + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);

可修改为下面的预编译语句形式,利用预编译将SQL编译后放入缓存池,当服务器执行入参时并不会编译SQL,而是直接传参,所以可以避免SQL注入。

PreparedStatement statement = connection.prepareStatement("SELECT * FROM products WHERE category = ?");
statement.setString(1, input);
ResultSet resultSet = statement.executeQuery();

4.2. 使用存储过程

使用存储过程也在可以缓解SQL注入,效果与预编译语句类似。区别在于存储过程需要先将SQL语句定义在数据库中,而非缓存池。

当然,在使用存储过程中也可能存在注入问题,需要尽量避免在存储过程内使用动态的SQL语句。如实在无法避免,可严格过滤输入或对输入编码后再处理。

4.3. 输入数据校验

对传入的数据类型做校验,比如只能是时间类型、整型,或满足指定格式(IP、邮箱格式)

4.4. 最小化权限配置

避免对普通账号赋予过高的权限,数据库账户设计时需要遵循最小化权限原则。比如,只涉及查询类的应用,数据库账户只需要赋予只读权限即可,避免因注入问题导致下个数据库被删除;

5. 参考

[1] https://portswigger.net/web-security/sql-injection#what-is-sql-injection-sqli


前期回顾:
「 典型安全漏洞系列 」01.XSS攻击详解

文章来源:https://blog.csdn.net/u013129300/article/details/135539706
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。