X

【挫折させないWebアプリ開発講座、最終回】セキュリティを強化しよう

この記事は約 10 分で読めます。
YUMA
こんにちは。エンジニア社長のYUMAです。

ついにこの講座も最終回まで来ました。ここまで開発環境の準備やデータベースとのやりとりを色々と学んできましたね。

最終回はセキュリティ対策ができていない部分を修正して、シンプルWebアプリの完成としていきますので、最後もがんばっていきましょう!

XSS対策

以前ご紹介した攻撃手法のクロスサイトスクリプティング(XSS)対策ができていなかったので、追加していきますね。

XSSはWebアプリを利用したユーザーで悪意のあるjavascriptが動作していまうことが原因でしたね。そのタイミングはWebアプリ側がhtmlとしてブラウザに文字を出力するタイミングなんです。

じゃあそのjavascriptを無効化してあげればいいんだ!とてもシンプルですね。

出力する時は全てエスケープ

ではここで問題。今作っているアプリがHTMLに出力するタイミングはどこでしょうか?

*ヒントはechoコマンドを使っている部分です。

正解はコチラ

最後の名前表示部分ですね。

phpでのエスケープ方法

phpが最初から持っている関数’htmlspecialchars’を使ってエスケープします。

引数とは関数に与えるデータのこと、htmlspecialcharsは与えたデータを処理するので、データが空だとエラーになる。
詳しくはWebアプリ実践編で解説予定。

今作っているアプリはこうなります。

 <h1>名前表示</h1>
 
        <p>通し番号:<?php echo htmlspecialchars($data['id'], ENT_QUOTES, 'UTF-8'); ?></p>
 
        <p>お名前:<?php echo htmlspecialchars($data['uname'], ENT_QUOTES, 'UTF-8'); ?></p>

ブラウザに出力したいものをhtmlspecialcharsで囲ってあげればOK!

念のため、ちゃんと名前が表示されるかCloud9で試してみましょう。

繰り返し使うものは関数化

うまく表示されましたか?今回は2つだけだったからhtmlspecialcharsと長ったらしい命令を書けたのですが、もっと大きなWebアプリになると何十何百とあるので、めんどくさいし間違えそう。

YUMA
僕は楽したいです(笑)

そこで関数化という便利な方法があるので紹介しましょう。

関数化とは自分で命令を作ることです。今回は長くてめんどくさい(笑)って理由なので、h()という関数を作っていきます。

関数をステップとは

  1. php部分でfunction 任意の関数名(ここではh)を宣言
  2. funcion h(){ } のように波カッコを書く
  3. 波カッコの中に処理を書く→ついでにechoも入れてさらに楽にしちゃいましょう!

するとh関数の宣言はこのようになります。

function h(){
    echo htmlspecialchars();
}

 

ただ、これだと不完全で、htmlspecialcharsには引数が必要だと書きましたね。エスケープ処理する文字がなかったらエラーになりますからね。

だからオリジナル関数自体に引数を与えて、その引数をhtmlspecialcharsに与える。完成形はコチラ↓

function h($s){
    htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
ENT_QUOTES, ‘UTF-8’あたりは文字コードを日本語対応のUTF-8で出力しますよっていう呪文なので、ここはこのままコピペでOKです

オリジナル関数を使ってみる

では先ほど作ったオリジナル関数をphpの最初に宣言しましょう。関数はコードの中で使うので一番上で宣言するのがオススメ。

view.phpの最初に宣言。

では次にエスケープしたい部分にオリジナル関数h()を適用してみましょう。適用後のコードはコチラ。

 <h1>名前表示</h1>
 
        <p>通し番号:<?php h($data['id']); ?></p>
 
        <p>お名前:<?php h($data['uname']); ?></p>

 

では試しに名前を表示してみましょう。以前と変わらない結果となるでしょう。

SQL,OSインジェクション対策は実施済み

あと二つ重要なセキュリティ項目がありましたが、こちらはプリペアドステートメントバインドバリューで対策済みでしたね。

もし忘れてしまった方はこちらを参考に。

【挫折させないWebアプリ開発講座11】アプリへの攻撃事例とその対策

2017.06.27

最後に

YUMA
ここまで長い間お付き合いありがとうございました

今日でWebアプリ開発の基本の基本をマスターできました。

もちろんこの講座だけでは実用的なWebアプリケーションを開発することは難しいでしょう。

そのため、次回からは「挫折させないWebアプリ開発講座実践編」をお送りします。

コードを気軽にコピペできるメモ帳のようなWebアプリを開発していきますので、是非ともお楽しみに!

PS : この講座を見てWebアプリに興味を持ってオリジナルアプリを開発したい人向けに個人レッスンを行っています。コチラもどうぞ!

プログラミング個別レッスン

 

完成コード

index.php

<?php 
 session_start();
 if(isset($_POST['datapost'])){ 
 $_SESSION['name'] = $_POST['name']; 
 header('Location: regist.php'); 
 } 
 ?>

<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>PHP_TEST</title>
    </head>
    <body>
        <h1>お名前プログラム</h1>
        <p>お名前を入力してください</p>
        <form action="" method="post">
            <input type="text" name="name">
            <input type="submit" name="datapost">
            
        </form>
        <a href="select.php">IDから探す</a>
    </body>
</html>

regist.php

<?php
session_start();
try{
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8','kaeru3','',array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
} catch (PDOException $e) {
 exit('データベース接続失敗。'.$e -> getMessage());
}

$name = $_SESSION['name'];//ユーザーから受け取った値を変数に入れる
$stmt = $pdo -> prepare("INSERT INTO name(uname) VALUES(:name)");//登録準備
$stmt -> bindValue(':name', $name, PDO::PARAM_STR);//登録する文字の型を固定
$stmt -> execute();//データベースの登録を実行
$pdo = NULL;//データベース接続を解除
?>

 

select.php

<?php
session_start();

if(isset($_POST['datapost'])){
    $_SESSION[id] = $_POST['id'];
    header('Location: view.php');
}

?>

<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>PHP_TEST</title>
    </head>
    <body>
        <h1>ID選択画面</h1>
        <p>IDを入力してください</p>
        <form action="" method="post">
            <input type="int" name="id">
            <input type="submit" name="datapost">
        </form>
    </body>
</html>

 

view.php

<?php
session_start();

function h($s){
  echo htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

try{
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8','kaeru3','',array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
} catch (PDOException $e) {
 exit('データベース接続失敗。'.$e -> getMessage());
}

$id = $_SESSION['id'];
 
$stmt = $pdo -> prepare("SELECT * FROM name WHERE id=:id");
 
$stmt -> bindValue(':id', $id, PDO::PARAM_INT);
 
$stmt -> execute();
 
$data = $stmt -> fetch();
 
$pdo = NULL;
?>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>PHP_TEST</title>
    </head>
    <body>
        <h1>名前表示</h1>
 
        <p>通し番号:<?php h($data['id']); ?></p>
 
        <p>お名前:<?php h($data['uname']); ?></p>
    </body>
</html>