31
ウウウウウウウウウウウウウ ウウウウウウウウ PHP ウウウウ ( ω )

ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

  • Upload
    -

  • View
    23.691

  • Download
    2

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

ウェブアプリのセキュリティをちゃんと知ろうPHP でやるお ( ^ ω ^ )

Page 2: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

基本的な考え方• 脆弱性とは何か• 対策ではなく、原理を知る• バグは必ずある• 入力、処理、出力の仕様を明確化する

Page 3: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

脆弱性って何?• 脆弱性とは「バグ」です。– 正確には、「第三者」が「ウェブサイト利用

者」や「ウェブサイト運営者」に対して悪用することが可能になる「バグ」です

• 本来、「セキュリティ対策」という特別な作業がある訳ではない– 普通に「バグ」のないプログラムを書くこと

が、「セキュリティ対策」

Page 4: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

対策ではなく、原理を知ろう• 「バグ」を直すには、「バグ」が起こる

原理を知らなければならない• 「バグ」を直す魔法などない

Page 5: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

バグは必ずある• とはいえ、バグを作らない人はいません• 過去、現在、未来、僕たちの作ったプロ

グラムには必ずバグがある• もちろん、バグを作らない努力は最大限

行うべき• 気が付いたり、指摘されたら、すぐに直

すことこそが一番重要

Page 6: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

入力、処理、出力の仕様を明確化する

• バグとは仕様を守らないこと• ウェブアプリは非常に複雑– 仕様も複雑、バグも作りやすい

• 細かい単位(入力、処理、出力)で、仕様を明確にし、バグの出現箇所から原因をすぐに特定できるようにする

Page 7: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

ウェブアプリの入力、処理、出力

ウェブアプリ

(PHP など )

ウェブサーバ外部 API サーバ

(Facebook API 、決済会社など )

データベースサーバ(MySQL など )

入出力

入出力

入出力

ウェブブラウザ

処理

Page 8: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

ウェブアプリの入力、処理、出力

• ちゃんと仕様を答えられるようにしよう– 入出力の仕様

• ウェブサーバーを通したウェブブラウザとの入出力の仕様

• データベースサーバーとの入出力の仕様• 外部 API サーバーとの入出力の仕様• その他さまざまな機器や、サーバーとの入出力の仕

様– 処理の仕様

• ウェブアプリの処理の仕様• ライブラリやフレームワークが行う処理の仕様

Page 9: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

今日は以下の仕様について考えてみよう

• ウェブサーバーを通したウェブブラウザからの入力の仕様

• ウェブサーバーを通したウェブブラウザへの出力の仕様

• データベースサーバーへの出力の仕様• どのようなリクエストを処理すべきか?と

いう仕様を考えよう

Page 10: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

ウェブサーバーを通したウェブブラウザからの入力の仕様を考えよう

• PHP に入ってくる値は何かを知る– 可変長のバイト列 ( 文字列ではない!! )

• GET パラメータ• POST パラメータ• アップロードファイル• リクエストヘッダ (Cookie など )

• 実際の処理に渡すべき値は何かを考える– 文字列か、バイト列か?文字コードは何か?

• ( ウェブサーバーでバイト列を処理することってあまりないので、 PHP では基本的に文字コードのバリデーションは必要だと思って良い )

– 長さはどうか?– どういう文法や構造を持つデータ?

• 入力された値を実際の処理に渡すべき値かどうかを確認することを「バリデーション」という

Page 11: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

GET パラメータのバリデーション

# PHP に入ってくる可能性があるのは可変長のバイト列$url = $_GET['url'];

if (!mb_check_encoding($url, 'UTF-8')) throw new Exception(' 文字列ではない ');

# この時点で $url は UTF-8 でエンコーディングされた文字列ということが保証される

$url_length = mb_strlen($url, 'UTF-8');if ($url_length > 512) throw new Exception(' 文字列が長すぎる ');

# この時点で $url は UTF-8 でエンコーディングされた 512 文字以下の文字列ということが保証される

if (!preg_match('/\As?https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:@&=+$,%#]+\z/u', $url)) throw new Exception('URL として不正 ');

# この時点で $url は Http URL であることが保証される

$url_info = parse_url($url);if ($url_info['host'] !== 'ohma-inc.com') throw Exception(' 外部サイトの URL');

#  この時点で $url は ohma-inc.com の Http URL であることが保証される

Page 12: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

アップロードファイルのバリデーション

if (!$_FILES['file']) throw new Exception(' ファイルがアップロードされなかった ');

$file = $_FILE['file'];

# この時点で mulipart/form-data によって file というパラメタ名で# ファイルが送信されたことが保証される

if (!is_uploaded_file($file['tmp_name']) or $file['error'] !== 0) throw new Exception(' アップロードエラー ');

$filename = $file['tmp_name'];

# この時点で $filename は HTTP_POST によって送信されたファイルを# 一時保存しているファイルのパスであり、 php.ini に設定された# アップロードファイルのファイルサイズ以内であることが保証される

$info = getimagesize($filename);if (!$info or !isset($info['mime']) or $info['mime'] === 'image/gif') throw new Exception(' アップロードされたファイルが GIF じゃない ');

# この時点で $filename は HTTP_POST によって送信されたファイルを# 一時保存しているファイルのパスであり、 php.ini に設定された# アップロードファイルのファイルサイズ以内であり# GIF のマジックバイトを持つことが保証される

Page 13: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

ウェブサーバーを通したウェブブラウザへの出力の仕様を考えよう

• ブラウザへ渡すべき値は何かを考える– 文字列なのか、バイト列なのか?文字コードは何?

• ( 動的に画像を生成するような場合以外は、だいたい文字列を出力することが多いよね )

– 出力するデータの、文法やデータ構造は? (MIME タイプは何? )• ブラウザが正しく文法やデータ構造、文字コードを理解し処理できるに

は何が必要?– X-Content-Type-Options: nosniff を送ったうえで、 Content-Type は正しく遅れてい

るか– 文法やデータ構造を守った文字列やバイト列を生成するにはどうしたらいいか

• 文法やデータ構造を正しく出力するための手法– シリアライズ、エスケープ– テンプレートに埋め込む場合に重要なことは、文法をまたがらず、たった一つ

のリテラルのみを作ること• XSS は、正しく HTML や JavaScript を生成出来ていない場合や、ブラウザ

に正しく Content-type や文字コードを伝えられていない場合などに発生する

Page 14: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

• php

• apache の設定

HTML に正しくコンテンツを認識させる

header('Content-Type: text/html; charset=utf-8');header('X-Content-Type-Options: nosniff');

header('Content-Type: application/json; charset=utf-8');header('X-Content-Type-Options: nosniff');

AddDefaultCharset utf-8Header set X-Content-Type-Options nosniff

Page 15: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

正しいデータを生成する1

...

<a href="/search?q=<?= $data ?>"></a>

...

• ダメな例

• 例えば $data = "\"><script>alert(1)</script><a href=\"";

Page 16: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

正しいデータを生成する1

CDATAPCDATA PCDATA PCDATARCDATA

JS文字列

JS 識別

URL 

Component

CSS識別

$data

htmlspecialchars(rawurlencode($data), ENT_QUOTES, 'UTF-8')

Page 17: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

正しいデータを生成する1

...

<a href="/search?q=<?= htmlspecialchars(rawurlencode($data), ENT_QUOTES, 'UTF-8') ?>"></a>

...

• 正しい例

Page 18: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

• ダメな例

正しいデータを生成する2

...

<script>var data = "<?= $data ?>";

...

Page 19: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

正しいデータを生成する2

CDATAPCDATA PCDATA PCDATARCDATA

JS文字列

JS 識別

URL 

Component

CSS識別

$data

preg_replace('/<\//u', '\\u003c\\u002f', json_encode($data))

Page 20: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

• 正しい例

正しいデータを生成する2

...

<script>var data = <?= preg_replace('/<\//u', '\\u003c\\u002f', json_encode($data)); ?>;

if (typeof(data) !== 'string') throw Error(' 文字列じゃない! ');...

Page 21: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

• ダメな例

正しいデータを生成する3

...

<a onclick="var data = '<?= $data ?>'; ...

...

Page 22: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

正しいデータを生成する3

CDATAPCDATA PCDATA PCDATARCDATA

JS 識別

JS文字

CSS識別

$data

htmlspecialchars(json_encode($data), ENT_QUOTES, 'UTF-8');

Page 23: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

• 正しい例

正しいデータを生成する3

...

<a onclick="var data = <?= htmlspecialchars(json_encode($data), ENT_QUOTES, 'UTF-8'); ?>; if (typeof(data) !== 'string') throw Error(' 文字列じゃない! '); ...

...

Page 24: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

正しいデータを生成する(まとめ)

• 正しいデータを生成するって大変だよね• 関数名や、関数パラメータも長いよね– 間違いの元だよね

• なので、フレームワークやライブラリを積極的に活用しよう

Page 25: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

データベースサーバーへの出力の仕様を考えよう

• データベースへ渡すべき値– SQL

• SQL を正しく生成するには?– プリペアードステートメントを使う• 別の言い方すると、値の埋め込みにはプレースホ

ルダを使う• 正しい SQL を生成できない = SQL イン

ジェクションが発生する

Page 26: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

• ダメな例 ( 正しくない SQL が生成される可能性がある )

• 正しい例 ( "?" がプレースホルダ )

正しい SQL を生成する

$stmt = $db->prepare('SELECT name FROM member WHERE member_id = ?');

$stmt->bind_param($member_id);$stmt->execute();

$db->execute('SELECT name FROM member WHERE member_id = "' . $member_id . '"');

Page 27: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

どのようなリクエストを処理すべきか?という仕様を考えよう

• リクエストされる状況にはどんなものがあるかを考える– script の src 属性に埋め込まれる– img の src 属性に埋め込まれる– XMLHttpRequest による呼び出し– 意図しないクリック– 意図したクリック

• リクエストに対して、処理をすべきかを考える– 自サイトからリクエストされたか– GET か POST か– XMLHttpRequest からリクエストされたか

• CSRF はここの仕様バグによっておこる

Page 28: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

自サイトからリクエストされたことを保証する

• リクエスト元で–予測不可能なトークンを cookie や session に

埋め込む–同じトークンをフォームに埋め込み POST す

る• リクエスト先で– cookie や session からトークンを読み込んで、

POST されたトークンと同じ値かどうかを確認する

Page 29: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

• リクエスト元

• リクエスト先

自サイトからリクエストされたことを保証する

$token = base64_encode(openssl_random_pseudo_bytes(64));setcookie('csrf_token', $token);

...<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">...

if ($_POST['csrf_token'] !== $_COOKIE['csrf_token']) throw new Exception('想定外 ');

Page 30: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

• 毎回これを書くのも大変なので、フレームワークやライブラリを活用しましょう。

自サイトからリクエストされたことを保証する

Page 31: ウェブアプリのセキュリティをちゃんと知ろう (毎週のハンズオン勉強会の資料)

正しくサイトを作るって• 難しいよね