Your SlideShare is downloading. ×
PyCon APAC 2013 Web Secure Coding
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

PyCon APAC 2013 Web Secure Coding

1,333
views

Published on

Basics of Web Secure Coding on Python

Basics of Web Secure Coding on Python


2 Comments
2 Likes
Statistics
Notes
  • http://www.sendspace.com/file/8kn03w
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • http://www.sendspace.com/file/8kn03w
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
1,333
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
9
Comments
2
Likes
2
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Webセキュア コーディングの基本 on 2013-09-15 13:00-13:50JST at PyCON APAC 2013 Room A0765 (Ja2) by OCHIAI, Gouji (@gjo)
  • 2. Abstract • セキュアコーディングとは何か? • Webシステムにおけるセキュリティ • コード解説中心 • 基本的な内容しかやりません • 「お前が言うな」は重々承知
  • 3. お前誰よ? • 落合 豪史 @gjo • 一応職業プログラマー セキュリティの専門家 とかではないです • twitter.com/gjo github.com/gjo
  • 4. 本セッションおよび本稿の内 容は私個人の見解であり、私 の所属する企業、団体等とは 一切の関わりはありません。
  • 5. TheYear of Python
  • 6. Year of the Dragon
  • 7. 心を入れ替えるよ その方法を教えてくれ
  • 8. セキュアコーディングとは 何か? • 脆弱性に対して堅固な コーディング • 脆弱性? • 堅固? http://www.flickr.com/photos/nene9/4444657449/
  • 9. セキュアな状態? • 一般的にセキュリティ は非機能要件 • 「セキュリティが保た れていること」 • 実装に依存する要件 • 実装に依存しない要件 http://www.flickr.com/photos/jermainejustice/3352100979/
  • 10. 実装に依存しない セキュリティ要件 • 「必要でない情報」を • 「必要でない対象」に • 「流通しない」
  • 11. 実装に依存しない セキュリティ要件 (Cont.) • 「必要な情報」を • 「必要な対象」に • 「流通する」 • 一般的な機能要件の逆
  • 12. 相互作用を持たないアクターに ユースケースが提供されないこと
  • 13. 情報?対象?流通? • 概念データモデリングの段階である程度明らかに なっているはず • システムの外側に何をみせるか? • プログラムは何を永続化するか? • 平文のパスワードとか • クレジットカードのセキュリティコードとか
  • 14. 実装に依存する セキュリティ要件 • 攻撃手法が刻々編み出 される • 既知の攻撃方法につい て対処されている、と いうことしか証明でき ない http://www.flickr.com/photos/danorth1/315489224/
  • 15. 脆弱性は時系列上で 固定ではない
  • 16. 堅固? • 現時点で既知の脆弱性が存在しない、 ではセキュアコーディング足り得ない • 脆弱性のあるコードが存在しないこと が自明な状態であることが、容易に測 定出来ること • 考えたら負け
  • 17. 考えたら負け Don’t Think...Feel!
  • 18. 考えたら負け • 脆弱性有無の計測は何度も行われるこ とになる • その都度、考えなければ脆弱性有無を 判定できないものは「堅固」ではない
  • 19. 安全性を型に嵌める いちいち考えないために
  • 20. このコードは セキュアですか? https://gist.github.com/gjo/6473557
  • 21. --- is_this_safe.py2013-09-14 17:42:19.000000000 +0900 +++ is_this_safe2.py 2013-09-13 21:07:16.000000000 +0900 @@ -40,8 +40,12 @@ sql = 'SELECT {} FROM page'.format(','.join(COLUMNS)) params = [] if None not in (field, value): + if field not in COLUMNS: + raise NotFound(field) sql += ' WHERE {} LIKE ?'.format(field) - params.append(value) + params.append('%{}%'.format( + value.replace(r'', r').replace(r'%', r'%').replace(r'_', r'_')) + ) cursor = db.cursor() cursor.execute(sql, params) pages = [dict(zip(COLUMNS, row)) for row in cursor] @@ -62,7 +66,7 @@ <tr><td>No maches. {% endfor %} </table> -""") +""", autoescape=True) return template.render(context) if __name__ == '__main__': - app.run(debug=True) + app.run()
  • 22. プログラム内部に 起因する脆弱性 • メモリ管理、ランタイム関係 • バッファオーバーフロー • 不正なシステム特権取得など • 自爆 • デバッグモード、メンテナンスモード • エラーメッセージ
  • 23. プログラムの外部 インタフェースに起因する脆弱性 • HTTP Request, HTTP Response • Database • MessageQueue, Cache, SMTP • ...etc
  • 24. 古式ゆかしき何とやら • SQL Injection • Directory Traversal OS Command Injection • Response Header Injection Response Header Spliting E-mail Header Injection
  • 25. SQL Injection def some_view(req): sql = 'SELECT * FROM {}'.format( req.GET['some_param'], ) cursor = req.db.cursor() cursor.execute(sql) for row in cursor: some_action(row)
  • 26. Directory Traversal def some_view(req, filename): path = os.path.join(ROOT, filename) content = open(path).read() resp = Response( content=content, content_type='', ) return resp
  • 27. OS Command Injection def some_view(req, cmdopt): resp = Response(content_type='') subprocess.call( 'some_command ' + cmdopt, shell=True, stdout=resp, stderr=subprocess.STDOUT, ) return resp
  • 28. Response Header Injection Response Header Spliting HTTP/1.0 200 OK Content-Type: text/html Date: Sun, 15 Sep 2013 04:00:00 GMT Set-Cookie: HOGEHOGE; expires=Sun, 15-Jul-2013 15:00:00 GMT Connection: Close <html> ... </html>
  • 29. E-Mail Header Injection Date: Sun, 15 Sep 2013 04:00:00 GMT From: "FOO" <foo@example.org> To: "BAR" <bar@example.org> Subject: Hello BAR: Thank you for your registration Hi all, foo bar baz .
  • 30. 共通する危険性 • テキストベースのプロトコル • 構造が存在する • 構造を実現するためにメタキャラクタが 存在する • メタキャラクタを使用することで、 誤動作を誘発する
  • 31. 解法 • 入力のバリデーション • 出力のエスケープ
  • 32. 入力のバリデーション • "すべての"入力を検査すべき • QueryString, FormDataはライブラリが 充実している • それゆえPATH_INFOを忘れがち • Cookie他ヘッダーも忘れがち
  • 33. 出力のエスケープ • 出力先毎に異なるエスケープルール • HTTPレスポンス • ヘッダー: Cookie, Content-Type, Location etc... • 本文: HTML, JSON/JSONP, CSV, etc... • インタフェース • 永続化等: データベース, ファイルシステム, キャッシュ • 外部接続: SMTP,Web-API Request, etc...
  • 34. なぜ両方必要か? AppINPUT OUTPUTValidate Escape 入力をAppで使用できる形式に (Byte strem to Python type) OUTPUTをプロトコルに合わせる (Python type to Byte strem)
  • 35. つまらない結論を
  • 36. フレームワークの ルールに従って コーディング しましょう
  • 37. なぜ? • ひとつの問題を解決する手法は ひとつであるべき • 同じことを何度も書かない
  • 38. どこかで見たような •Zen of Python •DRY原則
  • 39. チュートリアルの罠 • https://gist.github.com/gjo/6558690 • https://gist.github.com/gjo/6558691
  • 40. # -*- coding: utf-8 -*- # http://flask.pocoo.org/docs/quickstart/#a-minimal-application # から # http://flask.pocoo.org/docs/quickstart/#variable-rules # までを写経していくと from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'Index Page' @app.route('/hello') def hello(): return 'Hello World' @app.route('/user/<username>') def show_user_profile(username): # show the user profile for that user return 'User %s' % username if __name__ == '__main__': app.run()
  • 41. # -*- coding: utf-8 -*- # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/ # から抜粋 from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): return Response('Hello %(name)s!' % request.matchdict) if __name__ == '__main__': config = Configurator() config.add_route('hello', '/hello/{name}') config.add_view(hello_world, route_name='hello') app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever()
  • 42. フレームワークの選択 • 必要な外部インタフェースが実装され ているか? • フレームワークへのアドオン追加 (新規作成 or グルーコードのみ作成) • 個別のコードでバリデートやエスケー プを実装しない
  • 43. では本題
  • 44. Against SQL Injection • SQLを動的に組み立てるな ORM使え • どうしても必要であれば、置換箇所は完 全一致で確認して、特殊文字を追い出せ • Prepared Statementの場合も、値の箇所 で評価される特殊文字はハンドルしろ
  • 45. SQL LIKE or REGEX # LIKEのエスケープはORMに任せる query.filter(MyModel.myfield.endswith( myparam, )) # REGEXのエスケープはまともに書ける # レベルを超えてしまう。。。
  • 46. Against Directory Traversal def safe_path(*args): return os.path.join( [PRE_DEFINED_ROOT] + [os.path.basename(p) for p in args]) def some_view(req, module_, file_): path = safe_path(module_, file_)
  • 47. Response Header Injection Response Header Spliting • WSGIサーバーでのResponseHeaderの扱 いは単なるByte (!Unicode) http://www.python.org/dev/peps/pep-3333/#the-start-response-callable http://www.python.org/dev/peps/pep-3333/#unicode-issues
  • 48. • フレームワークごとで動作が違う • django (convert) https://docs.djangoproject.com/en/1.5/ref/request-response/#setting-headers • flask (Exception on NL) http://flask.pocoo.org/docs/quickstart/#about-responses • pyramid (Exception on NL in Key) http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/api/ response.html#pyramid.response.Response.headers
  • 49. • Cookieはたいてい特別扱いだが、動作そ のものは、通常のヘッダの場合とほぼ同 じ • django https://docs.djangoproject.com/en/1.5/ref/request-response/#django.http.HttpResponse.set_cookie • flask http://flask.pocoo.org/docs/quickstart/#cookies • pyramid http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/api/ response.html#pyramid.response.Response.set_cookie
  • 50. E-mail Header Injection • 標準ライブラリemailでも問題なくエス ケープを捌ける http://docs.python.jp/2/library/email.header.html#email.header.Header
  • 51. (おまけ) 流通しない情報は 取得しない
  • 52. Script Injection 大事なものを忘れていないかい?
  • 53. Script Injection <html> {{ unchecked_var|safe }} </html>
  • 54. Script Injection <script> var x = {{ unchecked }}; </script>
  • 55. Script Injection <style> .some-class { color: {{ unchecked }}; } </style>
  • 56. Script Injection <script src="{{ unchecked }}"> </script>
  • 57. スクリプト実行が問題にな るのは何故か • DOMを操作できる • 新しくHTTP Requestを発生させられる • iframe, img, link, script • イベントを発生させられる • a.click, form.submit
  • 58. Some Server Attack Servers Browser Some Document Attack Documents
  • 59. 何が問題か • 同意していないリクエストが送信され る • パーソナライズされている情報が外部 に送信される
  • 60. 問題が複雑になってしまう 背景 • クライアントはサーバーを信用してはならない • クライアントはサーバーを信用せざるを得ない • サーバーはクライアントを信用してはならない • サーバーはクライアントを信用せざるを得ない
  • 61. クライアントはサーバーを 信用してはならない • 正しいサーバーに接続しているのか • サーバーに送信した情報は妥当に取り 扱われているのか • サーバーから受信した情報は信用に足 りるのか?
  • 62. クライアントはサーバーを 信用せざるを得ない • ひとまず正しいサーバーに接続してい ると仮定するに足る手法を使う • ひとまずプライバシーポリシー等のデ ータハンドリングを信じる • ひとまずレスポンスは正しいと信じる
  • 63. サーバーはクライアントを 信用してはならない • 接続元クライアントが正しいユーザー であるか見分けることは困難 • クライアントから受信した情報が妥当 であるか保証できない • クライアントに送信した情報が適切に 扱われるか保証できない
  • 64. サーバーはクライアントを 信用せざるを得ない • ひとまず接続元が悪意あるユーザーでない ことを証明するに足るチェックを行う • ひとまずクライアントから受信した入力に 虚偽はないと信じるに足るチェックを行う • ひとまずレスポンスデータが想定しない外 部に流出していないことを信じる
  • 65. 送信してしまったデータの来 方行末を保証するものはない • クライアントはサーバーのセキュリテ ィが保たれていることを信じるしかな い • サーバーはクライアントのセキュリテ ィが保たれていることを信じるしかな い
  • 66. Same Origin Policy The Great Wall
  • 67. Same Origin Policy • “scheme://host:port” の組み合わせが一致 するものを信じる • Ajax Requestの範囲を制限する • (類似の話題: 1st-party Cookie)
  • 68. [横道] Cookie • 適切な範囲のCookieか domain, path, secure, httpOnly, expire • (セッション値がhttpOnlyでないのは...) • 3rd-Party Cookie • DNT, EU Cookie法
  • 69. Browsing Context • (超省略形) JavaScriptが、どのページで動 作するか • 通常は表示しているページ • script要素で読み込んだ外部Originの JavaScriptも、表示しているページの Contextで動作する
  • 70. Browsing Context (Cont.) • iframe配下のdocumentは別のContext • Context間でのデータ流通にもSame Origin Policyが適用される
  • 71. 本当に信じていいの? • Ajax Requestにしか作用しない • iframe, img, link, script要素の挿入による Requestは送信される • hostsファイル汚染/DNS汚染 • 中間のネットワーク汚染 [New!]
  • 72. [横道] Content Security Policy • HTTP Response Headerで指定 • サーバーが返却するリソースに何を使 えるかを厳密に指定できる • 対応ブラウザーでないと効果なし (Firefox-23, Chrome-25, IE-10 (experimental))
  • 73. モダンな何とやら • XSS (cross site scripting) • CSRF (cross site request forgery) • Click-jacking • Mitigation, BEAST, CRIME, BREACH
  • 74. XSS • Script injection等によって攻撃用のJavaScriptをscript 要素で挿入する • 挿入手段による多くのバリエーション • HTTP Response Header Spliting • Response Content-Type誤認バグの利用 • script要素の代わりにimg要素やlink要素を利用 • とにかく意図しないScript実行を抑止するしかない
  • 75. CSRF • 直接POST Requestを送りつける • 通常はXSSとの併用で踏み台ページを経由する • すべてのFormにtokenパラメータを付与し、同 時にCookieやSessionでもtokenを保持してPOST 受付時に、外部から割り込んだPOSTを抑止す る
  • 76. Click-jacking • iframe等を利用して攻撃対象ページを操作する • 通常はSame Origin Policyによって別Contextの Documentは操作できない • 攻撃対象ページにXSS脆弱性があると、踏み台 ページのSameOriginでiframeが作成されやりた い放題に
  • 77. BEAST, CRIME, BREACH • SSL保護下にあるデータを総当り的な計 算を使用して盗む • ルーター等でのパケット観測が必要 (カンファレンスの無線LANとか) • 総当り的計算を行うためのRequestを XSSに生成する
  • 78. モダンな何とやら (再訪) • モダンな攻撃の多くはXSSとの組み合わせ • HTTP Response以外のインタフェースへ のエスケープは多くの場合対処済み • HTTP Response以外では攻撃するための シーケンスが長くなりがち • 今更SQL injectionとか恥ずかしいだけ
  • 79. まとめ • 日々新しい攻撃方法を考えている人がいます • 多くは既知の攻撃方法の巧妙な組み合わせ にエッセンスを加えたものです • セキュアであるということは、時系列にお けるスナップショットです • よって何度も確認するハメになります
  • 80. 間違えようのないやり方が ひとつだけあるのがいいね ― Zen of Python
  • 81. EOF