そのPHP環境の普遍的なパスワードのSQLインジェクションの脆弱性と防御手段
汎用パスワードの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環境の構築と防御手段の詳細については、スクリプトハウスの他の関連記事に注意を払うください
関連
-
SQL Server 2019 データベースバックアップ&リストアスクリプト(一括バックアップ)
-
SQLにおける3つの重複排除手法の概要
-
SQLインジェクションとその防止、マイベイトの基本的な役割について
-
あるユーザーの連続ログイン日数を求めるSQLクエリ
-
DataGrip Formatting SQLの実装(カスタムSqlフォーマット)
-
MySQLスレーブ遅延1列外部キーチェックとセルフインクリメントロック
-
SQLの書き方--行ごとの比較
-
NavicatはSQL Serverのデータに接続します。エラー08001に対する完璧な解決策 - Named Pipeline Provider
-
SQL文におけるJOINの利用シーンの分析
-
SQLServerにおけるJSONドキュメント型データのクエリ問題を解決する。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
SQL SERVERのストアドプロシージャを使用した履歴データの移行について
-
SQL Server 2017がサーバーに接続できない問題解決
-
SQLSERVER 変数文字列を用いたスプライシング ケース詳細
-
SqlServerデータベースリモート接続ケースチュートリアル
-
SQL Server のジョブが失敗しました。所有者がサーバーアクセス権を持っているかどうか判断できない
-
SQLにアイドルCPU条件が定義されていないため、OnIdleジョブプランが機能しない
-
SQLステートメントにおけるNULL値の扱い方
-
SQL クエリ結果カラムのカンマ区切り文字列へのステッチング法
-
sql serverで最初の1000行のデータを削除する方法の例
-
データベース毎日練習問題、毎日少しづつ進歩(1)