web-dev-qa-db-ja.com

接続に失敗したときにPDOがパスワードを印刷するのはなぜですか?

PDOを使用してMysqlサーバーへの接続を確立する簡単なWebサイトがあります。

$dbh  =  new PDO('mysql:Host=localhost;dbname=DB;port=3306', 'USER', 
'SECRET',array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

サイトにトラフィックがあり、サーバーの接続制限に達しました。PLAINパスワードが含まれているため、Webサイトからこのエラーがスローされます。

致命的なエラー:/home/domain/html/index.php:xxxスタックトレース:#0 /home/domain/html/index.phpで、キャッチされない例外「PDOException」とメッセージ「SQLSTATE [08004] [1040]接続が多すぎます」 (64):PDO-> __construct( 'mysql:Host = loca ...'、 'USER'、 'SECRET'、Array)#1 {main}が64行目の/home/domain/html/index.phpにスローされます

皮肉なことに、セキュリティ上の理由からPDOに切り替えたので、これは本当にショックを受けました。この正確なエラーは、単純なhttpフラッディングを使用してほとんどのサイトで非常に簡単に引き起こす可能性があるためです。

接続をtry/catchブロックでラップしましたが、それでもこれは壊滅的だと思います。

私はPDOを初めて使用するので、私の質問は、安全であると見なすために何をしなければならないかということです。安全な方法で接続を確立するにはどうすればよいですか?私が知っておく必要があるこのような既知のセキュリティホールは他にありますか?

53
The Surrican

この問題を回避するには、とにかくPHP.iniに_display_errors = off_を含める必要があります。このような詳細を明らかにするエラーは、PDOに加えて、多くの場所から発生します。

はい、try/catchブロックにも含める必要があります。

$pdo->setAttribute(PDO::ERRMODE_SILENT)も可能ですが、try/catchブロックを使用するのではなく、手動でエラーコードを確認する必要があります。その他のエラー定数については、 http://php.net/manual/en/pdo.setattribute.php を参照してください。

21
Brad

PDOコンストラクターによってスローされたPDOExceptionをキャッチするための簡単な回避策:

try {
    $dbh  =  new PDO('mysql:Host=localhost;dbname=DB;port=3306', 'USER', 
    'SECRET',array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
} catch (PDOException $e) {
    throw new Exception($e->getMessage());
}
14
Matthias

さて、これで少しくすくす笑いました。エラーレポートの使用法はデバッグを目的としており、問題をすばやく見つけて修正することができます。

ライブ環境内にいる場合、サーバーは直接出力ではなく内部ログ専用に構成する必要があるため、基本的にはphp.ini内のエラーの出力をオフにする必要があります。

display_errors = Off

ただし、テスト環境内にいる間、このスタックは単に役立つツールであり、構成可能です。

ライブ環境内でエラーが発生するとログに記録されるため、常にログファイルを確認し、それに応じて修正する必要があります。

人々はあなたがあなたのPHPアプリケーション内でエラーを管理できると指定するかもしれませんが、個人的な好みによって、これはそれを行うための間違った方法だと思います、INIとWebサーバーとMySQL/MsSQLの構成ファイルを使用すると、より厳密な管理が可能になります。

アプリケーションがパブリックアプリケーションの場合、クライアントの大部分が共有ホスティング上にあり、サーバー構成へのフルアクセスがない可能性があるため、アプリケーション内のエラーを処理することもお勧めします。

7
RobertPitt

エンコードされたユーザー名とパスワードを使用し、PDOコンストラクターでそれらをデコードしてから、PDOExceptionをキャッチし、古い例外メッセージとともに新しいPDOExceptionをスローして、トレースにエンコードされたユーザー名とパスワードのみが表示されるようにします。

PHPの適切な暗号化ライブラリは次のとおりです:defuse/php-encryption

https://github.com/defuse/php-encryption

コード例:

<?php
class myPDOWrapper extends PDO
    {

        public function __construct(string $dns, string $encodedUser, string $encodedPassword)
        {
            try {
                parent::__construct($dns, $this->decodeFunction($encodedUser), $this->decodeFunction($encodedPassword),
                    [
                        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                    ]
                );
            }
            catch (PDOException $exception) {
                throw new PDOException($exception->getMessage());
            }
        }

        private function decodeFunction(string $encoded): string
        {
            return \Defuse\Crypto\Crypto::decrypt($encoded, $this->decodeKey());
        }

        private function decodeKey(): \Defuse\Crypto\Key
        {
            static $key = null; 

            if(null === $key) {
                $key = \Defuse\Crypto\Key::loadFromAsciiSafeString(getenv('MY_PDO_DECODE_KEY'));
            }

            return $key;
        }
    }
2