2

複数の開発者が作業し、定期的な更新とメンテナンスを受けるプロジェクトを考えると、読みやすさとセキュリティを考慮して、以下の 2 つのコードのどれが PHP のベスト プラクティスと見なすことができますか? パフォーマンスで言えば、おそらく2番目のオプションの方が少し良いでしょうが、この点を解決する方法があります。

オプション1

$user = $_POST['user'];
$pass = $_POST['pass'];

// Prevent SQL Injection and XSS
$user = anti_injection($user);
$pass = anti_injection($pass);

if(strlen($user) <= 8 && strlen($pass) <= 12)
{
    // SQL query
    $sql = "SELECT id 
            FROM users 
            WHERE username = '$user' AND password = '$pass';";
}

オプション 2

// Retrieve POST variables and prevent SQL Injection and XSS
$_POST['user'] = anti_injection($_POST['user']);
$_POST['pass'] = anti_injection($_POST['pass']);

if(strlen($_POST['user']) <= 8 && strlen($_POST['pass']) <= 12)
{
    // SQL query
    $sql = "SELECT id 
            FROM users 
            WHERE username = '" . $_POST['user']. "' AND password = '" . $_POST['pass'] . "';";
}

編集1

MySQL は使用していません。データベースは PostgreSQL です。

4

5 に答える 5

7

どちらもしないでください。

私が推測できるanti_injectionのは、Bad Idea™ である何らかのカスタム フィルタリング機能であるとしか考えられません。これらのアイデアのいずれかを本当に採用したい場合は、 を使用する必要がありますmysql_real_escape_string

SQL クエリを作成するときに安全性を維持する唯一の方法は、MySQLi などを介してパラメーターを使用することです。いずれにせよ、mysql_*関数は非推奨になりつつあるため、できるだけ早く移行することをお勧めします。

実際mysql_real_escape_string、インジェクション攻撃に対する完全な防御ではありません。などのクエリでの整数比較を検討してくださいWHERE $var > 301=1 or 100正常に挿入でき$var、ロジックを完全に破ることができました。

パラメータはクエリ言語からデータを完全に分離し、インジェクションのリスクを完全に軽減します。サーバーは、パラメーター表記と挿入する値のセットを含むクエリを受け取るため、クエリ言語とデータをまったく異なる方法で処理できます。

さらに、パスワードを平文で保存しているようです。これは 悪い 考えです。bcrypt などの強力なパスワード ストレージ ハッシュ アルゴリズムを検討する必要があります。これにより、ハッシュから平文のパスワードを取得することが非常に困難になります。

MD5 と SHA1 は高速になるように設計されているため、パスワードの保存には理想的ではありません。最新の GPU は、1 秒あたり 50 億の MD5 ハッシュを達成できます。実際、専用のハッシュ クラッキング リグを持っている人がいて、その中には1 秒あたり 450 億ハッシュで MD5 をクラックできる人もいます。

また、SQL インジェクション攻撃、パスワードの保存、およびその他の多数のセキュリティ問題を完全にカバーする、これらのすばらしい質問もご覧ください。

更新: postgres を使用していると述べました。こちら で簡単に説明されているように、 PDOを使用して PHP からパラメーター化されたクエリを実行できます。

于 2012-07-13T14:51:53.993 に答える
3

これらのオプションはどちらもベスト プラクティスanti_injectionはありません。次の事項もあります。

  • 入力データを検証する前に変更する
  • 平文パスワードの保存
  • バインドされたパラメーターを使用する代わりに、手動で SQL クエリを作成する

プロジェクトの範囲によっては、最後のものが許容される場合があります。

パフォーマンスに関しては、最初のオプションは配列のインデックス作成が少ないため、理論的には高速になります。しかし、違いは間違いなく非常に小さいため、まったく観察できません。最初のオプションは、より読みやすく、より優れた抽象化を提供します。余分なメモリ量はごくわずかです。

于 2012-07-13T14:49:49.923 に答える
2

どちらも間違っています。パスワードを平文で保存しているように見えますが、これはトラブルを求めているだけです。

また、ユーザー名が の場合、にエスケープして「長さ <= 8」チェックに失敗する"123456"ため、ログインできません。\"123456\"

于 2012-07-13T14:49:54.480 に答える
1

セキュリティとパフォーマンスが必要な場合は、PDO を使用することをお勧めします。パラメータを使用している場合、SQL インジェクションはできません。以下は例です。以下を機能させるには、mysql パラメータを「定義」する必要があります。

これにより、データベースがクエリをキャッシュすることもできます。これは、異なるパラメーターを使用して実行するたびにクエリが変更されるわけではないため、パフォーマンスも向上します。

:p_user and :p_pass

パラメータを示すために使用され、

array ( ':p_user' => $user, ':p_pass' => $pass ' )

パラメータを、渡す必要のある値に設定します。

また、パスワード ソルトを追加し、その sha1 をデータベースに格納することも検討する必要があります。これにより、データベースが侵害された場合に、パスワードがハッカーに明確に明らかにされなくなります。

class users{ 
  function __construct() {
    define('MySQLHost', 'localhost');
    define('MySQLName', 'databasename');
    define('MySQLUser', 'username');
    define('MySQLPass', 'password');
    define('pwsalt', 'tScgpBOoRL7A48TzpBGUgpKINc69B4Ylpvc5Xc6k'); //random characters
  }

  static function GetQuery($query, $params)
  {
    $db = new PDO('pgsql:host='.MySQLHost.';dbname='.MySQLName.';', MySQLUser, MySQLPass);
    $cmd = $db->prepare($query);
    $cmd->execute($params);

    if($cmd->rowCount()==0)
      return null;
    return $cmd->fetchAll();
  }

  static function GetUser($user, $pass)
  {
    $query = "select id
      from `users`
      where username = :p_user and password = :p_pass";

    $rows = users::GetQuery($query, array(
      ':p_user' => $user,
      ':p_pass' => sha1($pass.pwsalt) //Append the salt to the password, hash it
    ));

    return $rows;
  }
}

$user = $_POST['user'];
$pass = $_POST['pass'];
if( strlen($user) <= 8 && strlen($pass) <= 12 )
{
  $result = users::GetUser($user, $pass);
  if($result != null)
    print 'Login Found';
}
于 2012-07-13T14:51:09.533 に答える
0

私は他のポスターに同意しますが、SQLセキュリティに関して「車輪の再発明」を試みないでください。問題はパフォーマンスとスーパーグローバルの使用方法に関するもののようです。

ベストプラクティスとして、スーパーグローバル($ _GET、$ _ POST、$ _ REQUEST、$ _ SYSTEMなど)を変更しないでください。このルールに違反するとパフォーマンスが向上し、変更を加えると不確実性が生じ、将来の混乱。

したがって、この場合、どちらのオプションも正しくありません。Option1は変数を不必要にコピーします(Googleパフォーマンスドキュメントによるとno-no)。オプション2はスーパーグローバルを変更しますが、これは上記の格言に違反します。代わりに、次のようなことを行います。

$user = anti_injection( $_POST['user'] );
$pass = anti_injection( $_POST['pass'] );

if( strlen($user) <= 8 && strlen($pass) <= 12 ) ...

しかし、「自家製の衛生」は恐ろしい見通しであることを繰り返し述べておきますが、他のコメント提供者はこの点について非常に徹底的に詳しく説明しています。

于 2012-07-13T16:11:14.603 に答える