安全なプリペアードクエリ

PDO を使ってデータベース操作を行うときの、プリペアードクエリに少し躓いたのでまとめておこうと思います。

参考サイト:

PDO (PHP Data Object) とは

PHP の中で DB に接続し、操作を行うためのインターフェースです。DB の種類ごとに異なる関数を使う必要がなく、使用する DB の種類を変更する際にも対応しやすいという利点があります。

プリペアードクエリ とは

PDO が提供する、DB へ送る SQL 文を2段階に分けて実行する手法です。1段階目では、 SQL 文を 解析・コンパイル・最適化 し、2段階目で実行します。また、1段階目でパラメータをプレースホルダに置き換え、2段階目でそのプレースホルダにパラメータを渡すことが可能です。

プリペアードクエリを使うメリットは次の通りです。

  • SQL 文の解析~最適化は最初の1回だけ行えばよく、その SQL を何回も行うときに高速な動作が期待できる。
  • SQL 文の構成に入力値を使うときSQL インジェクションの危険があるが、適切にプレースホルダを用いることで容易にインジェクション対策ができる。

今回は、2つ目のインジェクション対策に焦点を当てます。

実際に使ってみる

今回はテキストボックスへの入力を受けて、下のテーブルから値を取り出し名前を表示するようにします。「きゅうり」を受け取ったら「きゅうり」を表示するという単純なものです。

+----------+-------+
| name     | price |
+----------+-------+
| キャベツ |   200 |
| きゅうり |    80 |
| かぼちゃ |   300 |
+----------+-------+

まず、プリペアードクエリを使わない方法でやってみます。コードは以下の通りです。

$sql = 'SELECT name FROM food WHERE name="'.$_POST['food'].'"';
    $foods = $db->query($sql)->fetchALL(PDO::FETCH_ASSOC);
    foreach ($foods as $food) {
        foreach ($food as $name) {
            print $name;
        }
    }

入力を行った結果を見てみます。

入力:「きゅうり」

入力:「きゅうり” or name=”キャベツ」

インジェクション対策を行ってないので、意図的に SQL 文を書き換えられています。

次に、プリペアードクエリを使いますが、入力値をプレースホルダで置き換えずにやってみます。コードは以下の通りです。

$sql = 'SELECT name FROM food WHERE name="'.$_POST['food'].'"';
    $stmt = $db->prepare($sql);
    $stmt->execute();
    $foods = $stmt->fetchALL(PDO::FETCH_ASSOC);
    foreach ($foods as $food) {
        foreach ($food as $name) {
            print $name;
        }
    }

結果を見てみます。

入力:「きゅうり」

入力:「きゅうり” or name=”キャベツ」

プリペアードクエリを使ったのにインジェクションされています。

それでは、プレースホルダを使ったプリペアードクエリを試してみましょう。コードは以下です。

$sql = 'SELECT name FROM food WHERE name=?';
    $stmt = $db->prepare($sql);
    $stmt->execute(array($_POST['food']));
    $foods = $stmt->fetchALL(PDO::FETCH_ASSOC);
    foreach ($foods as $food) {
        foreach ($food as $name) {
            print $name;
        }
    }

結果を見てみます。

入力:「きゅうり」

入力:「きゅうり” or name=”キャベツ」

ここだけ $foods を var_dump() してます。

インジェクション を防げました!

まとめ

今回は、

  • プリペアードクエリを使わない通常クエリ
  • プレースホルダを用いないプリペアードクエリ
  • プレースホルダを用いたプリペアードクエリ

の3通りについて試し、プレースホルダを用いたプリペアードクエリのみがインジェクションを防ぐことができました。prepare() でクエリを解析した後にプレースホルダに値を渡すので、不適切な入力を防いでくれています。

なので、入力値を用いて DB を操作する際には、プレースホルダを用いてプリペアードクエリを使うのがほぼ必須になります。構文によってどうしても難しい場合は、かなり厳格な入力バリデーションが必要になります。

Javascriptだけで値をPOSTして、ページ遷移もする方法

ちょっとした事情があって、Javascriptだけで値をPOSTし、ページ遷移もしたいなということがありました。

HTMLでタグを書いて、<input> とか<submit>とかやればいいんですが、ちょっとした値を渡したいだけなので、面倒だな、というのが事情でした。(大した事情ではなかった)

しかし、ググっても意外と出てこないんですよ。

Googleさんが出してくれるのは、AjaxでPOSTして値を取ってきましょう ってページばっかり…。同じページに、Javascriptでページ遷移しようみたいなのもあったりするので、Hitするんですかね。

「それじゃ…ダメ…っ!違うっ…!HTMLでやるみたいにやりたいんだ…っ!」

と思い、英語でググったところ、StackOverflowさんにドンピシャでありました。

「ククク…カイジくん…!君が求めていたのは…これ…っ!」

「あ…ありがてぇ…っ!!!formタグを作ってそれにパラメーターを含んだinputタグを作るという悪魔的発想!!!サンプルコードをコピペするだけで、やりたいことができちまった…!」

と感動しましたので、ここでも紹介させていただきます。
Rakesh Pai さんに大感謝ですね。┌o ペコッ

下記に掲載されていたサンプルコードを掲載しておきます。

/**
 * sends a request to the specified url from a form. this will change the window location.
 * @param {string} path the path to send the post request to
 * @param {object} params the paramiters to add to the url
 * @param {string} [method=post] the method to use on the form
 */

function post(path, params, method='post') {

  // The rest of this code assumes you are not using a library.
  // It can be made less wordy if you use one.
  const form = document.createElement('form');
  form.method = method;
  form.action = path;

  for (const key in params) {
    if (params.hasOwnProperty(key)) {
      const hiddenField = document.createElement('input');
      hiddenField.type = 'hidden';
      hiddenField.name = key;
      hiddenField.value = params[key];

      form.appendChild(hiddenField);
    }
  }

  document.body.appendChild(form);
  form.submit();
}

使う場合は、下記のように使います。

post("index.php", {val:"hogehoge"});

転載元はこちら

https://stackoverflow.com/questions/133925/javascript-post-request-like-a-form-submit/133997#133997

Javascriptだけで次のページにパラメーターをPOSTで渡したいJavascripterのみんなに届け♡

英語で探すって大事だね…(´ω`)

GitHubで誤ってプッシュしたコミットを消去する

初めまして!今年の春から新卒でオンラインコンサルタントに入社した者です。

先日、DB アクセスのためにパスワードをソースコードに書いたまま GitHub の公開リポジトリにプッシュすると言う失態を犯しましたので、その時に行った対処と陥った事態をここに記しておきます。

(1)捨てパスワードを用意し、コミット&プッシュ

大事なパスワードが GitHub で公開され続けるのはまずいので、パスワードを見られても構わない捨てパスワードに変更し、コミット&プッシュします。しかし、これでは何も安心ではありません。

GitHub にて該当ファイルのページに行き、「History」ボタンを押してみましょう。すると、ファイルのコミット履歴が表示されます。その中から、パスワードを変更したコミットを選択してください。

はい。しっかりと残っています。これも消さなくては。

(2)ローカルリポジトリでコミット履歴を消去し、プッシュする。

初めに言わなければならないことがあります。これ以降については、作業を並行して行いながら読まないでください。最後に、落とし穴について述べます。

まず、ターミナルで以下のコマンドでコミット履歴を確認します。

$ git log
commit 94ae37722f47d8b76b6e14f5288b37b368b7cc05 (HEAD -> master, origin/master, origin/HEAD)
Author: 森近 楓 <kaede.morichika@onlineconsultant.jp>
Date:   Wed Apr 22 13:45:17 2020 +0900

    安全なパスワードに変更。

commit 9e6642fb642983e8c3a9a8b69c7a4716cf62ed36
Author: 森近 楓 <kaede.morichika@onlineconsultant.jp>
Date:   Wed Apr 22 13:44:17 2020 +0900

    大事。

commit 0a98ca4c6bd3244d06062d23901157c5598c0dfd
Author: 森近 楓 <kaede.morichika@onlineconsultant.jp>
Date:   Wed Apr 22 13:41:41 2020 +0900

    DB接続設定

コミットが最新のものから順に表示されます。今回は、1つ目と2つ目を消したい。以下のコマンドを使います。

$ git reset --hard HEAR~2 # 消すコミットの数
HEAD is now at 0a98ca4 DB接続設定

git log します。

$ git log
commit 0a98ca4c6bd3244d06062d23901157c5598c0dfd (HEAD -> master)
Author: 森近 楓 <kaede.morichika@onlineconsultant.jp>
Date:   Wed Apr 22 13:41:41 2020 +0900

    DB接続設定

ローカル環境からコミットの内容が消えました。さて、リモートにプッシュしましょう。以下のコマンドを使います。

$ git push -f

-f は、強制的に行うためのオプションです。-f なしでは、ブランチの状態が最新じゃないと判定されエラーで弾かれます。GitHub を確認します。

リモートにおいてもコミットが消えています。

(3)落とし穴

これで、公開リポジトリから大事な情報がなくなり一安心としたいところですが、実は消したコミットに該当する編集部分も消えています。もし、消したい部分(今回で言えば大事なパスワード)以外もコミット内容に含まれているのなら、コミット内容の魚拓をテキストファイルなどに作ってから行うことをお勧めします。

また、今回は最新の2つのコミットを消したかったので git reset で良かったのですが、遥か前のコミットを消そうとすると、git filter-branch というコマンドを使う必要があります。

(4)おわりに

GitHub に誤って大事な情報をプッシュしてしまった時の対処法について紹介しました。初心者エンジニアの皆さんは、コミット&プッシュの前にはしっかりと内容を確認し、僕みたいなことはしないようにしましょう。

参考サイト:https://qiita.com/dtan4/items/34e41e3bd40a43fd8cbf

Smartyの定数をJavaScriptメソッドで処理する

Smarty定数をJavaScriptのメソッドで処理して表示させたいのにできない!という状態に陥りました。
scriptタグのdata属性にSmartyの値を入れてみたのですが、文言の定数のためか上手く文言が表示されず…

最終的には、一度メソッド内に変数をつくって、そこにSmarty定数を代入するという方法に落ち着きました。以下が手順です。

手順
① Smartyの定数を代入するためのJavaScript変数を作成
② {/literal} と{literal} を使ってJavaScriptを外した状態でSmarty定数を記述
③ ②のsmarty定数を①の変数に代入

{literal}
 <script type="text/javascript">
  function check(){
   var delete_move = "{/literal}{$smarty.const.DELETE_MOVE}{literal}";
   return confirm(delete_move);
  }
 </script>
{/literal}

<a href="index.php?action=record_delete" {literal}onclick="return check()"{/literal}>
 {$smarty.const.COMMON_DELETE}
</a> 

デスクトップPC(Win10) HDD→SSD換装

メインの起動ドライブをSSDに換装した作業メモです。

参考サイト:【最新】HDDからSSDに交換・換装!コピー・クローン方法解説!

HDDのパーティション形式を確認

取り付けたSSDを初期化する際、元のHDDとパーティション形式を揃えた方が安全なので、予め形式を確認しておきます。

参考サイト:MBRまたはGPTの確認方法(パーティション形式) | パソコンの問題を改善

  1. 「ディスクの管理」を開く
    「[Win] + [X] キーでメニュー表示 → [k] キーで開く」が一番早いと思います。
  2. OSが入っているディスク(Cドライブのあるディスク)のパーティション形式を確認
    MBRGPTかを確認します。

SSDをPCに接続

PCにSSDを取り付けるだけです。

  1. 一度PCの電源を落とす
    ※メモリ増設などと同様、シャットダウン後に電源ケーブルまで外します。
  2. SSDを取り付ける
    電源ケーブルとSATAケーブルをしっかり取り付けます。

SSDの初期化

元のHDDのフォーマット形式に合わせて、SSDを初期化します。

  1. Windows を起動する
  2. 「ディスクの管理」を開く
  3. SSDの初期化とボリュームの作成を行う
    ※このときのパーティション形式を、元のHDDと合わせます。

EaseUS Todo Backup Freeでディスクのクローン

ソフトのインストールとクローン処理を実施します。

EaseUS Todo Backup Freeの取得

ディスクのクローンやバックアップ等が行えるパワフル便利ソフトです。ディスクのクローンをするだけなら無料版でできてしまう。つよい。

公式サイトからダウンロードするのが安定でしょうか。

(参考記事から名前調べて適当に窓の杜からダウンロードしちゃった)

インストール(ver. 12.0)

[次へ]で問題ありません
許諾に同意
適当なフォルダを選択
不要なチェックを外す
バックアップ機能を使う際の既定のフォルダ指定
適当で良さそう
インストールがゴリゴリ進んでこの画面に
[完了]をクリックするとブラウザで何か開くけど閉じていい
ライセンス認証とか出るけど[後で]

画面右下に出てくるセールスポップアップは「今後表示しない」にチェック入れて消す。

ソフトウェアアップデートはお好みで。

クローン

クローンするには、画面左の「クローン」のとこをクリック
1. ソースの指定はディスクまるごとチェックした
(正直C:のみとかでもいける気はするし、細かいパーティションが切られるのはイヤ……)
2. ターゲットの指定でSSDを選択(画像はHDD選んでるけど気にしない)
2.a. 「編集」をクリックすると、クローン後のレイアウトをいじれる
SSD内に不要なパーティションが切られて困る場合はいじると吉
2.b. 高度なオプションで「SSDに最適化」にチェック入れると良さげ
3. 次の画面で「実行」をクリックすると、ターゲットのデータが消えるとかなんとか聞かれる
OKをクリックすると、ゴリゴリと処理が走る

そこそこ時間がかかるので待機。クローンが完了したら閉じる。

SSDからブートするように変更して起動チャレンジ

ブートの優先順位を変更します。

  • BIOSのブートオプションから、ブートの優先順位を変えるのが丁寧。
  • 元のHDDのSATAケーブルをポートから抜き、同じポートにSSDのSATAケーブルを挿すのは手荒。(これでもちゃんと起動できた)

問題なくSSDから起動できてPCが使えることを確認したら、元のHDDは煮るなり焼くなり……

以上。