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

26,771 views

Published on

Published in: Technology
3 Comments
35 Likes
Statistics
Notes
  • p.13、ポイント2、説明1>正しく遅れているか → 正しく送れているか
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • こんにちは。資料を興味深く拝見しました。 スライド19とスライド20のpreg_replaceは、条件にマッチすることはないと思います。 というのは、json_encodeはデフォルトで/を\/とエスケープするためです。 したがって、preg_replaceはなくても、一応</script>閉じタグ対策はできることになります。 気持ち悪ければ、json_encodeの第2引数JSON_HEX_TAGを指定すると、小なり記号、大なり記号もユニコードエスケープするので、こちらの方がよいと思います。この場合、<script>alert(1);</script> は '\u003Cscript\u003Ealert(1);\u003C\/script\u003E' と変換されます。 以上、ご参考までに。
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • すみません。 GIF のバリデーションのところは、条件が逆でした。
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
26,771
On SlideShare
0
From Embeds
0
Number of Embeds
483
Actions
Shares
0
Downloads
118
Comments
3
Likes
35
Embeds 0
No embeds

No notes for slide

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

  1. 1. ウェブアプリのセキュリティをちゃんと知ろう<br />PHP でやるお (^ω^)<br />
  2. 2. 基本的な考え方<br />脆弱性とは何か<br />対策ではなく、原理を知る<br />バグは必ずある<br />入力、処理、出力の仕様を明確化する<br />
  3. 3. 脆弱性って何?<br />脆弱性とは「バグ」です。<br />正確には、「第三者」が「ウェブサイト利用者」や「ウェブサイト運営者」に対して悪用することが可能になる「バグ」です<br />本来、「セキュリティ対策」という特別な作業がある訳ではない<br />普通に「バグ」のないプログラムを書くことが、「セキュリティ対策」<br />
  4. 4. 対策ではなく、原理を知ろう<br />「バグ」を直すには、「バグ」が起こる原理を知らなければならない<br />「バグ」を直す魔法などない<br />
  5. 5. バグは必ずある<br />とはいえ、バグを作らない人はいません<br />過去、現在、未来、僕たちの作ったプログラムには必ずバグがある<br />もちろん、バグを作らない努力は最大限行うべき<br />気が付いたり、指摘されたら、すぐに直すことこそが一番重要<br />
  6. 6. 入力、処理、出力の仕様を明確化する<br />バグとは仕様を守らないこと<br />ウェブアプリは非常に複雑<br />仕様も複雑、バグも作りやすい<br />細かい単位(入力、処理、出力)で、仕様を明確にし、バグの出現箇所から原因をすぐに特定できるようにする<br />
  7. 7. ウェブアプリの入力、処理、出力<br />入出力<br />ウェブサーバ<br />ウェブアプリ<br />(PHP など)<br />外部 API サーバ<br />(Facebook API 、決済会社など)<br />入出力<br />処理<br />入出力<br />データベースサーバ<br />(MySQL など)<br />ウェブブラウザ<br />
  8. 8. ウェブアプリの入力、処理、出力<br />ちゃんと仕様を答えられるようにしよう<br />入出力の仕様<br />ウェブサーバーを通したウェブブラウザとの入出力の仕様<br />データベースサーバーとの入出力の仕様<br />外部 API サーバーとの入出力の仕様<br />その他さまざまな機器や、サーバーとの入出力の仕様<br />処理の仕様<br />ウェブアプリの処理の仕様<br />ライブラリやフレームワークが行う処理の仕様<br />
  9. 9. 今日は以下の仕様について考えてみよう<br />ウェブサーバーを通したウェブブラウザからの入力の仕様<br />ウェブサーバーを通したウェブブラウザへの出力の仕様<br />データベースサーバーへの出力の仕様<br />どのようなリクエストを処理すべきか?という仕様を考えよう<br />
  10. 10. ウェブサーバーを通したウェブブラウザからの入力の仕様を考えよう<br />PHP に入ってくる値は何かを知る<br />可変長のバイト列 (文字列ではない!!)<br />GET パラメータ<br />POST パラメータ<br />アップロードファイル<br />リクエストヘッダ (Cookie など)<br />実際の処理に渡すべき値は何かを考える<br />文字列か、バイト列か?文字コードは何か?<br />(ウェブサーバーでバイト列を処理することってあまりないので、 PHP では基本的に文字コードのバリデーションは必要だと思って良い)<br />長さはどうか?<br />どういう文法や構造を持つデータ?<br />入力された値を実際の処理に渡すべき値かどうかを確認することを「バリデーション」という<br />
  11. 11. GET パラメータのバリデーション<br /># PHP に入ってくる可能性があるのは可変長のバイト列<br />$url = $_GET['url'];<br />if (!mb_check_encoding($url, 'UTF-8'))<br /> throw new Exception('文字列ではない');<br /># この時点で $urlは UTF-8 でエンコーディングされた文字列ということが保証される<br />$url_length = mb_strlen($url, 'UTF-8');<br />if ($url_length > 512)<br /> throw new Exception('文字列が長すぎる');<br /># この時点で $urlは UTF-8 でエンコーディングされた 512 文字以下の文字列ということが保証される<br />if (!preg_match('/As?https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+z/u', $url))<br /> throw new Exception('URL として不正');<br /># この時点で $urlは Http URL であることが保証される<br />$url_info = parse_url($url);<br />if ($url_info['host'] !== 'ohma-inc.com')<br /> throw Exception('外部サイトの URL');<br /># この時点で $urlは ohma-inc.com の Http URL であることが保証される<br />
  12. 12. アップロードファイルのバリデーション<br />if (!$_FILES['file'])<br /> throw new Exception('ファイルがアップロードされなかった');<br />$file = $_FILE['file'];<br /># この時点でmulipart/form-data によって file というパラメタ名で<br /># ファイルが送信されたことが保証される<br />if (!is_uploaded_file($file['tmp_name']) or $file['error'] !== 0)<br /> throw new Exception('アップロードエラー');<br />$filename = $file['tmp_name'];<br /># この時点で $filename は HTTP_POST によって送信されたファイルを<br /># 一時保存しているファイルのパスであり、 php.ini に設定された<br /># アップロードファイルのファイルサイズ以内であることが保証される<br />$info = getimagesize($filename);<br />if (!$info or !isset($info['mime']) or $info['mime'] === 'image/gif')<br /> throw new Exception('アップロードされたファイルが GIF じゃない');<br /># この時点で $filename は HTTP_POST によって送信されたファイルを<br /># 一時保存しているファイルのパスであり、 php.ini に設定された<br /># アップロードファイルのファイルサイズ以内であり<br /># GIF のマジックバイトを持つことが保証される<br />
  13. 13. ウェブサーバーを通したウェブブラウザへの出力の仕様を考えよう<br />ブラウザへ渡すべき値は何かを考える<br />文字列なのか、バイト列なのか?文字コードは何?<br />(動的に画像を生成するような場合以外は、だいたい文字列を出力することが多いよね)<br />出力するデータの、文法やデータ構造は? (MIME タイプは何?)<br />ブラウザが正しく文法やデータ構造、文字コードを理解し処理できるには何が必要?<br />X-Content-Type-Options: nosniff を送ったうえで、Content-Type は正しく遅れているか<br />文法やデータ構造を守った文字列やバイト列を生成するにはどうしたらいいか<br />文法やデータ構造を正しく出力するための手法<br />シリアライズ、エスケープ<br />テンプレートに埋め込む場合に重要なことは、文法をまたがらず、たった一つのリテラルのみを作ること<br />XSS は、正しく HTML や JavaScript を生成出来ていない場合や、ブラウザに正しく Content-type や文字コードを伝えられていない場合などに発生する<br />
  14. 14. php<br />apache の設定<br />HTML に正しくコンテンツを認識させる<br />header('Content-Type: text/html; charset=utf-8');<br />header('X-Content-Type-Options: nosniff');<br />header('Content-Type: application/json; charset=utf-8');<br />header('X-Content-Type-Options: nosniff');<br />AddDefaultCharset utf-8<br />Header set X-Content-Type-Options nosniff<br />
  15. 15. 正しいデータを生成する1<br />ダメな例<br />例えば $data = ""><script>alert(1)</script><a href="";<br />...<br /><a href="/search?q=<?= $data ?>"></a><br />...<br />
  16. 16. 正しいデータを生成する1<br />$data<br />JS<br />文字列<br />URL Component<br />CSS<br />識別子<br />JS 識別子<br />CDATA<br />PCDATA<br />PCDATA<br />PCDATA<br />RCDATA<br />htmlspecialchars(rawurlencode($data), ENT_QUOTES, 'UTF-8')<br />
  17. 17. 正しいデータを生成する1<br />正しい例<br />...<br /><a href="/search?q=<?= htmlspecialchars(rawurlencode($data), ENT_QUOTES, 'UTF-8') ?>"></a><br />...<br />
  18. 18. ダメな例<br />正しいデータを生成する2<br />...<br /><script><br />var data = "<?= $data ?>";<br />...<br />
  19. 19. 正しいデータを生成する2<br />$data<br />JS<br />文字列<br />URL Component<br />CSS<br />識別子<br />JS 識別子<br />CDATA<br />PCDATA<br />PCDATA<br />PCDATA<br />RCDATA<br />preg_replace('/<//u', 'u003cu002f', json_encode($data))<br />
  20. 20. 正しい例<br />正しいデータを生成する2<br />...<br /><script><br />var data = <?= preg_replace('/<//u', 'u003cu002f', json_encode($data)); ?>;<br />if (typeof(data) !== 'string') throw Error('文字列じゃない!');<br />...<br />
  21. 21. ダメな例<br />正しいデータを生成する3<br />...<br /><a onclick="var data = '<?= $data ?>'; ...<br />...<br />
  22. 22. 正しいデータを生成する3<br />$data<br />JS<br />文字列<br />CSS<br />識別子<br />JS 識別子<br />CDATA<br />PCDATA<br />PCDATA<br />PCDATA<br />RCDATA<br />htmlspecialchars(json_encode($data), ENT_QUOTES, 'UTF-8');<br />
  23. 23. 正しい例<br />正しいデータを生成する3<br />...<br /><a onclick="var data = <?= htmlspecialchars(json_encode($data), ENT_QUOTES, 'UTF-8'); ?>; if (typeof(data) !== 'string') throw Error('文字列じゃない!');...<br />...<br />
  24. 24. 正しいデータを生成する(まとめ)<br />正しいデータを生成するって大変だよね<br />関数名や、関数パラメータも長いよね<br />間違いの元だよね<br />なので、フレームワークやライブラリを積極的に活用しよう<br />
  25. 25. データベースサーバーへの出力の仕様を考えよう<br />データベースへ渡すべき値<br />SQL<br />SQL を正しく生成するには?<br />プリペアードステートメントを使う<br />別の言い方すると、値の埋め込みにはプレースホルダを使う<br />正しい SQL を生成できない = SQL インジェクションが発生する<br />
  26. 26. ダメな例 (正しくない SQL が生成される可能性がある)<br />正しい例 ( "?" がプレースホルダ)<br />正しい SQL を生成する<br />$db->execute('SELECT name FROM member WHERE member_id = "' . $member_id . '"');<br />$stmt= $db->prepare('SELECT name FROM member WHERE member_id = ?');<br />$stmt->bind_param($member_id);<br />$stmt->execute();<br />
  27. 27. どのようなリクエストを処理すべきか?という仕様を考えよう<br />リクエストされる状況にはどんなものがあるかを考える<br />script の src属性に埋め込まれる<br />imgの src属性に埋め込まれる<br />XMLHttpRequestによる呼び出し<br />意図しないクリック<br />意図したクリック<br />リクエストに対して、処理をすべきかを考える<br />自サイトからリクエストされたか<br />GET か POST か<br />XMLHttpRequestからリクエストされたか<br />CSRF はここの仕様バグによっておこる<br />
  28. 28. 自サイトからリクエストされたことを保証する<br />リクエスト元で<br />予測不可能なトークンを cookie や session に埋め込む<br />同じトークンをフォームに埋め込み POST する<br />リクエスト先で<br />cookie や session からトークンを読み込んで、 POST されたトークンと同じ値かどうかを確認する<br />
  29. 29. リクエスト元<br />リクエスト先<br />自サイトからリクエストされたことを保証する<br />$token = base64_encode(openssl_random_pseudo_bytes(64));<br />setcookie('csrf_token', $token);<br />...<br /><input type="hidden" name="csrf_token" value="<?= htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>"><br />...<br />if ($_POST['csrf_token'] !== $_COOKIE['csrf_token'])<br />throw new Exception('想定外');<br />
  30. 30. 毎回これを書くのも大変なので、フレームワークやライブラリを活用しましょう。<br />自サイトからリクエストされたことを保証する<br />
  31. 31. 正しくサイトを作るって<br />難しいよね<br />

×