PHPとJavaScriptは、Web開発においてよく一緒に使われるプログラミング言語です。PHPはサーバーサイドでデータ処理を行い、その結果をJavaScriptに渡してクライアントサイドで利用することが多々あります。しかし、データの受け渡しには注意が必要です。受け渡し方によっては予期しないエラーや脆弱性につながる場合があります。この記事ではPHPで生成された値をJavaScriptで安全かつ確実に受け渡す方法を紹介します。
受け渡し方法の大まかな流れは次です。プリミティブな値や配列、データのみのオブジェクトは結構楽にやれます。リソース型や何がしかのインスタンスは頑張って JavaScript 上で過不足なく使える JSON を生成する所からスタートです。
- PHPでデータを生成し、JSON形式にエンコードする。
- HTMLのデータ属性(`data-*`)を使って、JSONデータを埋め込む。この時 htmlspecialchars でエラーや脆弱性を防ぐ
- JavaScriptでデータ属性からJSONデータを取得し、デコードしてオブジェクトに変換して利用する。
実装手順です。
まず、PHPでデータを生成し、JSON形式にエンコードします。
<?php
$raw = [
'key1' => '`1',
'key2' => '\\2',
'key3' => '\\\\3',
'key4' => '"4',
'key5' => '\'5',
'key6' => '<6',
'key7' => '>7',
];
$json = json_encode($raw);
?>
次にHTMLの属性を使ってJSON形式にエンコードしたを埋め込みます。ここではデータ属性(data-*)を使用しています。データ属性の他には meta タグの content 属性が HTML のセマンティクスを守りつつ使えておすすめです。
データ属性の使用 – ウェブ開発を学ぶ | MDN
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PHP data in JavaScript</title>
</head>
<body>
<!-- PHPで生成した値をデータ属性に埋め込む -->
<!-- htmlspecialcharsを使うことで不等号、クォーテーション混じりの文字列によるエラーやXSSを防ぐ -->
<div id="php-data" data-json='<?php echo htmlspecialchars($json, ENT_QUOTES, "UTF-8"); ?>' style="display:none;"></div>
<!-- Laravel組み込みのテンプレートエンジンであるBladeを使うなら次の様になる -->
<!-- <div id="php-data" data-json='{{ $json }}' style="display:none;"></div> -->
<!-- JavaScriptファイルを読み込む -->
<script src="script.js"></script>
</body>
</html>
最後に、JavaScriptでデータ属性からJSONデータを取得し、デコードしてオブジェクトに変換して利用します。
// script.js
document.addEventListener('DOMContentLoaded', function() {
// データ属性からJSONデータを取得
var jsonDataElement = document.getElementById('php-data');
var jsonData = jsonDataElement.getAttribute('data-json');
// JSONデータをデコードしてオブジェクトに変換
var data = JSON.parse(jsonData);
// 値を利用する
console.log(data);
// Object { key1: "`1", key2: "\\2", key3: "\\\\3", key4: '"4', key5: "'5", key6: "<6", key7: ">7" }
});
この様にして適切なエスケープを行い、PHPからJavaScriptにデータを渡せます。
以下に、JSONデータを正しくエスケープせずに`<script>`タグ内に直接埋め込むアンチパターンを紹介します。これはほとんどのデータで正常に動くのですがエスケープが必要なデータでは期待通りに動きません。適切なエスケープができていればいいのですが、この渡し方に即した htmlspecialchars の様なエスケープ用の関数が PHP に存在しないためエスケープは煩雑になりミスが生まれやすいです。もし適切なエスケープがなされなかった場合、'"`といった文字列リテラルで処理を区切ることによってJavaScriptの構文エラーが起こる可能性があり、区切り方と隙間にいれるコードに次第でXSSもされます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PHP data in JavaScript</title>
</head>
<body>
<!-- PHPで生成した値をHTML中に埋め込む(アンチパターン) -->
<script>
// そのまま JSON データを echo すると ' が含まれるデータでやりたい放題、壊れたい放題になります
var jsonData = '<?php echo $json; ?>';
// 文字列リテラルの区切り文字のみエスケープした場合、$raw = "\\';alert('1');'"; のようなデータで壊れます
var jsonData = '<?php echo str_replace("'", "\\'", $json); ?>';
// htmlspecialcharsを使った場合、& がそのまま読まれてしまい JSON として不適切なデータと扱われてしまいます
var jsonData = '<?php echo htmlspecialchars($json, ENT_QUOTES, "UTF-8"); ?>';
</script>
<!-- JavaScriptファイルを読み込む -->
<script src="script.js"></script>
</body>
</html>
この記事では、PHPで生成された値を安全かつ確実にJavaScriptに渡す方法を紹介しました。データ属性を利用してJSONデータを埋め込み、JavaScriptでデータ属性からデータを取得し、デコードしてオブジェクトに変換することで、簡単かつ安全にデータの受け渡しができます。