Java 中的 SQL 注入以及如何轻松预防

2023-11-09

什么是 SQL 注入?

SQL注入是十大 Web 应用程序漏洞之一。简单来说,SQL注入是指通过用户输入的数据在查询中注入/插入SQL代码。它可以发生在任何使用关系数据库(如 Oracle、MySQL、PostgreSQL 和 SQL Server)的应用程序中。

为了执行 SQL 注入,恶意用户首先尝试在应用程序中找到可以嵌入 SQL 代码和数据的位置。它可以是登录页面任何网络应用程序或任何其他地方。因此,当应用程序收到嵌入 SQL 代码的数据时,SQL 代码将与应用程序查询一起执行。

SQL注入的影响

  • 恶意用户可以获得对您的应用程序的未经授权的访问并窃取数据。
  • 他们可以更改、删除数据库中的数据并关闭您的应用程序。
  • 黑客还可以通过执行数据库特定的系统命令来控制运行数据库服务器的系统。

SQL注入是如何工作的?

假设我们有一个名为tbluser它存储应用程序用户的数据。这userId是表的主列。我们的应用程序具有功能,可让您通过以下方式获取信息userId.userId 的值是从用户请求中接收的。

让我们看一下下面的示例代码。

String userId = {get data from end user}; 
String sqlQuery = "select * from tbluser where userId = " + userId;

1. 有效的用户输入

当使用有效数据(即 userId 值 132)执行上述查询时,它将如下所示。

输入数据:132

执行的查询:从 tbluser 中选择 *,其中 userId=132

结果:查询将返回 userId 132 的用户的数据。在这种情况下没有发生 SQL 注入。

2. 黑客用户输入

黑客可以使用 Postman、cURL 等工具更改用户请求,将 SQL 代码作为数据发送,从而绕过任何 UI 端验证。

输入数据:2 or 1=1

执行的查询:从 tbluser 中选择 *,其中 userId=2 或 1=1

结果:现在上面的查询有两个带有 SQL OR 表达式的条件。

  • userId=2:这部分将匹配 userId 值为“2”的表行。
  • 1=1:这部分将始终被评估为正确。所以查询将返回表的所有行。

SQL注入的类型

我们来看看 SQL 注入的四种类型。

1.基于布尔的SQL注入

上面的例子是一个基于布尔的SQL注入的例子。它使用计算结果为 true 或 false 的布尔表达式。它可用于从数据库获取附加信息。例如;

输入数据:2或1=1

SQL 查询:从 tbl_employee 中选择名字、姓氏,其中 empId=2 or 1=1

2. 基于联合的 SQL 注入

SQL 联合运算符将来自两个不同查询的数据与相同列数组合在一起。在本例中,联合运算符用于从其他表中获取数据。

输入数据:2 union从tbluser中选择用户名、密码

查询:从 tbl_employee 中选择名字、姓氏,其中 empId=2 union从tbluser中选择用户名、密码

通过使用基于联合的 SQL 注入,攻击者可以获得用户凭据。

3. 基于时间的 SQL 注入

In  基于时间的 SQL 注入,在查询中注入特殊函数,可以将执行暂停指定的时间。这种攻击会降低数据库服务器的速度。它可能会影响数据库服务器的性能,从而降低您的应用程序的性能。例如,在 MySQL 中:

输入数据:2 + 睡眠(5)

Query:  从 tbl_employee 中选择 emp_id、first_name、last_name,其中 empId=2 + SLEEP(5)

在上面的示例中,查询执行将暂停 5 秒。

4. 基于错误的 SQL 注入

在此变体中,攻击者尝试从数据库获取错误代码和消息等信息。攻击者注入语法不正确的SQL,因此数据库服务器将返回错误代码和消息,可用于获取数据库和系统信息。

Java SQL注入示例

我们将使用一个简单的Java网络应用程序演示 SQL 注入。我们有登录.html,这是一个基本的登录页面,它从用户那里获取用户名和密码并将其提交到登录Servlet.

LoginServlet 从请求中获取用户名和密码,并根据数据库值验证它们。如果身份验证成功,Servlet 会将用户重定向到主页,否则将返回错误。

登录.html Code:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Sql Injection Demo</title>
    </head>
    <body>
    <form name="frmLogin" method="POST" action="https://localhost:8080/Web1/LoginServlet">
        <table>
            <tr>
                <td>Username</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>Password</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td colspan="2"><button type="submit">Login</button></td>
            </tr>
        </table>
    </form>
    </body>
</html>

LoginServlet.java Code:

package com.journaldev.examples;
import java.io.IOException;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (Exception e) {}
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        boolean success = false;
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        // Unsafe query which uses string concatenation
        String query = "select * from tbluser where username='" + username + "' and password = '" + password + "'";
        Connection conn = null;
        Statement stmt = null;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/user", "root", "root");
            stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(query);
            if (rs.next()) {
                // Login Successful if match is found
                success = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                stmt.close();
                conn.close();
            } catch (Exception e) {}
        }
        if (success) {
            response.sendRedirect("home.html");
        } else {
            response.sendRedirect("login.html?error=1");
        }
    }
}

数据库查询[MySQL]:

create database user;

create table tbluser(username varchar(32) primary key, password varchar(32));

insert into tbluser (username,password) values ('john','secret');
insert into tbluser (username,password) values ('mike','pass10');

1. 当从登录页面输入有效的用户名和密码时

输入用户名: john

输入用户名: secret

Query:从 tbluser 中选择 *,其中用户名 =“john”,密码 =“secret”

Result:用户名和密码存在于数据库中,因此认证成功。用户将被重定向到主页。

2. 使用SQL注入对系统进行未经授权的访问

输入用户名: dummy

输入密码: ' 或 '1'='1

Query:从 tbluser 中选择 *,其中用户名=‘dummy’且密码 =‘’或‘1’=‘1’

Result:输入的用户名和密码在数据库中不存在,但验证成功。Why?

这是由于 SQL 注入,因为我们输入了 ' 或 '1'='1 作为密码。查询中有3个条件。

  1. 用户名=‘虚拟’:它将被评估为 false,因为表中没有用户名为 dummy 的用户。
  2. 密码=‘’:由于表中没有空密码,因此它将被评估为 false。
  3. ‘1’=‘1’:它将被评估为 true,因为这是静态字符串比较。

现在结合所有 3 个条件,即假与假或真=>最终结果将是true.

在上面的场景中,我们使用了布尔表达式来执行SQL注入。还有一些其他方法可以进行 SQL 注入。在下一节中,我们将了解在 Java 应用程序中防止 SQL 注入的方法。

防止 Java 代码中的 SQL 注入

最简单的解决方案是使用准备好的声明代替陈述执行查询。

我们不是将用户名和密码连接到查询中,而是通过PreparedStatement 的 setter 方法提供它们进行查询。

现在,从请求接收到的用户名和密码值仅被视为数据,因此不会发生 SQL 注入。

我们来看看修改后的servlet代码。

String query = "select * from tbluser where username=? and password = ?";
Connection conn = null;
PreparedStatement stmt = null;
try {
    conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/user", "root", "root");
    stmt = conn.prepareStatement(query);
    stmt.setString(1, username);
    stmt.setString(2, password);
    ResultSet rs = stmt.executeQuery();
    if (rs.next()) {
        // Login Successful if match is found
        success = true;
    }
    rs.close();
} catch (Exception e) {
    e.printStackTrace();
} finally {
    try {
        stmt.close();
        conn.close();
    } catch (Exception e) {
    }
}

让我们了解一下这个案例中发生了什么。

Query: 从 tbluser 中选择 *,其中用户名=?和密码=?

上述查询中的问号(?)称为位置参数。上面的查询中有 2 个位置参数。我们不会连接用户名和密码来查询。我们使用中可用的方法准备好的声明提供用户输入。

我们通过使用设置了第一个参数stmt.setString(1, username)和第二个参数使用stmt.setString(2, password)。底层 JDBC API 负责清理这些值以避免 SQL 注入。

避免 SQL 注入的最佳实践

  1. 在查询中使用数据之前先验证数据。
  2. 不要使用常用词作为表名或列名。例如,许多应用程序使用 tbluser 或 tblaccount 来存储用户数据。电子邮件、名字、姓氏是常见的列名称。
  3. 不要直接连接数据(作为用户输入接收)来创建 SQL 查询。
  4. 使用类似的框架休眠 and Spring数据JPA用于应用程序的数据层。
  5. 在查询中使用位置参数。如果您使用普通JDBC,然后使用PreparedStatement执行查询。
  6. 通过权限和授权限制应用程序对数据库的访问。
  7. 不要向最终用户返回敏感的错误代码和消息。
  8. 进行适当的代码审查,以免开发人员意外编写不安全的 SQL 代码。
  9. 使用类似的工具SQLMap查找并修复应用程序中的 SQL 注入漏洞。

这就是 Java SQL 注入的全部内容,我希望这里没有遗漏任何重要的内容。

您可以从下面的链接下载示例 Java Web 应用程序项目。

SQL注入Java项目

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 中的 SQL 注入以及如何轻松预防 的相关文章

  • 如何将本机库链接到 IntelliJ 中的 jar?

    我正在尝试在 IntelliJ 中设置 OpenCV 但是我一直在弄清楚如何告诉 IntelliJ 在哪里可以找到本机库位置 在 Eclipse 中 添加 jar 后 您可以在 Build Config 屏幕中设置 Native 库的位置
  • 如何让 BlazeDS 忽略属性?

    我有一个 java 类 它有一个带有 getter 和 setter 的字段 以及第二对 getter 和 setter 它们以另一种方式访问 该字段 public class NullAbleId private static final
  • 不同帐户上的 Spring Boot、JmsListener 和 SQS 队列

    我正在尝试开发一个 Spring Boot 1 5 应用程序 该应用程序需要侦听来自两个不同 AWS 帐户的 SQS 队列 是否可以使用 JmsListener 注解创建监听器 我已检查权限是否正确 我可以使用 getQueueUrl 获取
  • 序列的排列?

    我有具体数量的数字 现在我想以某种方式显示这个序列的所有可能的排列 例如 如果数字数量为3 我想显示 0 0 0 0 0 1 0 0 2 0 1 0 0 1 1 0 1 2 0 2 0 0 2 1 0 2 2 1 0 0 1 0 1 1 0
  • 在内存中使用 byte[] 创建 zip 文件。 Zip 文件总是损坏

    我创建的 zip 文件有问题 我正在使用 Java 7 我尝试从字节数组创建一个 zip 文件 其中包含两个或多个 Excel 文件 应用程序始终完成 没有任何异常 所以 我以为一切都好 当我尝试打开 zip 文件后 Windows 7 出
  • Java 枚举与创建位掩码和检查权限的混淆

    我想将此 c 权限模块移植到 java 但是当我无法将数值保存在数据库中然后将其转换为枚举表示形式时 我很困惑如何执行此操作 在 C 中 我创建一个如下所示的枚举 public enum ArticlePermission CanRead
  • 如何在java中将一个数组列表替换为另一个不同大小的数组列表

    我有两个大小不同的数组列表 如何从此替换 ArrayList
  • Pig Udf 显示结果

    我是 Pig 的新手 我用 Java 编写了一个 udf 并且包含了一个 System out println 其中的声明 我必须知道在 Pig 中运行时该语句在哪里打印 假设你的UDF 扩展了 EvalFunc 您可以使用从返回的 Log
  • 在 Jar 文件中运行 ANT build.xml 文件

    我需要使用存储在 jar 文件中的 build xml 文件运行 ANT 构建 该 jar 文件在类路径中可用 是否可以在不分解 jar 文件并将 build xml 保存到本地目录的情况下做到这一点 如果是的话我该怎么办呢 Update
  • 从最终实体获取根证书和中间证书

    作为密码学的菜鸟 我每天都会偶然发现一些简单的事情 今天只是那些日子之一 我想用 bouncy castle 库验证 java 中的 smime 消息 我想我几乎已经弄清楚了 但此时的问题是 PKIXparameters 对象的构建 假设我
  • Eclipse Maven Spring 项目 - 错误

    I need help with an error which make me crazy I started to study Java EE and I am going through tutorial on youtube Ever
  • 内部类的构造函数引用在运行时失败并出现VerifyError

    我正在使用 lambda 为内部类构造函数创建供应商ctx gt new SpectatorSwitcher ctx IntelliJ建议我将其更改为SpectatorSwitcher new反而 SpectatorSwitcher 是我正
  • Spring Boot Data JPA 从存储过程接收多个输出参数

    我尝试通过 Spring Boot Data JPA v2 2 6 调用具有多个输出参数的存储过程 但收到错误 DEBUG http nio 8080 exec 1 org hibernate engine jdbc spi SqlStat
  • 尝试将 Web 服务部署到 TomEE 时出现“找不到...的 appInfo”

    我有一个非常简单的项目 用于培训目的 它是一个 RESTful Web 服务 我使用 js css 和 html 创建了一个客户端 我正在尝试将该服务部署到 TomEE 这是我尝试部署时遇到的错误 我在这里做错了什么 刚刚遇到这个问题 我曾
  • Eclipse 选项卡宽度不变

    我浏览了一些与此相关的帖子 但它们似乎并不能帮助我解决我的问题 我有一个项目 其中 java 文件以 2 个空格的宽度缩进 我想将所有内容更改为 4 空格宽度 我尝试了 正确的缩进 选项 但当我将几行修改为 4 空格缩进时 它只是将所有内容
  • Flutter 中有预填充数据库使用的示例吗?

    Flutter 中有预填充数据库使用的示例吗 我不需要 CRUD 示例 此时我只需要从数据库读取数据即可 我是 Flutter 新手 所以一步一步的教程会很好 您可以将您的应用程序与预填充的 sqlite 数据库捆绑在一起assets文件夹
  • Android:无法使用 DbHelper 和 Contract 类将数据插入 SQLite

    public class Main2Activity extends AppCompatActivity private EditText editText1 editText2 editText3 editText4 private Bu
  • 找不到符号 NOTIFICATION_SERVICE?

    package com test app import android app Notification import android app NotificationManager import android app PendingIn
  • 使用反射覆盖最终静态字段是否有限制?

    在我的一些单元测试中 我在最终静态字段上的反射中遇到了奇怪的行为 下面是说明我的问题的示例 我有一个基本的 Singleton 类 其中包含一个 Integer public class BasicHolder private static
  • 如何将双精度/浮点四舍五入为二进制精度?

    我正在编写对浮点数执行计算的代码的测试 不出所料 结果很少是准确的 我想在计算结果和预期结果之间设置一个容差 我已经证实 在实践中 使用双精度 在对最后两位有效小数进行四舍五入后 结果始终是正确的 但是usually四舍五入最后一位小数后

随机推荐

  • 如何在 Linux 中查找/获取您的 IP 地址

    在排除网络问题 设置新连接或配置防火墙时 了解设备的 IP 地址非常重要 IP 地址可分为两类 公共地址和私有地址 公共IP是唯一的并且可以从互联网访问的IP地址 私有 IP 地址保留供私有网络内部使用 而不直接暴露于 Internet 此
  • Python if..else 语句

    决策是计算机编程最基本的概念之一 Python 支持其他语言中常见的流程控制语句 但进行了一些修改 这if控制语句是最基本和众所周知的语句之一 用于根据特定条件执行代码 在这篇文章中 我们将回顾一下基础知识ifPython 中的语句 Pyt
  • Linux 中的 Echo 命令及示例

    The echo命令是Linux中最基本 最常用的命令之一 参数传递给echo打印到标准输出 echo常用于 shell 脚本中显示消息或输出其他命令的结果 回显命令 echo是 Bash 和大多数其他流行 shell 如 Zsh 和 Ks
  • 如何检查你的 Ubuntu 版本

    当您第一次登录 Ubuntu 系统时 在进行任何工作之前 最好检查一下计算机上运行的 Ubuntu 版本 新的 Ubuntu 版本每六个月发布一次 而 LTS 长期支持 版本每两年发布一次 LTS 版本在桌面和服务器上均受支持五年 其他标准
  • 如何在 Debian 9 上安装和使用 Docker

    Docker 是一个容器化平台 允许您快速构建 测试和部署应用程序作为可移植 自给自足的容器 几乎可以在任何地方运行 Docker 是容器技术事实上的标准 它是 DevOps 工程师及其持续集成和交付管道的重要工具 在本教程中 我们将指导您
  • Linux 中的定位命令

    在 Linux 上工作时最常见的操作之一是搜索文件和目录 Linux 系统上有多个命令允许您搜索文件 其中 find 和locate 是最常用的命令 The locate命令是按名称搜索文件和目录的最快 最简单的方法 在这篇文章中 我们将解
  • Linux 中的 Du 命令

    The du命令是 磁盘使用情况 的缩写 报告给定文件或目录使用的估计磁盘空间量 它对于查找占用大量磁盘空间的文件和目录非常有用 如何使用du命令 的一般语法为du命令如下 du OPTIONS FILE 如果给定FILE是一个目录 du将
  • 如何在 CentOS 7 上安装和配置 VNC

    在本教程中 我们将引导您完成在 CentOS 7 系统上安装和配置 VNC 服务器的步骤 我们还将向您展示如何通过 SSH 隧道安全地连接到 VNC 服务器 虚拟网络计算 VNC 是一种图形桌面共享系统 允许您使用键盘和鼠标远程控制另一台计
  • 如何在 CentOS 7 上安装 Iptables

    从 CentOS 7 开始 防火墙D取代 iptables 成为默认的防火墙管理工具 FirewallD 是一个完整的防火墙解决方案 可以使用名为firewall cmd 的命令行实用程序进行控制 如果您更熟悉 Iptables 命令行语法
  • 如何用 Java 编写您的第一个程序

    作者选择了免费开源基金接受捐赠作为为捐款而写程序 介绍 Java是一种成熟且设计良好的编程语言 用途广泛 从课堂学习到运行任何规模的商业应用程序 它的独特优势之一是它是跨平台的 一旦创建了 Java 程序 就可以在许多操作系统上运行它 包括
  • 如何在 Ubuntu 12.10 上设置 ownCloud 5

    什么是Red Means 用户需要输入或自定义的行将位于red在本教程中 其余的大部分应该是可复制和粘贴的 关于ownCloud ownCloud 是一种开源数据存储解决方案 类似于 Dropbox 或 Google Drive 人们可以获
  • Android RecyclerView 示例 - 多种 ViewType

    到目前为止 我们已经在一个视图中显示了相同类型的视图回收视图 在本教程中 我们将在 RecyclerView 中实现异构布局 回收视图 具有异构布局的 RecyclerView 通常用于显示节标题和详细信息 两者都需要不同的布局 因此需要不
  • 如何在 Ubuntu 18.04 上安装 Node.js

    介绍 Node js是一个用于通用编程的JavaScript平台 允许用户快速构建网络应用程序 通过在前端和后端利用 JavaScript Node js 使开发更加一致和集成 在本指南中 您将了解在 Ubuntu 18 04 服务器上安装
  • 如何在 CentOS 7 上安装 WordPress

    介绍 WordPress 是一个使用 PHP 和 MySQL 的免费开源网站和博客工具 WordPress 是目前互联网上最流行的 CMS 内容管理系统 拥有超过 20 000 个插件来扩展其功能 这使得 WordPress 成为快速轻松地
  • Java二维码生成器-zxing示例

    今天我们将研究Java QR 码生成器程序 如果您精通技术和小工具 那么您一定知道二维码 如今 您随处都可以找到它 在博客 网站 甚至在一些公共场所 这在移动应用程序中非常流行 您可以使用 QR 码扫描仪应用程序扫描 QR 码 它会显示文本
  • Python 位运算符

    Python 按位运算符用于对整数执行按位计算 将整数转换为二进制格式 然后逐位执行运算 因此称为按位运算符 Python 按位运算符仅适用于整数 最终输出以十进制格式返回 Python 按位运算符也称为二元运算符 Python 位运算符
  • 如何在 Ubuntu 14.04 上使用 UFW 设置防火墙

    介绍 UFW 即简单防火墙 是一个接口iptables旨在简化配置防火墙的过程 尽管iptables虽然是一个可靠且灵活的工具 但对于初学者来说 学习如何使用它来正确配置防火墙可能会很困难 如果您希望开始保护您的网络 并且不确定使用哪个工具
  • 如何在 Ubuntu 14.04 上将 MySQL 或 MariaDB 与 Django 应用程序一起使用

    介绍 Django 是一个用于快速创建 Python 应用程序的灵活框架 默认情况下 Django 应用程序配置为将数据存储到轻量级 SQLite 数据库文件中 虽然这在某些负载下效果很好 但更传统的 DBMS 可以提高生产性能 在本指南中
  • Spring MVC @RequestMapping 注解示例,包含控制器、方法、标头、参数、@RequestParam、@PathVariable

    RequestMapping is one of the most widely used Spring MVC annotation org springframework web bind annotation RequestMappi
  • Java 中的 SQL 注入以及如何轻松预防

    什么是 SQL 注入 SQL注入是十大 Web 应用程序漏洞之一 简单来说 SQL注入是指通过用户输入的数据在查询中注入 插入SQL代码 它可以发生在任何使用关系数据库 如 Oracle MySQL PostgreSQL 和 SQL Ser