SQL注入攻击和防御

声明:本文仅限于技术讨论与分享,严禁用于非法途径。若读者因此作出任何危害网络安全行为后果自负,与本号及原作者无关。

# 概述

SQL注入是一种网络安全攻击,它利用了Web应用程序对用户输入的验证不足,从而在后台数据库中执行恶意的SQL语句,获取或修改数据,甚至控制数据库服务器。

# SQL注入产生的原因

Web应用程序使用了动态SQL语句,即根据用户输入的内容拼接SQL语句,而没有对用户输入的内容进行合法性检查或过滤。

例如,以下是一个PHP代码片段,用于根据用户输入的用户名和密码进行登录验证:

$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($conn, $sql);

这段代码直接使用了用户输入的用户名和密码,拼接成了一个SQL查询语句,然后执行该语句。如果用户输入的用户名和密码都是合法的,例如username = 'admin'password = '123456',那么SQL语句就是:

SELECT * FROM users WHERE username = 'admin' AND password = '123456'

这个语句的作用是从users表中查询用户名和密码都匹配的记录,如果存在,就表示登录成功,否则,就表示登录失败。

但是,如果用户输入的用户名或密码包含了一些特殊的字符或命令,例如username = 'admin'password = '123456' OR 1 = 1,那么SQL语句就变成了:

SELECT * FROM users WHERE username = 'admin' AND password = '123456' OR 1 = 1

这个语句的作用是从users表中查询用户名和密码都匹配,或者1等于1的记录,由于1等于1永远为真,所以这个语句会返回users表中的所有记录,相当于绕过了密码验证,实现了SQL注入攻击。(万能密码就是这个原理)

例2,假设你想在某个网站上购买一本书,你需要输入你的姓名、地址、信用卡号等信息,然后点击提交按钮,完成订单。这个网站的后台程序可能会使用类似于以下的SQL语句,将你的信息插入到数据库中:

INSERT INTO orders (name, address, credit_card) VALUES ('$name', '$address', '$credit_card')

其中,$name$address$credit_card是你输入的内容,用单引号包围。如果你输入的内容都是合法的,例如name = '张三'address = '重庆市南岸区YJ'credit_card = '1234-5678-9012-3456',那么SQL语句就是:

INSERT INTO orders (name, address, credit_card) VALUES ('张三', '重庆市南岸区YJ', '1234-5678-9012-3456')

这个语句的作用是将你的信息插入到orders表中,没有任何问题。

但是,如果你输入的内容包含了一些特殊的字符或命令,例如name = '张三'address = '重庆市南岸区YJ'credit_card = '1234-5678-9012-3456'; DROP TABLE orders; --',那么SQL语句就变成了:

INSERT INTO orders (name, address, credit_card) VALUES ('张三', '重庆市南岸区YJ', '1234-5678-9012-3456'; DROP TABLE orders; --')

这个语句的作用是先将你的信息插入到orders表中,然后执行一个分号后面的命令,即删除orders表,最后执行一个双破折号后面的注释,即忽略后面的内容。这样,你就利用了SQL注入攻击,删除了网站的订单数据,造成了严重的损失。

# SQL注入防御
  • 使用参数化查询或预编译语句

    即将用户输入的内容作为参数传递给SQL语句,而不是直接拼接。参数化查询或预编译语句可以有效地防止SQL注入攻击,因为它们会将用户输入的内容作为字面值,而不是可执行的代码。

    例如,以下是一个使用参数化查询的PHP代码片段,用于根据用户输入的用户名和密码进行登录验证:

    $username = $_POST['username'];
    $password = $_POST['password'];
    $sql = "SELECT * FROM users WHERE username = ? AND password = ?";
    $stmt = mysqli_prepare($conn, $sql);
    mysqli_stmt_bind_param($stmt, "ss", $username, $password);
    mysqli_stmt_execute($stmt);
    $result = mysqli_stmt_get_result($stmt);
    

    这段代码使用了mysqli_prepare函数来预编译SQL语句,使用了?占位符来表示参数,使用了mysqli_stmt_bind_param函数来绑定参数的值和类型,使用了mysqli_stmt_execute函数来执行SQL语句。这样,即使用户输入的用户名或密码包含了特殊的字符或命令,也不会影响SQL语句的结构和语义,而只会作为普通的字符串进行比较,从而避免了SQL注入攻击。

    • 使用最小权限原则

      如果给数据库用户分配了过多的权限,导致攻击者可以利用SQL注入攻击执行一些危险的操作,例如删除表、修改数据、执行系统命令等。例如,如果Web应用程序使用的数据库用户拥有db_owner角色,那么攻击者就可以利用SQL注入攻击执行以下语句,删除users表:

      DROP TABLE users
      

      或者执行以下语句,调用xp_cmdshell扩展存储过程,执行系统命令,例如打开计算器:

      EXEC xp_cmdshell 'calc.exe'
      

      因此,为了防止SQL注入攻击,Web应用程序应该使用最小权限原则,即只给数据库用户分配必要的权限,例如只允许执行查询和更新操作,而不允许执行删除和执行操作。

      你可以使用MySQL的GRANT命令,指定用户的用户名、主机名、数据库名、表名和权限。例如,假设你的webapp用户的用户名是webapp,主机名是localhost,数据库名是mydb,表名是mytable,你可以使用以下命令:

      GRANT SELECT, UPDATE ON mydb.mytable TO 'webapp'@'localhost';
      

      这个命令的作用是给webapp用户授予在mydb数据库的mytable表上执行SELECT和UPDATE操作的权限。如果你想要授予webapp用户在mydb数据库的所有表上执行查询和更新操作的权限,你可以使用以下命令:

      GRANT SELECT, UPDATE ON mydb.* TO 'webapp'@'localhost';
      
相关推荐
月落星还在19 分钟前
Redis 的过期策略与键的过期时间设置
数据库·redis·bootstrap
酒酿祺子3 小时前
蓝队第三次
sql
cg50173 小时前
MySQL数据库复杂的增删改查操作
数据库·mysql
虾球xz4 小时前
游戏引擎学习第147天
数据库·学习·游戏引擎
向上的车轮5 小时前
什么是时序数据库?有哪些时序数据库?常见的运用场景有哪些?
数据库·时序数据库
岱宗夫up7 小时前
【Python】Django 中的算法应用与实现
数据库·python·opencv·django·sqlite
比花花解语7 小时前
使用数据库和缓存的时候,是如何解决数据不一致的问题的?
数据库·缓存·数据一致性
YGGP7 小时前
Redis篇:基础知识总结与基于长期主义的内容更新
数据库·redis·缓存
KINICH ahau7 小时前
数据库1-2章
数据库·oracle
我想吃烤肉肉8 小时前
leetcode-sql数据库面试题冲刺(高频SQL五十题)
数据库·sql·leetcode