sql >> データベース >  >> RDS >> Database

PythonによるSQLインジェクション攻撃の防止

    数年ごとに、Open Web Application Security Project(OWASP)は、最も重大なWebアプリケーションセキュリティリスクをランク付けします。最初の報告以来、注射のリスクは常にトップでした。すべてのインジェクションタイプの中で、SQLインジェクション は最も一般的な攻撃ベクトルの1つであり、間違いなく最も危険です。 Pythonは世界で最も人気のあるプログラミング言語の1つであるため、PythonSQLインジェクションから保護する方法を知ることは重要です。

    このチュートリアルでは、次のことを学びます。

    • PythonSQLインジェクション とそれを防ぐ方法
    • クエリを作成する方法 パラメータとしてリテラルと識別子の両方を使用
    • クエリを安全に実行する方法 データベース内

    このチュートリアルは、すべてのデータベースエンジンのユーザーに適しています。 。ここでの例ではPostgreSQLを使用していますが、結果は他のデータベース管理システム(SQLite、MySQL、Microsoft SQL Server、Oracleなど)で再現できます。

    無料ボーナス: Python Masteryに関する5つの考え、Python開発者向けの無料コースで、Pythonスキルを次のレベルに引き上げるために必要なロードマップと考え方を示します。


    PythonSQLインジェクションについて

    SQLインジェクション攻撃は非常に一般的なセキュリティの脆弱性であるため、伝説的な xkcd ウェブコミックはそれにコミックを捧げました:

    SQLクエリの生成と実行は一般的なタスクです。ただし、SQLステートメントの作成に関しては、世界中の企業がひどい間違いを犯すことがよくあります。 ORMレイヤーは通常SQLクエリを作成しますが、独自に作成する必要がある場合もあります。

    Pythonを使用してこれらのクエリをデータベースに直接実行すると、システムを危険にさらす可能性のある間違いを犯す可能性があります。このチュートリアルでは、動的SQLクエリをなしで作成する関数を正常に実装する方法を学習します。 システムをPythonSQLインジェクションのリスクにさらします。



    データベースのセットアップ

    開始するには、新しいPostgreSQLデータベースをセットアップし、データを入力します。チュートリアル全体を通して、このデータベースを使用して、PythonSQLインジェクションがどのように機能するかを直接確認します。


    データベースの作成

    まず、シェルを開き、ユーザーpostgresが所有する新しいPostgreSQLデータベースを作成します。 :

    $ createdb -O postgres psycopgtest
    

    ここでは、コマンドラインオプション-Oを使用しました データベースの所有者をユーザーpostgresに設定します 。データベースの名前も指定しました。これはpsycopgtestです。 。

    注: postgres 特別なユーザーです 、通常は管理タスク用に予約しますが、このチュートリアルでは、postgresを使用しても問題ありません。 。ただし、実際のシステムでは、データベースの所有者となる別のユーザーを作成する必要があります。

    新しいデータベースの準備が整いました。 psqlを使用して接続できます :

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

    これで、データベースpsycopgtestに接続されました。 ユーザーとしてpostgres 。このユーザーはデータベースの所有者でもあるため、データベース内のすべてのテーブルに対する読み取り権限があります。



    データを使用したテーブルの作成

    次に、いくつかのユーザー情報を含むテーブルを作成し、それにデータを追加する必要があります。

    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)
    

    テーブルには2つの列があります:username およびadminadmin 列は、ユーザーが管理者権限を持っているかどうかを示します。あなたの目標は、adminをターゲットにすることです フィールドとそれを悪用しようとします。



    Python仮想環境のセットアップ

    データベースができたので、次はPython環境をセットアップします。これを行う方法の詳細な手順については、Python仮想環境:入門書をご覧ください。

    新しいディレクトリに仮想環境を作成します:

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

    このコマンドを実行すると、venvという新しいディレクトリが作成されます。 作成されます。このディレクトリには、仮想環境内にインストールしたすべてのパッケージが保存されます。



    データベースへの接続

    Pythonでデータベースに接続するには、データベースアダプタが必要です。 。ほとんどのデータベースアダプタは、PythonデータベースAPI仕様PEP 249のバージョン2.0に準拠しています。すべての主要なデータベースエンジンには、主要なアダプタがあります。

    データベース アダプター
    PostgreSQL Psycopg
    SQLite sqlite3
    Oracle cx_oracle
    MySQL MySQLdb

    PostgreSQLデータベースに接続するには、PythonでPostgreSQL用に最も一般的なアダプタであるPsycopgをインストールする必要があります。 Django ORMはデフォルトでこれを使用し、SQLAlchemyでもサポートされています。

    ターミナルで、仮想環境をアクティブ化し、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
    

    これで、データベースへの接続を作成する準備が整いました。 Pythonスクリプトの始まりは次のとおりです。

    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 接続するデータベースの名前です。以前に作成したデータベースpsycopgtestに接続します 。

    • user データベースへのアクセス許可を持つユーザーです。この場合、所有者としてデータベースに接続する必要があるため、ユーザーにpostgresを渡します。 。

    • password userで指定した人のパスワードです 。ほとんどの開発環境では、ユーザーはパスワードなしでローカルデータベースに接続できます。

    接続を設定した後、autocommit=Trueを使用してセッションを構成しました 。 autocommitをアクティブ化する commitを発行してトランザクションを手動で管理する必要がないことを意味します またはrollback 。これは、ほとんどのORMのデフォルトの動作です。ここでもこの動作を使用して、トランザクションの管理ではなくSQLクエリの作成に集中できるようにします。

    注: Djangoユーザーは、ORMが使用する接続のインスタンスをdjango.db.connectionから取得できます。 :

    from django.db import connection
    


    クエリの実行

    データベースへの接続ができたので、クエリを実行する準備が整いました。

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

    connectionを使用しました cursorを作成するオブジェクト 。 Pythonのファイルと同じように、cursor コンテキストマネージャーとして実装されます。コンテキストを作成するとき、cursor データベースにコマンドを送信するために使用するために開かれます。コンテキストが終了すると、cursor 閉じて、使用できなくなります。

    注: コンテキストマネージャーの詳細については、Pythonコンテキストマネージャーと「with」ステートメントを確認してください。

    コンテキスト内で、cursorを使用しました クエリを実行して結果を取得します。この場合、usersの行をカウントするクエリを発行しました テーブル。クエリから結果を取得するには、cursor.fetchone()を実行しました。 タプルを受け取りました。クエリは1つの結果しか返すことができないため、fetchone()を使用しました 。クエリが複数の結果を返す場合は、cursorを繰り返す必要があります。 または、他のfetch*のいずれかを使用します メソッド。




    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
    

    ユーザーが存在しない場合、TypeError 上げられます。これは、.fetchone()が原因です。 Noneを返します 結果が見つからず、Noneを解凍した場合 TypeErrorを発生させます 。タプルを解凍できる唯一の場所は、adminにデータを入力する場所です。 resultから 。

    存在しないユーザーを処理するには、resultの場合の特別なケースを作成します Noneです :

    # 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()
    
        if result is None:
            # User does not exist
            return False
    
        admin, = result
        return admin
    

    ここでは、Noneを処理するための特別なケースを追加しました 。 usernameの場合 が存在しない場合、関数はFalseを返す必要があります 。もう一度、一部のユーザーで機能をテストします:

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

    素晴らしい!この関数は、存在しないユーザー名も処理できるようになりました。



    PythonSQLインジェクションを使用したクエリパラメータの活用

    前の例では、文字列補間を使用してクエリを生成しました。次に、クエリを実行し、結果の文字列をデータベースに直接送信しました。ただし、このプロセス中に見落とした可能性のあることがあります。

    usernameを思い出してください is_admin()に渡した引数 。この変数は正確に何を表していますか? username 実際のユーザーの名前を表す単なる文字列です。ただし、これから説明するように、侵入者はこの種の監視を簡単に悪用し、PythonSQLインジェクションを実行することで大きな害を及ぼす可能性があります。

    次のユーザーが管理者であるかどうかを確認してください:

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

    待って…どうしたの?

    実装をもう一度見てみましょう。データベースで実行されている実際のクエリを印刷します:

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

    結果のテキストには、3つのステートメントが含まれています。 Python SQLインジェクションがどのように機能するかを正確に理解するには、各部分を個別に検査する必要があります。最初のステートメントは次のとおりです。

    select admin from users where username = '';
    

    これは意図したクエリです。セミコロン(; )クエリを終了するため、このクエリの結果は重要ではありません。次は2番目のステートメントです:

    select true;
    

    この声明は侵入者によって作成されました。常にTrueを返すように設計されています 。

    最後に、次の短いコードが表示されます:

    --'
    

    このスニペットは、その後に続くものをすべて解読します。侵入者はコメント記号を追加しました(-- )最後のプレースホルダーの後に入力した可能性のあるすべてのものをコメントに変換します。

    この引数を使用して関数を実行すると、常にTrueが返されます。 。たとえば、ログインページでこの機能を使用すると、侵入者はユーザー名'; select true; -- 、アクセスが許可されます。

    これが悪いと思うなら、悪化する可能性があります!テーブル構造の知識を持つ侵入者は、PythonSQLインジェクションを使用して永続的な損傷を引き起こす可能性があります。たとえば、侵入者は更新ステートメントを挿入して、データベース内の情報を変更できます。

    >>>
    >>> 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';
    

    このセクションでは、adminを更新します trueに ユーザーhakiの場合 。

    最後に、次のコードスニペットがあります:

    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
    

    それで、なぜこれが起こっているのですか?さて、あなたはusernameについて何を知っていますか 口論?ユーザー名を表す文字列である必要があることはわかっていますが、このアサーションを実際に確認または強制することはありません。これは危険です!攻撃者がシステムをハッキングしようとするときに、まさにそれを探しています。


    安全なクエリパラメータの作成

    前のセクションでは、侵入者が慎重に作成された文字列を使用して、システムを悪用し、管理者権限を取得する方法を説明しました。問題は、クライアントから渡された値を、いかなる種類のチェックや検証も実行せずに、データベースに直接実行できるようにすることでした。 SQLインジェクションは、このタイプの脆弱性に依存しています。

    データベースクエリでユーザー入力が使用される場合は常に、SQLインジェクションに脆弱性が存在する可能性があります。 Python SQLインジェクションを防ぐための鍵は、開発者が意図したとおりに値が使用されていることを確認することです。前の例では、usernameを対象としていました 文字列として使用されます。実際には、生のSQLステートメントとして使用されていました。

    値が意図したとおりに使用されていることを確認するには、エスケープする必要があります 値。たとえば、侵入者が文字列引数の代わりに生のSQLを挿入するのを防ぐために、引用符をエスケープできます。

    >>>
    >>> # BAD EXAMPLE. DON'T DO THIS!
    >>> username = username.replace("'", "''")
    

    これはほんの一例です。 Python SQLインジェクションを防止しようとするときに、考慮すべき特殊文字やシナリオがたくさんあります。幸運なことに、最新のデータベースアダプターには、クエリパラメーターを使用してPythonSQLインジェクションを防止するための組み込みツールが付属しています。 。これらは、パラメータを使用してクエリを作成するために、単純な文字列補間の代わりに使用されます。

    注: さまざまなアダプタ、データベース、およびプログラミング言語が、さまざまな名前でクエリパラメータを参照します。一般的な名前には、バインド変数が含まれます 、置換変数 、および置換変数

    脆弱性についての理解が深まったので、文字列補間の代わりにクエリパラメータを使用して関数を書き直す準備ができました:

     1def is_admin(username: str) -> bool:
     2    with connection.cursor() as cursor:
     3        cursor.execute("""
     4            SELECT
     5                admin
     6            FROM
     7                users
     8            WHERE
     9                username = %(username)s
    10        """, {
    11            'username': username
    12        })
    13        result = cursor.fetchone()
    14
    15    if result is None:
    16        # User does not exist
    17        return False
    18
    19    admin, = result
    20    return admin
    

    この例の違いは次のとおりです。

    • 9行目 名前付きパラメーターusernameを使用しました ユーザー名をどこに置くべきかを示します。パラメータusernameに注目してください 単一引用符で囲まれなくなりました。

    • 11行目 usernameの値を渡しました cursor.execute()の2番目の引数として 。接続では、usernameのタイプと値が使用されます データベースでクエリを実行するとき。

    この関数をテストするには、以前の危険な文字列など、いくつかの有効な値と無効な値を試してください。

    >>>
    >>> is_admin('haki')
    False
    >>> is_admin('ran')
    True
    >>> is_admin('foo')
    False
    >>> is_admin("'; select true; --")
    False
    

    すばらしい!この関数は、すべての値に対して期待される結果を返しました。さらに、危険な文字列は機能しなくなります。理由を理解するために、execute()によって生成されたクエリを調べることができます :

    >>>
    >>> with connection.cursor() as cursor:
    ...    cursor.execute("""
    ...        SELECT
    ...            admin
    ...        FROM
    ...            users
    ...        WHERE
    ...            username = %(username)s
    ...    """, {
    ...        'username': "'; select true; --"
    ...    })
    ...    print(cursor.query.decode('utf-8'))
    SELECT
        admin
    FROM
        users
    WHERE
        username = '''; select true; --'
    

    接続はusernameの値を処理しました 文字列として、文字列を終了してPythonSQLインジェクションを導入する可能性のある文字をエスケープしました。



    安全なクエリパラメータの受け渡し

    データベースアダプタは通常、クエリパラメータを渡すためのいくつかの方法を提供します。 名前付きプレースホルダー 通常は読みやすさの点で最適ですが、一部の実装では他のオプションを使用することでメリットが得られる場合があります。

    クエリパラメータを使用する正しい方法と間違った方法のいくつかを簡単に見てみましょう。次のコードブロックは、避けたいクエリの種類を示しています。

    # BAD EXAMPLES. DON'T DO THIS!
    cursor.execute("SELECT admin FROM users WHERE username = '" + username + '");
    cursor.execute("SELECT admin FROM users WHERE username = '%s' % username);
    cursor.execute("SELECT admin FROM users WHERE username = '{}'".format(username));
    cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'");
    

    これらの各ステートメントは、usernameを渡します いかなる種類のチェックや検証も実行せずに、クライアントからデータベースに直接送信します。この種のコードは、PythonSQLインジェクションを招待するのに適しています。

    対照的に、これらのタイプのクエリは安全に実行できるはずです。

    # SAFE EXAMPLES. DO THIS!
    cursor.execute("SELECT admin FROM users WHERE username = %s'", (username, ));
    cursor.execute("SELECT admin FROM users WHERE username = %(username)s", {'username': username});
    

    これらのステートメントでは、username 名前付きパラメータとして渡されます。これで、データベースは指定されたタイプと値のusernameを使用します クエリを実行するとき、PythonSQLインジェクションからの保護を提供します。




    SQLコンポジションの使用

    これまで、リテラルのパラメータを使用してきました。 リテラル 数値、文字列、日付などの値です。しかし、別のクエリを作成する必要があるユースケースがある場合はどうでしょうか。たとえば、パラメータがテーブルや列の名前など、別のクエリである場合はどうでしょうか。

    前の例に触発されて、テーブルの名前を受け取り、そのテーブルの行数を返す関数を実装しましょう。

    # BAD EXAMPLE. DON'T DO THIS!
    def count_rows(table_name: str) -> int:
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT
                    count(*)
                FROM
                    %(table_name)s
            """, {
                'table_name': table_name,
            })
            result = cursor.fetchone()
    
        rowcount, = result
        return rowcount
    

    ユーザーテーブルで関数を実行してみてください:

    >>>
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 9, in count_rows
    psycopg2.errors.SyntaxError: syntax error at or near "'users'"
    LINE 5:                 'users'
                            ^
    

    コマンドはSQLの生成に失敗しました。すでに見てきたように、データベースアダプタは変数を文字列またはリテラルとして扱います。ただし、テーブル名は単純な文字列ではありません。これがSQL構成の出番です。

    文字列補間を使用してSQLを作成するのは安全ではないことはすでにご存知でしょう。幸い、Psycopgはpsycopg.sqlというモジュールを提供しています SQLクエリを安全に作成するのに役立ちます。 psycopg.sql.SQL()を使用して関数を書き直してみましょう :

    from psycopg2 import sql
    
    def count_rows(table_name: str) -> int:
        with connection.cursor() as cursor:
            stmt = sql.SQL("""
                SELECT
                    count(*)
                FROM
                    {table_name}
            """).format(
                table_name = sql.Identifier(table_name),
            )
            cursor.execute(stmt)
            result = cursor.fetchone()
    
        rowcount, = result
        return rowcount
    

    この実装には2つの違いがあります。まず、sql.SQL()を使用しました クエリを作成します。次に、sql.Identifier()を使用しました 引数の値に注釈を付けるtable_name 。 (識別子 列またはテーブルの名前です。)

    注: 人気のあるパッケージdjango-debug-toolbarのユーザー psycopg.sql.SQL()で構成されたクエリのSQLパネルでエラーが発生する場合があります 。バージョン2.0でのリリースには修正が必要です。

    次に、usersで関数を実行してみます テーブル:

    >>>
    >>> count_rows('users')
    2
    

    素晴らしい!次に、テーブルが存在しない場合に何が起こるかを見てみましょう。

    >>>
    >>> count_rows('foo')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 11, in count_rows
    psycopg2.errors.UndefinedTable: relation "foo" does not exist
    LINE 5:                 "foo"
                            ^
    

    この関数はUndefinedTableをスローします 例外。次の手順では、この例外を、関数がPythonSQLインジェクション攻撃から安全であることを示すものとして使用します。

    注: 例外UndefinedTable psycopg2バージョン2.8で追加されました。以前のバージョンのPsycopgを使用している場合は、別の例外が発生します。

    すべてをまとめるには、テーブル内の行を特定の制限までカウントするオプションを追加します。この機能は、非常に大きなテーブルに役立つ場合があります。これを実装するには、LIMITを追加します 制限値のクエリパラメータとともに、クエリの句:

    from psycopg2 import sql
    
    def count_rows(table_name: str, limit: int) -> int:
        with connection.cursor() as cursor:
            stmt = sql.SQL("""
                SELECT
                    COUNT(*)
                FROM (
                    SELECT
                        1
                    FROM
                        {table_name}
                    LIMIT
                        {limit}
                ) AS limit_query
            """).format(
                table_name = sql.Identifier(table_name),
                limit = sql.Literal(limit),
            )
            cursor.execute(stmt)
            result = cursor.fetchone()
    
        rowcount, = result
        return rowcount
    

    このコードブロックでは、limitに注釈を付けました sql.Literal()を使用する 。前の例のように、psycopg 単純なアプローチを使用する場合、すべてのクエリパラメータをリテラルとしてバインドします。ただし、sql.SQL()を使用する場合 、sql.Identifier()のいずれかを使用して各パラメータに明示的に注釈を付ける必要があります またはsql.Literal()

    注: 残念ながら、Python API仕様は識別子のバインドを対象としておらず、リテラルのみを対象としています。 Psycopgは、リテラルと識別子の両方を使用してSQLを安全に作成する機能を追加した唯一の人気のあるアダプターです。この事実により、識別子をバインドする際に細心の注意を払うことがさらに重要になります。

    関数を実行して、機能することを確認します。

    >>>
    >>> count_rows('users', 1)
    1
    >>> count_rows('users', 10)
    2
    

    関数が機能していることを確認したら、それも安全であることを確認してください。

    >>>
    >>> count_rows("(select 1) as foo; update users set admin = true where name = 'haki'; --", 1)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 18, in count_rows
    psycopg2.errors.UndefinedTable: relation "(select 1) as foo; update users set admin = true where name = '" does not exist
    LINE 8:                     "(select 1) as foo; update users set adm...
                                ^
    

    このトレースバックは、psycopg 値をエスケープすると、データベースはそれをテーブル名として扱いました。この名前のテーブルは存在しないため、UndefinedTable 例外が発生し、ハッキングされませんでした!



    結論

    なしで動的SQLを構成する関数を正常に実装しました システムをPythonSQLインジェクションのリスクにさらします!セキュリティを損なうことなく、クエリでリテラルと識別子の両方を使用しました。

    学んだこと:

    • PythonSQLインジェクション であり、それをどのように活用できるか
    • PythonSQLインジェクションを防ぐ方法 クエリパラメータの使用
    • SQLステートメントを安全に作成する方法 パラメータとしてリテラルと識別子を使用する

    これで、外部からの攻撃に耐えられるプログラムを作成できるようになりました。出て行ってハッカーを阻止してください!



    1. MySQLデータベース開発のためのGearHost入門

    2. SQL Serverでリンクされたデータベースサーバーを作成してクエリするにはどうすればよいですか?

    3. SELECTDISTINCTはさまざまなケースを無視します

    4. MariaDBでのTIMESTAMPDIFF()のしくみ