1. ホーム
  2. データベース
  3. エムエスエル

そのPHP環境の普遍的なパスワードのSQLインジェクションの脆弱性と防御手段

2022-01-18 15:18:19

汎用パスワードのSQLインジェクション脆弱性とそのPHP環境構築・防御手段

I. 環境構築

この侵入環境は、以下のもので構築されています。

  • セッションベースのセッション
  • ログイン画面
  • ログイン成功画面
  • ログアウト画面
  • データベース構築
  • データベース接続

II. セッション セッション

  • サーバー側は session_start() 関数でセッションを開始する
  • この時点でログインに成功し、ユーザーのデータはサーバー側のクッキーであるsession=、すなわちsessionIDに保存されています。
  • 再びアクセスする必要がある場合
  • サーバーサイドの $_SESSION['...'] はユーザーセッションを取得します
  • そして、そのセッションIDとサーバーに存在するオリジナルのセッションIDを照合し、照合が成功すれば、そのユーザーは正しいということになります

3つ目は、環境構築コード

1. データベーススクリプトの作成

スクリプトは、MySQLでsourceコマンドを使用して実行することができます。

drop database if exists lab;
create database lab;
use lab;

create table users
(
    id int not null auto_increment,
    username char(32) not null,
    passcode char(32) not null,
    primary key(id)
);

insert into users(username,passcode) values('admin','admin123');
insert into users(username,passcode) values('alice','alice456');


2. ログイン画面 html

<html>

<head>
    <meta charset="UTF-8">
    <title>Login</title>
    <style>
        #a {
            width: 500px;
            text-align: center;
        }
        
        .b {
            width: 200px;
            height: 30px;
        }
    </style>
</head>

<body>
    <div id=a>
        <h2>Login!</h2>
        <form name="form_login" method="POST" action="check_login.php">
            Username:<input type="text" class="b" name="username" /><br> <br> 
  Password:<input type="password" class="b" name="password" /><br>
            <input type="submit" name="Submit" value="Submit" />
            <input type="reset" name="reset" value="Reset" />
        </form>
    </div>
</body>

</html>



3. データベースに正しいアカウントのパスワードを問い合わせる php コード

<?php
include('con_database.php');

$username=isset($_POST['username'])? $_POST['username']:'';
$password=isset($_POST['password'])? $_POST['password']:'';
if($username=='' || $password==''){
    echo "<script>alert('Please enter account and password!') </script>";
    exit;
}

$sql="select * from users where username='$username' and passcode='$password'";

$query=mysqli_query($con,$sql) or die('SQL statement execution failed'.mysqli_error($con));
if ($row=mysqli_fetch_array($query)){
    session_start();
    $_SESSION['username']=$row[1];
    echo "<a href='welcome.php'>歡迎访问</a>";
}else{
    echo "<script>alert('Login failed!') ;history.go(-1)</script>";
}
mysqli_close($con);
? >




4. データベースへの接続 php コード。

<?php
$con=mysqli_connect('127.0.0.1','root','root') or die("Database connection failed! ");
mysqli_select_db($con,'lab') or die("Database connection failed");
? >

5. ログアウトのログインコード(つまりセッションのセッションを閉じる)

<?php
session_start();
session_unset();
session_destroy();
echo "Logout successful";
? >

6. ログインに成功した場合の歓迎画面

<?php
session_start();
if(isset($_SESSION['username'])){
    echo "Welcome user". $_SESSION['username']. "Login";
    echo "<br>";
    echo "<a href=logout.php>Logout login</a>";
}else{
    echo "You do not have permission to access";
}
? >




この時点で、私たちの侵入環境は構築されました

IV. ユニバーサルパスワード脆弱性プロファイリング

  • ユーザー名紛失 Enter' or 1=1 or' ランダムなパスワードで、ログインできることがわかりました。
  • パスワード入力 'or '1=1 また、ログインすることができます

もちろん、ログインの方法は1つではありません。

元のクエリ文はこのようなものでした。

$sql="select * from users where username='$username' and passcode='$password'";



インジェクション後は、こうなります。

$sql="select * from users where username='' or 1=1 or ' and passcode='****'";

usernameがwhereで閉じられ、uh節が続き、その節が3つに分割され、orで結合されていることがわかる。
SQL文の中でandはorより優先順位が高いので、1=1が先に判定されて真、つまりwhere以降の文が真、つまりSQL文全体が真、つまりクエリが正しいことになる
そして、形成された文はusersテーブル全体を問い合わせることができ、その後に $row=mysqli_fetch_array($query) がクエリの最初の行の値として選択され、SQL ステートメントを満たし、ログイン認証をスキップします。
whereの後の単語が真であれば、バリデーションをスキップできることが、以下のように導き出されます。

  • ' or 1=1 #
    ' or 1=1 -- 
    
  • (その後にスペースが続く)
  • 'or"="or'

V. ユニバーサルパスワード攻撃対策

1. 正規表現によるユーザー入力の制限

ユーザー名の入力を制限するために、例えば、正規表現を使用することができます。/^[a-z0-9A-Z_]{5,16}$/
これにより、ユーザー名への入力は、5桁以上16桁以下の英数字のアンダースコアに制限されます。
この制限はcheck_login.phpで追加されます。

<?php
include('con_database.php');

$username=isset($_POST['username'])? $_POST['username']:'';
$password=isset($_POST['password'])? $_POST['password']:'';
if (!preg_match("/^[a-Z0-9A-Z_]{5,16}$/",$username)){
    echo "<script>alert('username format error')</script>";
    exit;

if($username=='' || $password==''){
    echo "<script>alert('Please enter account and password!') </script>";
    exit;
}

$sql="select * from users where username='$username' and passcode='$password'";

$query=mysqli_query($con,$sql) or die('SQL statement execution failed'.mysqli_error($con));
if ($row=mysqli_fetch_array($query)){
    session_start();
    $_SESSION['username']=$row[1];
    echo "<a href='welcome.php'>歡迎访问</a>";
}else{
    echo "<script>alert('Login failed!') ;history.go(-1)</script>";
}
mysqli_close($con);
}
? >


2. PHPのエスケープ関数の使用

  • addslashes()関数:シングルクォート、ダブルクォート、バックスラッシュ、NULLをエスケープすることができます。
  • mysql_escape_string() 関数、mysql_real_escape_string() 関数 これは SQL 文の記号をエスケープするもので、php7.x 版は mysqli になる予定である。
$username=isset($_POST['username'])?adslashes($_POST['username']):'';
$password=isset($_POST['password'])?mysqli_real_escape_string($con,$_POST['password']):'';



3. 関数をエスケープすることのデメリット

ワイドバイトエンコーディングではなく、UTF-8エンコーディングを使用しているため、'formed'が%5c%27になります。
Windowsのデフォルトはワイドバイトのgbkエンコーディングです。
5c の前に文字を追加して複雑な中国語を形成した場合でも、シングルクォートは出力されます。

VI. MySQLi パラメータ付きクエリ

パラメトリッククエリの場合、サーバーはパラメータの内容をSQLコマンドの一部としません。
その代わり、データベースはSQLコマンドのコンパイルを完了してから、パラメータを代入して実行します。
このとき、仮にパラメータに悪意のあるデータがあったとしても
しかし、この時点でSQL文とコンパイルは完了しています
がデータベースで実行されることはありません。

PHP は、mysql データベースにアクセスするための 3 つの拡張モジュールを提供します。

  • MySQL (PHP 5.5 で非推奨となりました)
  • MySQLi
  • PDO (PHP Data Object PHPデータオブジェクト)

PDOとMySQLiはオブジェクト指向のAPIを提供する
MySQLiもプロセス指向のAPIとして存在するので、MySQLからMySQLiへの変換は簡単です

以下は、mysqliの形でcheck_login.phpを記述する方法です。

<?php
include('con_database.php');

$username=isset($_POST['username'])? $_POST['username']:'';
$password=isset($_POST['password'])? $_POST['password']:'';

if($username==''||$password==''){
    echo "<script>alert('Error!') ;history.go(-1);</script>";
    exit;
}
$sql="select * from users where username=? and passcode=? ;";//The question mark indicates that a parameter is needed
$stmt=$con->prepare($sql);//pre-compile SQL statement
if(! $stmt){
    echo 'prepare execution error';
}
else{
    $stmt->bind_param("ss",$username,$password); //bind SQL parameters for precompile, ss means two strings
    //i--int d--double s--string b-- boolean
    $stmt->execute();
    $result=$stmt->get_result();
    $row=$result->fetch_row();
    if($row){
        session_start();
        $_SESSION['username']=$row[1];
        echo $row[1]. "<a href='welcome.php'>歡迎访问</a>";
    }else{
        echo "<script>alert('Login failed!!!') ;history.go(-1);</script>";
    }
    $stmt->close();
}
$con->close();
? >



一部のコンテンツは、コードのコメントでマークされています
パラメータ化されたPHPコードは、SQLインジェクションの防止に実に効果的です。

上記は、普遍的なパスワードのSQLインジェクションの脆弱性とそのPHP環境の構築と防御手段の詳細であり、普遍的なパスワードのPHP環境の構築と防御手段の詳細については、スクリプトハウスの他の関連記事に注意を払うください