LIGデザイナー採用
LIGデザイナー採用
2016.07.12
#11
バックエンドへの道

セキュリティの考え方とは?構築したプログラムの脆弱性を考えてみた

エリカ

こんにちは、エリカです。

この連載「バックエンドへの道」では簡単な掲示板を構築しながら、バックエンド開発技術を学んでいきます。

前回までで掲示板が一通りできあがりましたが、セキュリティの観点を割愛した構築でした。そこで今回はセキュリティの考え方から防御の方法までをご紹介します。

セキュリティの考え方とは?

不正アクセスがあったとか、個人情報が流出したなどのニュースを定期的に耳にするかと思います。どうして、このような情報漏えいが発生するのでしょうか。

コンピュータはINPUTとOUTPUTを繰り返している

まず、コンピュータにはINPUT(入力)と、OUTPUT(出力)があります。コンピュータに命令を与えると、それに対応した処理を行うというものです。

キーボードのキーをパンチするのも、マウスを操作するのもすべてINPUTですし、結果として画面上に文字が現れるのもスクロールするのも、すべてそのINPUTに対するOUTPUTです。このようにINPUTとOUTPUTを繰り返してコンピュータは動作しています。

Webシステムも例外ではありません。「会社概要のページを見せて」というユーザーからのINPUTを受け取ったら、会社概要のページをOUTPUTしますが、Webシステムの場合はINPUTが「リクエスト」、OUTPUTが「レスポンス」などと言われます。

「あ」のキーをパンチしたら、画面上に「あ」が表示される。

マウスホイールを操作したら画面がスクロールする。

会社概要のページのリンクをクリックしたら、会社概要のページが表示される。

これらは当たり前のように感じるかも知れませんが、果たしてそれはどうしてでしょうか。なぜ期待するとおりに動くのでしょうか。

それは、そのようにプログラムされているからに他なりません。コンピュータは、INPUTに応じたプログラムを愚直に実行しているだけなのです。

「プログラムの脆弱性」とは?

しかし、万が一プログラムに不備があった場合には、このINPUTの内容によっては予期しない動作をする可能性があります。

このような不備が意図せず発生してしまったり、または悪用されたりするといった事態がしばしば見受けられます。耳にしたことがあるかと思いますが、それを「プログラムの脆弱性」などと言います。

こんな悪用が考えられます

例えば一般的な掲示板のプログラムでは、書き込まれた内容を保存して、他の訪問者が閲覧できるようにします。
しかし、その書き込みの中に、スパムサイトへ自動で遷移してしまうJavaScriptが埋め込まれていたらどうでしょうか。書き込みを閲覧したユーザーは、意図せずスパムサイトに飛ばされてしまうことでしょう。

さらにWebシステムでは、リクエストとレスポンスにブラウザとサーバの間での通信が発生することが多く、その通信データが利用される場合もあります。

セキュリティを考える上で大切なこと

セキュリティの考え方のひとつとしては、「いかにしてこのような問題を未然に防ぐか」があるかと思います。

一貫して言えることは、「ユーザーからINPUTされたデータを信用しない」ということです。それが本当にプログラムを処理する上で正しいデータなのかを検証することが大事になってきます。
同時に、OUTPUTを保証することも重要です。例え、システムに意図しないデータが入力されてしまった場合でも、それを表示する時点で影響のない形に変換することでコンピュータの動作を制限できる場合があるからです。

多くの人が訪問するようなWebシステムを構築する場合には、自分のサイトのみならず、他のサイトへの影響が及ぶことも考えて、より注意深くこのような点を考慮する必要があります。

はじめは、イメージすることが難しいかと思いますが、セキュリティに対してしっかりと注意を払うという意識・知識を持つことが大事です。

脆弱性にはどんな種類と対策があるの?

利用される脆弱性はさまざまですが、作成した掲示板の中からいくつかを具体的に見ていきたいと思います。まずは、セキュリティに対して考えるきっかけになれば幸いです。

意図しないSQLが実行されてしまう脆弱性

掲示板のプログラムには、

$smt = $this->pdo->prepare('insert into post (name,email,body,created_at,updated_at) values(:name,:email,:body,now(),now())');

という記述があります。

この部分ですが、

insert into post (name,email,body,created_at,updated_at) values('{$_GET['name']}','{$_GET['email']}','{$_GET['body']}', now(),now())

名前やメールアドレスをデータベースに保存したいのですから、こういう記述でもいいと思いませんか?

ではしかし、$_GET['name']の中身が名前以外だったらどうなるか。名前以外の文字が入力されてしまうことはもちろん、DELETEなどのSQLが含まれていれば、それが実行されてしまうことになります。

例えば、$_GET['name']に、$_GET['name'] = "0','','',now(),now());DELETE FROM post–"などが入力されていた場合はどうでしょう。INSERT文のあとにDELETE文が実行されてしまうかもしれません。
Webシステムが意図していないSQLが実行されてしまうという訳ですね。

これを防ぐには、ユーザーから入力された値がSQLとして評価(実行)されないようにすればよく、PDOのprepareを使用すると、この場合はSQLが含まれていた場合でもただの文字として扱ってくれるようになります。
元々はそのように記述されていましたね。

$smt = $this->pdo->prepare('insert into post (name,email,body,created_at,updated_at) values(:name,:email,:body,now(),now())');
        $smt->bindParam(':name',$data['name'], PDO::PARAM_STR);
        $smt->bindParam(':email',$data['email'], PDO::PARAM_STR);
        $smt->bindParam(':body',$data['body'], PDO::PARAM_STR);
        $smt->execute();

PDO(PHPDataObject)が、余計なSQLが実行されないように適切に処理します。少なくとも意図しない別のSQLが実行されてしまうことはありません。
これは「プリペアドステートメント」という概念になります。SQLを扱う場合には、常にこの点にも留意するとよいかもしれません。
しかし、SQLが実行されないとしても、入力された内容は保存されてしまいます。
では、そこにSQL以外のものが含まれていたらどうでしょうか。

意図しないJavaScriptが実行されてしまう脆弱性

<!-- 投稿表示エリア -->
    <?php if (!empty($post_datas)) {?>
    <div class="list">
        <?php foreach ($post_datas as $post) { ?>
        <div class="item">
            <div class="name"><?php if (!empty($post["email"])) {?><a href="mailto:<?php echo $post["email"];?>"><?php } ?><?php echo $post["name"];?><?php if (!empty($post["email"])) {?></a><?php } ?></div>
            <div class="body"><?php echo nl2br($post["body"]);?></div>
            <div class="date"><?php echo $post["created_at"];?></div>
        </div>
        <?php } ?>
    </div>
    <?php } ?>
<!-- // 投稿表示エリア -->

掲示板に投稿された内容が表示される部分になります。

もし$post["body"]などの中に、こんなJavaScriptが埋め込まれていたらどうでしょうか。

<script>window.location.href="指定のURL";</script>

この書き込みが表示された瞬間、指定のURLに遷移してしまうかもしれません。
これは、入力内容がブラウザにHTMLとして評価されてしまって、そのインラインJavaScriptが実行されてしまっているのです。

これを防ぐには、入力内容にHTMLやJavaScriptが含まれていたら、それがHTMLとして評価されないように変換してしまいましょう。
htmlentitiesという関数を使うと、入力内容の中で、HTMLエンティティに関連付けられたもの(<や&など)をすべて変換してくれます。

<div class="body"><?php echo nl2br(htmlentities($post["body"]));?></div>

場合によっては、変換でなく削除でもいいのですが、残った文字が意味を持ってしまう場合もあるので、その点は注意が必要になります。
しかし、仕様によっては、HTMLが有効にならなければ行けない場合があるかもしれません。ブログシステムでしたら、文字を大きくしたり、リンクが書けないといけない場合があるかもしれません。
その場合は、使用を許可するタグを決めておき、それ以外のタグを無効にするなどとしなければなりません。

作成した掲示板では、セッションやヘッダーの制御といった部分に触れていませんが、このあたりも脆弱性を持ちやすい部分です。

つまり、利用する機能に応じて、また、こういう場合はこうなるといった設計に応じて、脆弱性の有無をその都度考えていく必要があるということになります。

まとめ

攻撃の手法は他にもたくさんありますし、これからも増えていくことかと思います。
まずは脆弱になりうるかどうかを気にするように、基本的なところからしっかりと対策を施せるようにしておくとよいでしょう。

IPA情報処理推進機構の、安全なウェブサイトの作り方では非常にわかりやすくをまとめられていますので、ぜひ読んでみてください。

次回は、いよいよ掲示板の公開です。