Web 安全之 SQL 注入攻击

简介: Web 安全之 SQL 注入攻击

SQL 注入攻击是一种非常普遍的攻击方式,也是最危险的 Web 程序安全风险之一。所以学习并防范 SQL 注入攻击是非常必要的。

攻击原理

在编写 Web 程序时,如果后端代码不对前端用户输入数据做安全性检查,直接将数据当作参数拼接到 SQL 语句中,然后执行 SQL 语句,恶意用户就可以通过传入包含 SQL 关键字的参数来做一些相当危险的操作,如获取敏感数据、删除数据等,以此来达到攻击目的。

攻击示例

这里我用一个验证用户登录的例子来作为 SQL 注入攻击的示例。

首先执行如下 Python 代码来生成演示数据。为简单起见,程序中使用 SQLite 数据库来进行演示,以下代码创建了一张 user 表,包含三个字段分别为 idusernamepassword,并插入了一条数据,用户名密码都为 test

# demo_db.py
import sqlite3
conn = sqlite3.connect('test.db')
cursor = conn.cursor()
# 创建 user 表并插入数据
cursor.execute('create table user (id int(11) primary key, username varchar(20), password varchar(20))')
cursor.execute('insert into user(id, username, password) values (1, "test", "test")')
cursor.close()
conn.commit()
conn.close()

接下来使用 Flask 编写一个简单的 Web 应用。执行以下代码之前你需要使用 pip install flask 的方式安装 Flask

# demo_sql_inject.py
import sqlite3
from flask import Flask, request
app = Flask(__name__)
@app.route('/login', methods=['GET', 'POST'])
def login():
    # 如果为 POST 请求,则处理登录逻辑
    if request.method == 'POST':
        # 解析前端页面通过 form 表单传递过来的用户名、密码
        form = request.form
        username = form.get('username')
        password = form.get('password')
        print(f'username: {username}, password: {password}')
        # 连接数据库
        conn = sqlite3.connect('test.db')
        cursor = conn.cursor()
        # 根据用户名、密码拼接 SQL 语句
        sql = f"select * from user where username='{username}' and password='{password}'"
        print(f'sql: {sql}')
        # 执行 SQL 语句
        cursor.execute(sql)
        # 如果根据用户输入的用户名、密码能够查询出数据,则登录成功,否则登录失败
        result = cursor.fetchall()
        print(f'result: {result}')
        if result:
            return '登录成功'
        else:
            return '登录失败'
    # GET 请求,则返回登录表单
    else:
        return """
        <form method="post">
            <input name="username" placeholder="username">
            <input name="password" placeholder="password">
            <button>登录</button>
        </form>
        """
if __name__ == '__main__':
    app.run()

这个 Web 应用中只包含一个 login 视图函数,它分别接收两个请求方法 GETPOST。如果为 GET 请求,返回登录页面,如果为 POST 请求,解析前端页面通过 form 表单传递过来的用户名、密码,然后根据用户名、密码拼接 SQL 语句,接下来执行这条 SQL 语句来查询 user 表中的数据,能够查询出数据,则代表用户输入的用户名和密码正确,登录成功,否则登录失败。

启动 Flask 应用,浏览器访问登录页面 http://127.0.0.1:5000/login

首先输入正确的用户名 test 和密码 test,点击登录按钮,将得到 登录成功 响应。

 

# 控制台打印信息
username: test, password: test
sql: select * from user where username='test' and password='test'
result: [(1, 'test', 'test')]

然后测试输入错误的用户名或密码,如输入 123 作为密码,点击登录按钮,将得到 登录失败 响应。

 

# 控制台打印信息
username: test, password: 123
sql: select * from user where username='test' and password='123'
result: []

目前来看,我们的 Web 程序似乎能够正常工作,接下来我将演示如何通过传入 SQL 关键字来实现注入攻击。

这次在用户名的输入框输入 demo' or 1=1; --,密码输入框输入 123,点击登录按钮,你会惊奇的发现我们使用错误的用户名和密码竟然得到了 登录成功 响应。

 

# 控制台打印信息
username: demo' or 1=1; --, password: 123
sql: select * from user where username='demo' or 1=1; --' and password='123'
result: [(1, 'test', 'test')]

此时我们已经通过 SQL 注入攻击的方式绕过了后台系统的验证,前端用户在不知道用户名和密码的情况下实现了登录。

由控制台打印信息可以看到,实际上我们最终拼装的 SQL 语句为 select * from user where username='demo' or 1=1; --' and password='123',在 SQL 中 ; 用来结束一条语句,-- 用来注释后面的语句,所以这条 SQL 语句真正被执行的有效部分为 select * from user where username='demo' or 1=1;,而这条语句的条件 or 1=1 永远成立,所以只要 user 表中有数据,总会查出结果,也就能够返回 登录成功 响应。

这仅是在不知道用户名密码的情况下实现了登录,实际上还可能出现更糟糕的情况,如果代码中执行 SQL 语句不使用 cursor.execute(sql) 方法(execute 方法只支持执行单条 SQL 语句),而是使用 cursor.executescript(sql) 方法(executescript 方法支持执行一段 SQL 语句),并且用户猜到我们的用户表名为 user,那么用户将可以在登录页面的 username 输入框输入 demo' or 1=1; drop table user; -- 来删除整张 user 表。

由此可见,SQL 注入是一个相当危险的 Web 安全漏洞攻击。

防范方法

通过演示示例我们可以发现,SQL 注入攻击方式主要是通过用户输入 SQL 关键字来实现,并且如果用户想删除表数据则需要知道表名,所以防范 SQL 注入主要通过定义复杂表名和对用户输入的 SQL 关键字进行转义。

防范 SQL 注入攻击方法有很多,我这里主要介绍两种比较常见并且开发成本相对较低的解决方案:

  1. 可以使用参数化查询的方式执行 SQL 语句,从而避免手动拼接有安全风险的 SQL 语句。我们可以将上面演示的 Web 程序中拼接 SQL 语句的部分代码改为使用参数查询。
# demo_sql_inject.py
import sqlite3
from flask import Flask, request
app = Flask(__name__)
@app.route('/login', methods=['GET', 'POST'])
def login():
    # 如果为 POST 请求,则处理登录逻辑
    if request.method == 'POST':
        # 解析前端页面通过 form 表单传递过来的用户名、密码
        form = request.form
        username = form.get('username')
        password = form.get('password')
        print(f'username: {username}, password: {password}')
        # 连接数据库
        conn = sqlite3.connect('test.db')
        cursor = conn.cursor()
        # 构建参数化查询语句
        sql = "select * from user where username=? and password=?"
        print(f'sql: {sql}')
        # 执行 SQL 语句,将用户名、密码组成元组传入 execute 方法的第二个参数
        cursor.execute(sql, (username, password))
        # 如果根据用户输入的用户名、密码能够查询出数据,则登录成功,否则登录失败
        result = cursor.fetchall()
        print(f'result: {result}')
        if result:
            return '登录成功'
        else:
            return '登录失败'
    # GET 请求,则返回登录表单
    else:
        return """
        <form method="post">
            <input name="username" placeholder="username">
            <input name="password" placeholder="password">
            <button>登录</button>
        </form>
        """
if __name__ == '__main__':
    app.run()

可以看到,程序其实只修改了两行代码,修改后的程序不再通过字符串的方式拼接 SQL,而是首先构建参数化查询语句 select * from user where username=? and password=?,使用 ? 对参数进行占位,然后在执行 SQL 时 cursor.execute(sql, (username, password)) 将用户名和密码组装成元组传入。

再次测试在用户名的输入框输入 demo' or 1=1; --,密码输入框输入 123,点击登录按钮,将会得到 登录失败 响应。

 

使用 sqlite3 驱动自带的参数化查询方式能够有效的避免 SQL 注入攻击,其原理就是在执行 cursor.execute(sql, (username, password)) 时,sqlite3 内部会自动对 SQL 关键字进行转义。

  1. 可以使用如 SQLAlchemy 等 ORM 来解决 SQL 注入的问题,主流 ORM 框架都是经过市场检验的,对常见的 SQL 注入攻击都有比较有效的解决方案。

当然,所谓安全都是相对的,并没有绝对的安全,对敏感数据加密,做好数据库备份都是非常必要的工作。

相关文章
|
3月前
|
Web App开发 监控 安全
OSS客户端签名直传实践:Web端安全上传TB级文件方案(含STS临时授权)
本文深入解析了客户端直传技术,涵盖架构设计、安全机制、性能优化等方面。通过STS临时凭证与分片上传实现高效安全的文件传输,显著降低服务端负载与上传耗时,提升系统稳定性与用户体验。
423 2
|
9天前
|
安全 网络协议 NoSQL
Web渗透-常见的端口及对其的攻击思路
本文介绍了常见网络服务端口及其安全风险,涵盖FTP、SSH、Telnet、SMTP、DNS、HTTP、SMB、数据库及远程桌面等20余个端口,涉及弱口令爆破、信息泄露、未授权访问、缓冲区溢出等典型漏洞,适用于网络安全学习与渗透测试参考。
180 2
|
9天前
|
安全 Linux iOS开发
Burp Suite Professional 2025.9 发布 - Web 应用安全、测试和扫描
Burp Suite Professional 2025.9 (macOS, Linux, Windows) - Web 应用安全、测试和扫描
111 0
Burp Suite Professional 2025.9 发布 - Web 应用安全、测试和扫描
|
2月前
|
安全 Linux iOS开发
Burp Suite Professional 2025.7 (macOS, Linux, Windows) - Web 应用安全、测试和扫描
Burp Suite Professional 2025.7 (macOS, Linux, Windows) - Web 应用安全、测试和扫描
329 0
Burp Suite Professional 2025.7 (macOS, Linux, Windows) - Web 应用安全、测试和扫描
|
2月前
|
存储 安全 JavaScript
Web Storage有哪些安全风险?
Web Storage有哪些安全风险?
|
5月前
|
安全 Linux API
Burp Suite Professional 2025.4 发布 - Web 应用安全、测试和扫描
Burp Suite Professional 2025.4 (macOS, Linux, Windows) - Web 应用安全、测试和扫描
273 6
Burp Suite Professional 2025.4 发布 - Web 应用安全、测试和扫描
|
7月前
|
人工智能 Linux iOS开发
Burp Suite Professional 2025.2 (macOS, Linux, Windows) - Web 应用安全、测试和扫描
Burp Suite Professional 2025.2 (macOS, Linux, Windows) - Web 应用安全、测试和扫描
353 12
Burp Suite Professional 2025.2 (macOS, Linux, Windows) - Web 应用安全、测试和扫描
|
10月前
|
SQL 监控 安全
Flask 框架防止 SQL 注入攻击的方法
通过综合运用以上多种措施,Flask 框架可以有效地降低 SQL 注入攻击的风险,保障应用的安全稳定运行。同时,持续的安全评估和改进也是确保应用长期安全的重要环节。
410 71
|
9月前
|
SQL 安全 Java
除了Flask框架,还有哪些框架能防止SQL注入攻击?
这些框架都在安全方面有着较好的表现,通过它们的内置机制和安全特性,可以有效地降低 SQL 注入攻击的风险。然而,无论使用哪个框架,开发者都需要具备良好的安全意识,正确配置和使用框架提供的安全功能,以确保应用的安全可靠。同时,持续关注安全更新和漏洞修复也是非常重要的。
289 61
|
6月前
|
安全 大数据 数据挖掘
课时9:阿里云Web应用防火墙:全面保障网站的安全与可用性
阿里云Web应用防火墙(WAF)基于阿里巴巴十年攻防经验,提供全面的网站安全防护。它通过Web应用防护、CC攻击防护和业务风控,有效应对各类网络威胁,确保网站的安全与可用性。智能双引擎技术降低误报率,实时数据分析和虚拟补丁更新保障系统安全。WAF已成功护航多个重大活动,为企业提供高效、简便的安全解决方案。
150 0