@tameme0128

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

PHPからJavascriptへの配列引き渡しについて

解決したいこと

HTMLで入力された内容を登録ボタンでデータベースに登録する際に、
既にその値が登録されているかをJavascriptで確認したいのですが、Javascriptが動かなくて困っています。

やりたい内容としては、PHPでデータベースの情報を取得し、配列に格納。
その配列をJavascriptに引き渡し、GetelementsbyNameで送信されたフォームの内容を取得して
配列の中にその値があるのかを確認したいです。

該当するソースコード

function checkinput() {
        let elements = document.getElementsByName('date');
        var date_array = <?php echo $date_array; ?>;
	if(date_array.indexOf(elements.value) != -1){ 
		flag = 1;
		error_msg= '!date is alrady registed!';
	}
 	if(flag == 1){
		window.alert(error_msg); // 警告ダイアログを表示
		return false; // 登録を中止
	}
	else{
		return true; // 登録を実行
	}
}

初心者なのですがわかりやすく教えていただけますと助かります。

1 likes

5Answer

大小色々な問題があるコードに見えますが、まず大前提として方針の筋が良くないです。データベースから引いた値を JavaScript に埋め込んで、それとフォームの値を比較するというやり方だと、以下のマズい状況が考えられます。

  1. A さんがフォームを表示する(この時点でページの JavaScript に値が埋め込まれる)
  2. B さんがフォームを表示し、未登録の値を送信する
  3. A さんが B さんと同じ値を送信しようとする

この状況では、 A さんのフォームで埋め込まれた値と比較するチェックは通りますが、サーバ側では B さんの値が登録済みになっていて登録が失敗してしまいます。

一般的に、フォームの値のチェックは以下のようなやり方があります。

  • JavaScript を使わないやり方:チェックなしでフォームを送信し、 PHP 側で値をチェックする。エラーがあればレスポンスで再度フォームのページを返す。このとき返す HTML において、入力済みの値をフォームに埋め込み、エラーメッセージもページに表示させておく。
  • JavaScript を使うやり方: PHP でフォームの値をチェックする API を提供する。 JavaScript で値を API に送り、 API からエラーを表すレスポンスが返ってきたらそれを表示する。
    • これもタイミング次第で、送信してみたら別の人が値を登録済みだったという可能性は残るので、 JavaScript を使わないやり方と組みわせるのが普通。
2Like

Comments

  1. @tameme0128

    Questioner

    回答ありがとうございました。
    ご提案いただいたJavaScript を使わないやり方で実装し、やりたいことができました
    大変助かりました。

  2. 解決したのであらば、本問をクローズしましょう。

質問者のソースコードはXSSを引き起こす可能性が十分にあります
万が一のことがあったら簡単に攻撃の踏み台にされます。

このような値をダイレクトに渡すやり方は愚行です。絶対に止めましょう。

既にいくつかの問題点を指摘されていますが、私からは直接的ソースコードの指摘はせず、値を渡す流れだけ簡潔に説明しておきます。

PHPの配列は一度JSONに直してから<script type="application/json">...</script>要素に張り付けます。

JS側でgetElementByIdにJSONを張り付けた要素のテキスト内容を取得してからデコード処理します。

<?php
$date_array = getdate();
?>
<script id="json_date_array" type="application/json"><?= json_encode($date_array, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_INVALID_UTF8_IGNORE) ?></script>
<script>
const date_array = JSON.parse(window.document.getElementById("json_date_array").text);
console.log(date_array);
</script>

これでもまだ気をつけるべき点はありますが、値をダイレクトに渡すよりかはマシです。
JSON.parseに対してtry & catchはしません。JSONに不正な値があればそこで停止して貰うのが良いでしょう。
APIを構築できるならそちらを検討した方が賢明です。
paizaでの実験コードも置いておきます。

PHPのjson_encodeについては公式ドキュメントをよく確認して下さい。
知らずにホイホイ使わないようにして下さい。これも一つ間違えばXSSを生み出します。

質問者さんのPHP側の$date_arrayに何が含まれているのか分かりませんが、基本的に外からきた値は常に信用しないことです。

2Like

Comments

  1. @tameme0128

    Questioner

    セキュリティのことを全然考えられていませんでした。
    ご指摘ありがとうございます。

HTMLで入力された内容を登録ボタンでデータベースに登録する際に、既にその値が登録されているかをJavascriptで確認したいのですが、Javascriptが動かなくて困っています。

何故動かないかは <?php echo $date_array; ?> で PHP が埋め込む html がどうなるのか、elements.value で取得できるユーザー入力がどういう形になるのかを書いてもらわないと分かりません。

ただ、そこの問題を解決できたとしても、他の回答者の方が指摘されているように、重複が避けられない場合があります。ユーザーが表示されたページを眺めて入力操作をしているうちに、他のユーザーが登録してしまって、それと重複するなど。

勉強のため自力で検証機能を実装しようとしているのでなければ、フレームワーク組み込みの検証機能がないかを探して、それを使うことを考えてはいかがですか?

システムとしてアプリ全体と整合を取って動く検証機能を、実用になるレベルで初心者が実装するのは無理がありますので。

例えば、ASP.NET Web アプリのフレームワークには RemoteAttribute という検証属性が用意されていて、サーバー側でなければユーザー入力の検証ができないケース(例えばデータベースに存在する名前との重複を確認するなど)で使ってクライアント側での検証が可能になる機能があります。同様なものが質問者さんの使っているフレームワークに無いか探してみてはいかがですか?

RemoteAttribute による検証
http://surferonwww.info/BlogEngine/post/2022/02/11/client-side-validation-using-remoteattribute.aspx

ただ、上記のように登録直前に検証を行っても、完全に重複を避けることはできません。検証した時点では重複してなくても、実際に登録を行うまでの時間に他のユーザーが重複するデータを登録することは可能なので。

100% 完全に重複を避けるには、DB のテーブルの当該列に Unique 制約を付与しておいて、サーバー側で登録処理を行う際に Unique 制約違反例外をキャッチし、エラーメッセージを表示するという方法ぐらいしかなさそうです。

具体的には、ASP.NET Web アプリの例ですが、以下の記事のようにして可能です。

EF6 で PK / Unique 制約違反例外をキャッチ
http://surferonwww.info/BlogEngine/post/2021/03/08/catch-sqlexception-for-primary-and-unique-key-violations-in-entity-framework-6.aspx

2Like

まず、確認なのですが javascriptが動かない というのは どのようなエラーが出ているのでしょうか?(本文に追記してください)

1Like

Comments

  1. @tameme0128

    Questioner

    さくらサーバーでコード書いているのですが、Javascriptのエラーがでなくて苦戦しておりました。
    ただ、本件解決しました。ありがとうございました。

そのほかの問題も指摘しておきます。

var date_array = <?php echo $date_array; ?>; と書いても JavaScript で使える形で配列を埋め込むことはできません。var date_array = <?php echo json_encode($date_array); ?>; としてください。($date_array の中身は JSON で表現できる値である必要があります。) →これだと XSS に対して脆弱なので @STSynthe さんの回答を参照してください。

date_array.indexOf(elements.value) != -1 で、 elements は配列的なもの(正確には NodeList コレクション)なので .value プロパティを持ちません。 elements[0].value とするか、 let element = document.getElementById('some-id') のように単一の要素を取得してください。複数の date 要素についてチェックする方法は省略します。

flag = 1; これはグローバル変数を定義してしまっています。 if 文の前で let flag; のようにしてローカル変数を用意すべきです。そもそも以下のように書けばフラグは不要です。

	if(date_array.indexOf(elements.value) != -1){ 
		let error_msg= '!date is alrady registed!';
		window.alert(error_msg); // 警告ダイアログを表示
		return false; // 登録を中止
	}
	else{
		return true; // 登録を実行
	}
0Like

Your answer might help someone💌