質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PDO

PDO(PHP Data Objects)はPHPのデータベース抽象化レイヤーです。

セキュリティー

このタグは、コンピューターシステムの安全性やデータの機密性に関連したトピックの為に使われます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

3回答

6659閲覧

【PHP】簡易掲示板の作成①

tnk_fuku

総合スコア42

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PDO

PDO(PHP Data Objects)はPHPのデータベース抽象化レイヤーです。

セキュリティー

このタグは、コンピューターシステムの安全性やデータの機密性に関連したトピックの為に使われます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

2クリップ

投稿2017/04/19 04:18

編集2017/04/29 10:57

PHP初心者です。

PHP,MySQLにて簡易掲示板を作成しています。

ファイルは
index.php(投稿一覧表示、新規投稿フォーム)
confirm.php(投稿内容確認画面)
complete.php(書き込み完了画面)
(DB接続、エスケープ処理はそれぞれ外部ファイル(db_info.php,encode.php)を作成)
という構成です。

現在の問題としてはcomplete.phpにて「書き込みが完了しました」と表示されますが、index.phpに戻ると投稿が表示されないことです。

ソースは下記です。

index.php

<?php require_once('db_info.php'); require_once('encode.php'); try { $pdo = new PDO($dsn, $user, $pass); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOExeption $e){ $error = 'Unable to connect to the database server: ' . $e->getMessage(); include 'error.php'; exit(); } try { $sql = "select * from bbsdata order by day desc"; $result = $pdo->query($sql); } catch (PDOExeption $e) { $error = 'Error fetching threads:' . $e->getMessage(); include 'error.php'; exit(); } echo <<<eot1 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>簡易掲示板</title> <style type="text/css"> body { background-color: #E1EDFF; line-height: 1.5rem; } </style> </head> <body> <h1>簡易掲示板</h1> <form action="confirm.php" method="post"> 名前:<input type="text" name="name"><br> タイトル:<input type="text" name="title"><br> メッセージ:<br> <textarea name="msg" rows="10" cols="30"></textarea><br> <input type="submit" value="確認画面へ"> </form> <hr> eot1; $i = 1; foreach($result as $row) { $s = "<p>($i)タイトル: <strong>%s</strong><br>投稿者: %s 投稿日 %s<br>内容:<br>%s</p>"; printf ($s,h($row['title']), h($row['name']), h($row['day']), h($row['msg'])); $i++; } echo <<<eot2 </body> </html> eot2;

confirm.php

<?php session_start(); $token = sha1(uniqid(mt_rand(), true)); $_SESSION['token'] = $token; require_once('db_info.php'); require_once('encode.php'); try { $pdo = new PDO($dsn, $user, $pass); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOExeption $e){ $error = 'Unable to connect to the database server: ' . $e->getMessage(); include 'error.php'; exit(); } ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>確認画面</title> </head> <body> <h1>確認画面</h1> タイトル: <?php print h($_POST['title']); ?> 投稿者: <?php print h($_POST['name']); ?><br> 内容:<?php print h($_POST['msg']); ?> <form action="complete.php" method="post"> <input type="hidden" name="name" value="<?php echo $_POST['name'];?>"> <input type="hidden" name="title" value="<?php echo $_POST['title'];?>"> <input type="hidden" name="msg" value="<?php echo $_POST['msg'];?>"> <input type="hidden" name="token" value="<?php $token ?>"> <input type="submit" value="送信する" name="regist"> </form> </body> </html>

complete.php

<?php session_start(); if (isset($_POST['regist'], $token, $_POST['token']) && $token === $_POST['token']) { unset($_SESSION['token']); require_once('db_info.php'); try { $pdo = new PDO($dsn, $user, $pass); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOExeption $e){ $error = 'Unable to connect to the database server: ' . $e->getMessage(); include 'error.php'; exit(); } //書き込み try { $sql = "insert into bbsdata (name, day, title, msg) values (:name, now(), :title, :msg)"; $s = $pdo->prepare($sql); $s->bindValue(':name', $_POST['name']); $s->bindValue(':title', $_POST['title']); $s->bindValue(':msg', $_POST['msg']); $s->execute(); } catch(PDOExeption $e) { $error = 'Error writing message: ' . $e->getMessage(); include 'error.php'; exit(); } header("Location: http://{$_SERVER['HTTP_HOST']}/practice/bbs/complete.php/", true, 303); } else { echo <<<eot1 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>完了画面</title> </head> <body> <h1>書き込みが完了しました</h1> <a href="http://{$_SERVER['HTTP_HOST']}/practice/bbs/index.php">トップページへ戻る</a> </body> </html> eot1; }

complete.phpはリロードでの二重送信防止目的にPRGパターンを使っているため、ブラウザに表示された時にはGETになるべきだと思うのですが、POSTとして表示されます。
したがってif (isset($_POST['regist'], $token, $_POST['token']) && $token === $_POST['token'])で弾かれてしまい、DBにinsertすることなく「書き込みが完了しました」と表示されているのではと思います。

また、私のcomplete.phpの設定では不正な値、値が空といった場合でも「書き込みが完了しました」と表示されてしまうのではと思います。

下記の点についてご回答お願い致します。

  • 投稿をinsertするためにはどうしたいいか
  • 不正な値・値が空の場合の処理をどうすべきか
  • セキュリティの方向性が現在の設定(エスケープ処理、PDO、トークンの使用)で間違いはないか
  • 二重送信への対策はこれで十分か(リロード、戻るボタン使用時など)
  • 現在の処理で不要な箇所などはないか

2017.4.29追記
構成など少し変更しとりあえず自分の思っていた形に実装できました。
フィードバックを頂きたく思い、ここでは記述が長くなり見にくくなるため新たにこちらに投稿させて頂きました。
アドバイス頂けると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

トークン

$token = sha1(uniqid(mt_rand(), true));

現在、mt_rand() をトークンに使用するのはあまり好ましくないようです。
PHPのバージョンや環境によりますが、PHP5.3以降ならopenssl_random_pseudo_bytesで、PHP7.0以降ならrandom_bytesを使うのがよいかと思います。
詳細は下記をどうぞ。

値の受け取り方

確認画面で入力された値が、空やNULLではないか確認してエラーを表示する方がよいかと思います。
issetfilter_input などで確認できます。
詳細は下記をどうぞ。

PHP7からは NULL合体演算子 ?? と呼ばれるものが導入され、もっとシンプルに値を受け取れます。
詳細は下記をどうぞ。

投稿2017/04/19 04:38

編集2017/05/11 05:51
7968

総合スコア253

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tnk_fuku

2017/04/21 19:45

コメントが遅くなりすみません! PHP7を使用していますのでアドバイス通りrandom_bytesに変更しました。 セキュリティをしっかり身につけたいと思っていますので非常に助かりました。 ttyp03さんのご回答に記載している対応でとりあえず投稿は出来るようになったのでこれから値のチェックや構成など変更してみてご報告します!
tnk_fuku

2017/04/29 15:04

今回アドバイス頂いた点を自分なりに考慮してまとめたものを再度別ページで質問させて頂きました。この質問の末尾に追記でリンクを張っています。 厳しい評価を頂いていますが、お時間がある時にご覧頂ければと思います。
guest

0

ベストアンサー

ざっとしか見てませんが、complete.phpでいきなり$tokenを参照しているのが問題では。
セッションから取り出す必要があるのではないでしょうか。

PHP

1<?php 2session_start(); 3$token = $_SESSION['token']; // ←追加 4if (isset($_POST['regist'], $token, $_POST['token']) && $token === $_POST['token']) { 5

投稿2017/04/19 04:29

編集2017/04/19 06:01
ttyp03

総合スコア17000

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tnk_fuku

2017/04/19 21:30

ご指摘の通り $token = $_SESSION['token']; を追加しましたが変化がみられません。 なぜでしょうか・・・。
ttyp03

2017/04/19 23:31

confirm.phpでtokenのところも違うような(小出しですみません) <input type="hidden" name="token" value="<?php $token ?>"> ↓ <input type="hidden" name="token" value="<?php echo $token; ?>">
tnk_fuku

2017/04/21 01:08

echoを記載することで無事に投稿できました。 ありがとうございました! しかし今度は $token = $_SESSION['token']; としたことで unset($_SESSION['token']); でセッションを破棄したものがリダイレクト後にも読み込まれるため 「書き込みが完了しました」の表示と共に Notice: Undefined index: token と警告表示されました。 したがってcomplete.phpでは $token = $_SESSION['token'] とせずに $_SESSION['token']のままissetすることで解決しました。 投稿がinsertできる形になりましたので、これから他の回答者の方のアドバイスに取り組んでいきます。 また助け船を出していただけるとありがたいです。 ありがとうございました!
guest

0

confirmはhiddenでデータの受けわたしは容易に改ざんされますのでNGです
(やってもいいですが確認画面だす意味がなくなるので)
セッションでやってみてください

completeはissetで$tokenをチェックしていますが
$_SESSION["token"]ですね
フロー上$tokenはセットされないので常にelseに流れているように見えます

条件でエラーだと完了画面をだすというフローが間違っていると思います
confirmからconfimにデータ送信、完了ならcompleteに飛ばすなどの方が良いのでは?
(completeは完了を知らせるだけのページとする)

投稿2017/04/19 04:37

編集2017/04/19 04:39
yambejp

総合スコア116443

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tnk_fuku

2017/04/21 19:50

返信が遅くなりすみません! $_SESSION["token"]でとりあえず投稿は出来るようになったのでいま構成の見直しをしています。 ちょっと躓いている部分があるのでまた進捗状況をこちらで報告させて頂きます!
tnk_fuku

2017/04/22 22:28

>hiddenでデータの受け渡しは容易に改ざんされますのでNGです トークンをhiddenでpostするのがいけないということでしょうか。 その場合、SESSIONで生成されたトークンとPOSTされたトークンの照合はどのようにしたらよいのでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問