web-dev-qa-db-ja.com

安全なAJAXリクエストをPHPおよびjQueryで送信する方法

問題

そのため、しばらくの間、さまざまなAJAXを使用して、MySQLデータベース内で処理および保存されるデータをサーバーに送信する方法を試してきました。

AJAXリクエストがapi.phpにヒットする]ページは、PHPのPDO準備済みステートメントを使用してデータを保存するため、MySQLインジェクションは実際には問題ではなく、パスワードまたはデータは暗号化もapi.phpによって処理されますが、これは私がここで尋ねているものではありません。私の質問は、クライアントからサーバーに転送されるときにデータを安全にする方法に関するものです。

アプローチ

私は現在持っています(以下に含まれるログイン例のために):

  • ドメインで実行されているSSL Cert/HTTPS。
  • 特定のAJAXリクエスト(明らかに、このログインリクエストの例ではセッションが開始されないため)は、PHPセッションがサイト全体で有効な場合にのみ機能します(この例ではlogin.phpapi.phpの両方で使用されています)。
  • 関数にアクセスするときのapi.phpのレート制限。
  • PHP PDOは、api.php内のデータベースと対話するときにステートメントを準備しました。
  • api.php内の機密データを暗号化します(質問には関係ありません)。

質問

最後に、私の質問は次のとおりです。

  1. 非同期HTTP(Ajax)リクエストを使用するこのアプローチは、単にデータをPHPページに送信して先にリダイレクトするのではなく、使用するのに十分安全ですか?(この方法により、ユーザーエクスペリエンスが向上します)。
  2. ユーザーの送信しているデータが改ざんされていないことを確認するにはどうすればよいですか?
  3. 自分のユーザーのデータを保護するために十分なことをしているか、もしそうでなければ、他に何ができるか

サイトのデータを処理し、そのデータを転送するには、誰もが異なるアプローチを持っていることを理解しています。また、あなたが何をしようとも、100%保護されることはありえないことを理解しています。システムに対処できない脆弱性や方法があるかもしれないからです。これは単なる例であるため、以下の特定のコードに対する批判ではなく、データを安全に送信するための一般的なアプローチに関するフィードバック/改善を探しています。しかし、建設的な答えは大歓迎です。読んで/答えてくれてありがとう。

function loginUser() {
    var process = "loginUser";
    var data = $("form").serializeArray();
    data[1].value = SHA512(data[1].value); // sha then encrypt on api.php page 
    data = JSON.stringify(data);

    $("#loginButton").html('<i class="fa fa-spinner fa-Pulse fa-lg fa-fw"></i> Login');
    $.ajax({
        type: "POST",
        url: "api.php",
        data: {"process": process, "data": data},
        success: function(data) {
            if (data.response.state == "success") {
                // if api.php returns success, redirect to homepage
            } else {
                // if api.php returns failure, display error
            }  
        },
        error: function(jqXHR, textStatus, errorThrown, data) {
            // error handling
        },
        dataType: "json"
    });
}
26
Luke Brown

1。 Originヘッダーを確認します

OWASPで指定 のように、これでは十分ではありませんが推奨されます:

自分のブラウザからヘッダーをスプーフィングすることは簡単ですが、一般的に、XSS脆弱性を介さない限り、CSRF攻撃でそれを行うことは不可能です。だからこそ、ヘッダーをチェックすることはCSRF防御の妥当な最初のステップですが、ヘッダーが常に存在するとは限らないので、通常、それ自体では十分な防御とは見なされません。

そして Mozillaによる

Originヘッダーは、JSONデータの盗難やCSRF攻撃に対して役立つと考えられています。 Originが提供する情報-ちょっとしたコンテキストのリクエスト作成情報-はリクエストの信頼性についてウェブサーバーにヒントを提供する必要があります[...]

HTTP_Originヘッダーは次のように記述できます。

header('Content-Type: application/json');

if (isset($_SERVER['HTTP_Origin'])) {
    $address = 'http://' . $_SERVER['SERVER_NAME'];
    if (strpos($address, $_SERVER['HTTP_Origin']) !== 0) {
        exit(json_encode([
            'error' => 'Invalid Origin header: ' . $_SERVER['HTTP_Origin']
        ]));
    }
} else {
    exit(json_encode(['error' => 'No Origin header']));
}

1。 (bis)REFERERヘッダーを確認します

もう一度 OWASPから

Originヘッダーが存在しない場合、Refererヘッダーのホスト名がサイトのOriginと一致することを確認します。リファラーの確認は、ユーザーごとの状態を必要としないため、組み込みネットワークデバイスでCSRFを防止する一般的に使用される方法です。このCSRF軽減方法は、認証されていない要求でも一般的に使用されます

HTTP_REFERERもPHP with $_SERVER['HTTP_REFERER']、上記のコードを更新できます。


BE CAREFUL常に具体的に確認する必要があるチェック:example.comまたはapi.example.comただし、完全なhttps://example.com。どうして ? api.example.com.hacker.comのようなOriginを使用してこのチェックを偽装する可能性があるためです。


2。 CSRFトークンの生成

十分に説明された PHPが与えられている)に固有の答え 、要するに:

  1. トークンを生成します。

    session_start();
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    
  2. メタ(Githubなど)を介して生成されたビューに追加します。

    <meta name="csrf-token" content="<?= $_SESSION['csrf_token'] ?>">
    
  3. このトークンを含めるようにjQuery ajax呼び出しをセットアップします。

    $.ajaxSetup({
        headers : {
            'CsrfToken': $('meta[name="csrf-token"]').attr('content')
        }
    });
    
  4. サーバー側でAJAXリクエストを確認してください:

    session_start();
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    
    header('Content-Type: application/json');
    
    $headers = Apache_request_headers();
    if (isset($headers['CsrfToken'])) {
        if ($headers['CsrfToken'] !== $_SESSION['csrf_token']) {
            exit(json_encode(['error' => 'Wrong CSRF token.']));
        }
    } else {
        exit(json_encode(['error' => 'No CSRF token.']));
    }
    

ほとんどのPHPフレームワークには独自のCSRF実装があり、これは多かれ少なかれ同じ原則に基づいています。


3。 消毒する ユーザー入力を検証します。

あなたはいつも フィルタ espace入力 および それらを検証


4。サーバーを保護する


5。ユーザー入力を信頼しない

@ blue112が言ったように、それは最も基本的なものの1つです セキュリティ原則

enter image description here

54
Ivan Gabriele

簡単な答え:クライアント側を保護することはできません。

長い答え:

  • AJAXを使用すると、たとえばフォームでデータを投稿するのと同じくらい安全です。
  • HTTPSを使用すると、中間者がユーザーデータを見ることができないため、ユーザーが送信する実際のデータは安全です。

クライアント側で実行されるのは実際にあなたのJavaScriptコードであることをブラウザに証明させるために何もすることはできません。次に、実行する明らかなアクションは最も単純なアクションです。NEVER TRUST USER INPUT。

これは、あなたが始めたときに、セッション、レート制限、データ検証、fail2ban(一定数の失敗後にクライアントIPを禁止する)、ログ監視を使用してサーバー側を保護することを意味します...

1
blue112

最善の方法は、サーバー側のセキュリティ保護を行うことです。サイトにログインしているユーザーであれば、メタタグのcsrfトークンを確認して、サーバーにスクリプトを偽造することもできます。確実な賭けはサーバー側の検証です