PHPで(グローバル変数含む)変数を使ったリンクを以下のように使った場合のセキュリティー対策が知りたいです。
以下のように対策前と対策後を自分なりに書いてみました。
正しい書き方が知りたいです。
よろしくお願いします。
【対策前】 <?php while($row = $stmt -> fetch(PDO::FETCH_ASSOC)) { ?> htmlタグ省略... <? echo "<a href='xxxxxxxxx.php?id=".$row['id']."'>".$row['title']."</a>";?> <?php } ?> 【対策後(失敗)】 <?php while($row = $stmt -> fetch(PDO::FETCH_ASSOC)) { ?> htmlタグ省略... <? $id=htmlspecialchars(filter_input(INPUT_row,'id')); echo "<a href='xxxxxxxxx.php?id={$id}'>".$row['title']."</a>";?> <?php } ?> 【再修正(追加分)】 <?php while($row = $stmt -> fetch(PDO::FETCH_ASSOC)) { ?> htmlタグ省略... <? htmlspecialchars($row['id']); echo "<a href='xxxxxxxxx.php?id={$id}'>".$row['title']."</a>";?> <?php } ?>
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2016/11/26 07:57
退会済みユーザー
2016/11/26 08:31
回答3件
0
明らかな間違いが
php
1 htmlspecialchars($row['id']); // なんのためにいれたの? 2echo "<a href='xxxxxxxxx.php?id={$id}'>".$row['title']."</a>";?> // $id ってどこからきたの?
ですね。
php
1$id = htmlspecialchars($row['id']); 2echo "<a href='xxxxxxxxx.php?id={$id}'>".$row['title']."</a>";?>
セキュリティの面で言えば、省略されている部分に重要な点がありますので、サンプルコードを元に説明します。
#変数にHTMLタグが代入されている点
php
1<? echo "<a href='xxxxxxxxx.php?id=".$row['id']."'>".$row['title']."</a>";?>
このコードは動作はしますが、好ましくないと感じます。
この出力部分、後になって a タグではなく、span に変更したいということになった場合、面倒ではありませんか?
PHPの変数にHTMLタグを代入すると、メンテナンス性が落ちます。
ですので、私は以下のように記述します。
php
1<a href='xxxxxxxxx.php?id="<?= $row['id']?>"><?= $row['title'] ?></a>
#HTMLに埋め込みしている <?php } ?>
よくあるコード例としては、以下のようなもの。
html
1<table> 2 <tr> 3 <?php while($row = $stmt -> fetch(PDO::FETCH_ASSOC)) { ?> 4 <td> </td> 5 <td> </td> 6 <?php } ?> 7 </tr> 8</table>
while だけならいいですが、このwhile の中に if の条件分岐などが入ることは稀ではありません。
そのとき、どちらの }
が、while の終了なのか if の終了なのかが判別しにくく、バグの元になりかねません.
while(): 〜 endwhile, if(): 〜 endif; , foreach () : 〜 endforeach; のように記述すると、みやすいコードになります。
html
1<table> 2 <tr> 3 <?php while($row = $stmt -> fetch(PDO::FETCH_ASSOC)) : ?> 4 <td> </td> 5 <td> </td> 6 <?php endwhile; ?> 7 </tr> 8</table>
#htmlspecialchars の使い方
htmlspecialchars は、第3引数まで指定し、本当に最後に出力するときだけ、利用しましょう。無用に二重エスケープしてしまうミスを防ぎます。
php
1htmlspecialchars($string, ENT_QUATES, 'utf-8');
#HTMLのなかにPHPのロジックを入れない
HTMLの中にデータベースの接続処理であったり、値の加工などがあると、後でバグを修正することが面倒です。
以下のような構造にして、HTMLとPHP部分を明確に分けるようにするとメンテナンスしやすくなります。
php
1<?php 2// PHPの処理 3?> 4<!DOCTYPE HTML> 5<html lang="ja"> 6 <head> 7 <meta charset="UTF-8"> 8 <title></title> 9 </head> 10 <body> 11 </body> 12</html>
#サンプルコード
コードを書く位置、書き方すべて意味があって以下のように書いています。「その意図はなんだろうか」と推測しながらコードを読んでみてください。その上でわからないことがあれば、コメントで聞いてください。
php
1<?php 2 3// デバッグ(開発)時は必ず記述する 4ini_set('display_errors', true); 5error_reporting(E_ALL); 6 7/** 8 * XSS対策 9 * @param string $string 10 * @return string 11 */ 12function h($string) 13{ 14 return htmlspecialchars($string, ENT_QUOTES, 'utf-8'); 15} 16 17try { 18 // DSNには必ず文字コードの指定をする 19 $dsn = 'mysql:host=localhost;dbname=test;charset=utf8;'; 20 $username = 'roor'; 21 $password = 'password'; 22 $pdo = new PDO($dsn, $username, $password); 23 24 // データベースのエラーを例外にする 25 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 26 27 // SQLの実行結果を連想配列で取り出す。 28 $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); 29 30 // プリペアドステートメントを宣言する 31 $sql = 'SELECT id, name FROM sample WHERE id = :id'; 32 $stmt = $pdo->prepare($sql); 33 34 // $_GET['id'] で取得しない 35 $id = filter_input(INPUT_GET, 'id'); 36 37 // 配列の初期化 38 $arr = []; 39 40 // パラメータをセットする 41 $arr[':id'] = $id; 42 43 // SQL 実行 44 $stmt->execute($arr); 45 46 // すべてのレコードを返す 47 $rows = $stmt->fetchAll(); 48} catch (Exception $e) { 49 $err = $e; 50} 51?> 52<!DOCTYPE HTML> 53<html lang="ja"> 54 <head> 55 <meta charset="UTF-8"> 56 <title></title> 57 </head> 58 <body> 59 <div> 60 <?php if (isset($err)) : ?> 61 <p><?= h($err->getMessage()); ?></p> 62 <?php endif; ?> 63 64 <?php if (0 < count($rows)): ?> 65 <table> 66 <?php foreach ($rows as $row) : ?> 67 <tr> 68 <td><?= h($row['id']); ?></td> 69 <td><?= h($row['name']); ?></td> 70 </tr> 71 <?php endforeach; ?> 72 </table> 73 <?php endif; ?> 74 </div> 75 </body> 76</html>
#追記
①// 配列の初期化 $arr = []; // パラメータをセットする $arr[':id'] = $id;
です。そもそもこういう処理自体初見に近いです。
この記述なら、みたことあるのではないでしょうか?
php
1$arr = array(); 2$arr[':id'] = $id;
php
1$arr = array();
と
php
1$arr = [];
は同じことを行なっています。 []
はPHP5.4以上で可能になった書き方です。
②html内の出力もすごいですね。ifで0以上なら(if(!empty)出力もあり?)…foreachでぶん回してhtml...(←特殊をhtmlに変換でしたっけ‥?)で出力でしょうか??
(他にも細かいこと聞いたらきりがなさそうです。)
この if
の条件式がないと、
html
1<table> 2</table>
という、HTMLとして不十分な出力になってしまいますよね。
画像を出力するときも
そうです。基本的に、PHPの「変数」を echo 及び print で出力するときにはすべて htmlspecialchars 関数を通します。
投稿2016/11/26 09:12
編集2016/11/26 12:03退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2016/11/26 09:58
退会済みユーザー
2016/11/26 11:28
退会済みユーザー
2016/11/26 11:50
退会済みユーザー
2016/11/27 08:25
退会済みユーザー
2016/11/27 09:06 編集
退会済みユーザー
2016/11/27 09:13
退会済みユーザー
2016/11/27 10:03 編集
退会済みユーザー
2016/11/28 10:32
退会済みユーザー
2016/11/28 10:38
0
idの方はクエリ文字列の中に入るので、パーセントエンコード(URLエンコード)も必要です。
そうしないと、idが 1&a=1 のような値の場合、&a=1 以降が別のパラメータ(a=1)と認識されてしまいます。
このため、まず urlencode 関数等でパーセントエンコードし、その結果を htmlspecialchars でHTMLエスケープすべきです。該当箇所のみ最小限のコード例を示します。
PHP
1$idx = htmlspecialchars(urlencode($row['id']), ENT_QUOTES, 'UTF-8'); 2echo "<a href='xxxxxxxxx.php?id={$idx}'>". htmlspecialchars($row['title'], ENT_QUOTES, 'UTF-8') ."</a>";?>
投稿2016/11/26 13:20
総合スコア11701
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
idだけではなく、titleもエスケープすべきだと思いますよ
php
1<?php 2function h($str { 3 return htmlspecialchars($str, ENT_QUOTES, 'UTF-8') 4} 5?> 6<?php while($row = $stmt -> fetch(PDO::FETCH_ASSOC)): ?> 7 <a href='xxxxxxxxx.php?id=<?= h($row['id']) ?>'><?= h($row['title']) ?></a> 8<?php endwhile; ?>
※DBから取得した値や外部から取得した入力値などをHTMLに出力する場合は、原則すべてHTMLエスケープしたほうがいいと思います。
※htmlspecialchars()は長いので、h()関数を定義しておくのが普通です(Cakephpなどでもそうなっています)
※h()でエスケープした値は変数に入れると別の処理で間違って使ってしまう可能性もあるので、出力時に直接書く方がいいと思います。
投稿2016/11/26 11:09
総合スコア6586
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2016/11/26 11:17
2016/11/26 11:32
退会済みユーザー
2016/11/26 11:38
2016/11/26 11:44
退会済みユーザー
2016/11/26 11:48
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。