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

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: 使用Python防止SQL注入攻击(上)

阅读本文需要7.5分钟


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


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

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


了解Python SQL注入


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

图片来源互联网


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


设置数据库


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

创建一个数据库

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


$ createdb -O postgres psycopgtest

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

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



$ psql -U postgres -d psycopgtest
psql (11.2, server 10.5)
Type "help" for help.

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

创建数据表

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


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环境了。

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

    (~/src) $ mkdir psycopgtest
    (~/src) $ cd psycopgtest
    (~/src/psycopgtest) $ python3 -m venv venv

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


    连接数据库

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

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


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

      (~/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

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

        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。激活自动提交意味着我们不必通过发出提交或手动管理事务。


        行查询

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


        >>> with connection.cursor() as cursor:
        ...     cursor.execute('SELECT COUNT(*) FROM users')
        ...     result = cursor.fetchone()
        ... print(result)
        (2,)


        在SQL中使用查询参数

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


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


        # 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中。


        >>> is_admin('haki')
        False
        >>> is_admin('ran')
        True

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

          >>> 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时创建一个特殊的情况:


          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。如下:

            >>> is_admin('haki')
            False
            >>> is_admin('ran')
            True
            >>> is_admin('foo')
            False


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

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


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

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


            >>> is_admin("'; select true; --")
            True

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

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


            >>> print("select admin from users where username = '%s'" % "'; select true; --")
            select admin from users where username = ''; select true; --'

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


            select admin from users where username = '';

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


            select true;

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


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


            --'

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


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


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


            >>> is_admin('haki')
            False
            >>> is_admin("'; update users set admin = 'true' where username = 'haki'; select true; --")
            True
            >>> is_admin('haki')
            True

            让我们再来分解一下:


            ';

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


            update users set admin = 'true' where username = 'haki';

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


            select true; --

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


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

              psycopgtest=# select * from users;
               username | admin
              ----------+-------
               ran      | t
               haki     | t
              (2 rows)

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


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


              psycopgtest=# update users set admin = false where username = 'haki';
              UPDATE 1

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



              待续。。。


              推荐阅读

              做个Python反转字符串的实验

              列表推导到zip()函数的五种技巧

              Python怎么删除字符

              岁月有你   惜惜相处

              相关实践学习
              使用PolarDB和ECS搭建门户网站
              本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
              阿里云数据库产品家族及特性
              阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
              相关文章
              |
              2月前
              |
              SQL 监控 小程序
              在微信小程序中使用 Vant 时如何防止 SQL 注入攻击?
              在微信小程序中使用 Vant 时如何防止 SQL 注入攻击?
              139 58
              |
              14天前
              |
              SQL 数据挖掘 Python
              数据分析编程:SQL,Python or SPL?
              数据分析编程用什么,SQL、python or SPL?话不多说,直接上代码,对比明显,明眼人一看就明了:本案例涵盖五个数据分析任务:1) 计算用户会话次数;2) 球员连续得分分析;3) 连续三天活跃用户数统计;4) 新用户次日留存率计算;5) 股价涨跌幅分析。每个任务基于相应数据表进行处理和计算。
              |
              15天前
              |
              SQL 存储 安全
              什么是XSS攻击?什么是SQL注入攻击?什么是CSRF攻击?
              理解并防范XSS、SQL注入和CSRF攻击是Web应用安全的基础。通过采用严格的输入验证、使用安全编码实践以及实现适当的身份验证和授权机制,可以有效防止这些常见的Web攻击,保障应用程序和用户的数据安全。
              25 0
              |
              1月前
              |
              SQL 机器学习/深度学习 数据库
              SQL与Python集成:数据库操作无缝衔接
              在开始之前,确保你已经安装了必要的Python库,如`sqlite3`(用于SQLite数据库)或`psycopg2`(用于PostgreSQL数据库)。这些库提供了Python与SQL数据库之间的接口。
              |
              1月前
              |
              SQL 安全 数据库
              Python防止SQL注入攻击的方法
              Python防止SQL注入攻击的方法
              59 0
              |
              1月前
              |
              SQL 机器学习/深度学习 数据采集
              SQL与Python集成:数据库操作无缝衔接2a.bijius.com
              Python与SQL的集成是现代数据科学和工程实践的核心。通过有效的数据查询、管理与自动化,可以显著提升数据分析和决策过程的效率与准确性。随着技术的不断发展,这种集成的应用场景将更加广泛,为数据驱动的创新提供更强大的支持。
              |
              1月前
              |
              SQL 机器学习/深度学习 数据库
              SQL与Python集成:数据库操作无缝衔接
              1. Python与SQL集成的关键步骤 在开始之前,确保你已经安装了必要的Python库,如`sqlite3`(用于SQLite数据库)或`psycopg2`(用于PostgreSQL数据库)。这些库提供了Python与SQL数据库之间的接口。
              |
              1月前
              |
              SQL 云安全 监控
              通过 Python 和 SQL 集成加强云环境
              通过 Python 和 SQL 集成加强云环境
              31 0
              |
              2月前
              |
              SQL 数据处理 数据库
              python 提取出sql语句中where的值
              python 提取出sql语句中where的值
              |
              SQL 关系型数据库 Python
              下一篇
              无影云桌面