前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Python防止SQL注入攻击(上)

使用Python防止SQL注入攻击(上)

作者头像
Python知识大全
发布2020-02-13 16:06:40
4.1K0
发布2020-02-13 16:06:40
举报
文章被收录于专栏:Python 知识大全Python 知识大全

阅读本文需要7.5分钟

SQL注入是最常见的攻击之一,并且可以说是最危险的。由于Python是世界上最受欢迎的编程语言之一,因此了解如何防止Python SQL注入至关重要。

在本教程中,我们将学习:

  • 什么是Python SQL注入以及如何防止注入
  • 如何使用文字和标识符作为参数组合查询
  • 如何安全地执行数据库中的查询

了解Python SQL注入

SQL注入攻击是一种常见的安全漏洞,传说中的xkcd网络漫画专门将其漫画化:

图片来源互联网

当使用Python将这些查询直接执行到数据库中时,很可能会犯可能损害系统的错误。在本教程中,将学习如何成功实现组成动态SQL查询的函数,而又不会使我们的系统遭受Python SQL注入的威胁。

设置数据库

首先,先建立一个新的PostgreSQL数据库并插入数据。

创建一个数据库

首先,创建一个新的PostgreSQL数据库拥有的用户postgres:

代码语言:javascript
复制
$ createdb -O postgres psycopgtest

这里使用命令行选项-O将数据库的所有者设置为用户postgres。指定了数据库的名称,即psycopgtest。

新数据库已经准备就绪!连接到并开始使用psql:

代码语言:javascript
复制
$ psql -U postgres -d psycopgtest
psql (11.2, server 10.5)
Type "help" for help.

现在以用户postgres的身份连接到数据库psycopgtest。该用户也是数据库所有者,因此将对数据库中的每个表都具有读权限。

创建数据表

接下来,需要创建一个表与一些用户信息,并添加数据到它:

代码语言:javascript
复制
psycopgtest=# CREATE TABLE users (
    username varchar(30),
    admin boolean
);
CREATE TABLE

psycopgtest=# INSERT INTO users
    (username, admin)
VALUES
    ('ran', true),
    ('haki', false);
INSERT 0 2

psycopgtest=# SELECT * FROM users;
 username | admin
----------+-------
 ran      | t
 haki     | f
(2 rows)

该表有两列:username和admin。admin列指示用户是否具有管理权限。我们的目标是试图滥用它。

设置Python虚拟环境

现在我们已经有了一个数据库,是时候设置Python环境了。

在一个新目录中创建虚拟环境:

代码语言:javascript
复制
(~/src) $ mkdir psycopgtest
(~/src) $ cd psycopgtest
(~/src/psycopgtest) $ python3 -m venv venv

运行此命令后,将创建一个名为venv的新目录。此目录将存储在虚拟环境中安装的所有包。

连接数据库

要连接到Python中的数据库,需要一个数据库适配器。

要连接到PostgreSQL数据库,需要安装Psycopg,这是Python中最流行的PostgreSQL适配器。

在终端中,激活虚拟环境并使用pip安装psycopg:

代码语言:javascript
复制
(~/src/psycopgtest) $ source venv/bin/activate
(~/src/psycopgtest) $ python -m pip install psycopg2>=2.8.0
Collecting psycopg2
  Using cached https://....
  psycopg2-2.8.2.tar.gz
Installing collected packages: psycopg2
  Running setup.py install for psycopg2 ... done
Successfully installed psycopg2-2.8.2

现在可以连接到数据库的了。

代码语言:javascript
复制
import psycopg2

connection = psycopg2.connect(
    host="localhost",
    database="psycopgtest",
    user="postgres",
    password=None,
)
connection.set_session(autocommit=True)

使用psycopg2.connect()来创建连接。这个函数接受以下参数:

host:数据库所在服务器的IP地址或DNS。在本例中,主机是localhost。

database:要连接的数据库的名称。

user:具有数据库权限的用户。

password:用户的密码。在大多数开发环境中

在设置连接之后,将会话配置为autocommit=True。激活自动提交意味着我们不必通过发出提交或手动管理事务。

执行查询

现在我们已经连接到数据库,准备执行一个查询:

代码语言:javascript
复制
>>> with connection.cursor() as cursor:
...     cursor.execute('SELECT COUNT(*) FROM users')
...     result = cursor.fetchone()
... print(result)
(2,)

在SQL中使用查询参数

在前面,我们创建了一个数据库,连接到了它,并执行了一个查询。

首先,我们将实现一个函数来检查用户是否为管理员。is_admin()接受用户名并返回该用户的管理状态:

代码语言:javascript
复制
# BAD EXAMPLE. DON'T DO THIS!
def is_admin(username: str) -> bool:
    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT
                admin
            FROM
                users
            WHERE
                username = '%s'
        """ % username)
        result = cursor.fetchone()
    admin, = result
    return admin

执行这个函数查询来获取给定用户名的admin列的值。使用fetchone()返回一个带有单个结果的元组。然后,将这个元组解压缩到变量admin中。

代码语言:javascript
复制
>>> is_admin('haki')
False
>>> is_admin('ran')
True

到目前为止一切正常。但是那些不存在的用户呢?看看这段Python代码:

代码语言:javascript
复制
>>> is_admin('foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 12, in is_admin
TypeError: cannot unpack non-iterable NoneType object

如果当用户不存在时,将引发一个错误。这是因为.fetchone()在没有找到结果时返回None,而解包None会引发一个类型错误。

为了处理不存在的用户,在结果为None时创建一个特殊的情况:

代码语言:javascript
复制
def is_admin(username: str) -> bool:
    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT
                admin
            FROM
                users
            WHERE
                username = '%s'
        """ % username)
        result = cursor.fetchone()

    if result is None:
        # User does not exist
        return False

    admin, = result
    return admin

这里,我们添加了一个处理None的特殊情况。如果用户名不存在,那么函数应该返回False。如下:

代码语言:javascript
复制
>>> is_admin('haki')
False
>>> is_admin('ran')
True
>>> is_admin('foo')
False

使用Python SQL注入利用查询参数

在前面的示例中,使用字符串插值表达式生成查询。然后,执行查询并将结果字符串直接发送到数据库。然而,在这个过程中我们可能忽略了一些东西。

之前我们传递给is_admin()的用户名参数。这个变量到底代表什么呢?大家可能认为username只是表示实际用户名的字符串。但是,入侵者可以很容易地利用这种疏忽,并通过执行Python SQL注入造成重大危害。

尝试检查以下用户是否是管理员:

代码语言:javascript
复制
>>> is_admin("'; select true; --")
True

天呐!!!发生什么事了?

让我们再看一下实现。打印出在数据库中执行的实际查询:

代码语言:javascript
复制
>>> print("select admin from users where username = '%s'" % "'; select true; --")
select admin from users where username = ''; select true; --'

结果文本包含三个语句。为了准确地理解Python SQL注入是如何工作的,我们需要分别检查每个部分。第一:

代码语言:javascript
复制
select admin from users where username = '';

这是我们想要的查询。分号终止查询,因此此查询的结果不怎么重要。第二:

代码语言:javascript
复制
select true;

这是入侵者编造的。它的设计总是返回True。

最后,将看到这一小段代码:

代码语言:javascript
复制
--'

这个代码段将消除后面的任何内容。入侵者添加了注释符号(——)来将可能放置在最后一个占位符之后的所有内容转换成注释。

当使用这个参数执行函数时,它总是返回True。例如,如果大家在登录页面中使用此函数,则入侵者可以使用用户名'登录;选择正确的;,他们将被允许进入。

更可怕的是了解表结构的入侵者可以使用Python SQL注入来造成永久性损害。例如,入侵者可以注入一条更新语句来改变数据库中的信息:

代码语言:javascript
复制
>>> is_admin('haki')
False
>>> is_admin("'; update users set admin = 'true' where username = 'haki'; select true; --")
True
>>> is_admin('haki')
True

让我们再来分解一下:

代码语言:javascript
复制
';

这段代码终止了查询,就像前面的注入一样。下一次注入如下:

代码语言:javascript
复制
update users set admin = 'true' where username = 'haki';

这次将用户haki的admin更新为true 。代码如下:

代码语言:javascript
复制
select true; --

与前面的示例一样,返回true并注释掉后面所有的内容。

如果入侵者设法执行这个输入的功能,那么用户haki将成为一个管理员:

代码语言:javascript
复制
psycopgtest=# select * from users;
 username | admin
----------+-------
 ran      | t
 haki     | t
(2 rows)

他们可以用用户名haki登录。(如果入侵者真的想造成伤害,那么他们甚至可以发出DROP DATABASE命令。)

提前把haki恢复到原来的状态:

代码语言:javascript
复制
psycopgtest=# update users set admin = false where username = 'haki';
UPDATE 1

为什么会这样呢?我们对用户名参数了解多少?我们只知道它应该是一个表示用户名的字符串,但是我们实际上并没有检查或执行这个断言。这可能很危险!攻击者试图利用这些东西入侵我们的系统。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python 知识大全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 了解Python SQL注入
  • 设置数据库
相关产品与服务
云数据库 PostgreSQL
腾讯云数据库 PostgreSQL(TencentDB for PostgreSQL,云 API 使用 postgres 作为简称)能够让您在云端轻松设置、操作和扩展目前功能最强大的开源数据库 PostgreSQL。腾讯云将负责绝大部分处理复杂而耗时的管理工作,如 PostgreSQL 软件安装、存储管理、高可用复制、以及为灾难恢复而进行的数据备份,让您更专注于业务程序开发。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档