狠狠撸

狠狠撸Share a Scribd company logo
Webセキュア
コーディングの基本
on 2013-09-15 13:00-13:50JST
at PyCON APAC 2013 Room A0765 (Ja2)
by OCHIAI, Gouji (@gjo)
Abstract
? セキュアコーディングとは何か?
? Webシステムにおけるセキュリティ
? コード解説中心
? 基本的な内容しかやりません
? 「お前が言うな」は重々承知
お前誰よ?
? 落合 豪史
@gjo
? 一応職業プログラマー
セキュリティの専門家
とかではないです
? twitter.com/gjo
github.com/gjo
本セッションおよび本稿の内
容は私個人の見解であり、私
の所属する企業、団体等とは
一切の関わりはありません。
TheYear of Python
Year of the Dragon
心を入れ替えるよ
その方法を教えてくれ
セキュアコーディングとは
何か?
? 脆弱性に対して堅固な
コーディング
? 脆弱性?
? 堅固?
http://www.?ickr.com/photos/nene9/4444657449/
セキュアな状態?
? 一般的にセキュリティ
は非機能要件
? 「セキュリティが保た
れていること」
? 実装に依存する要件
? 実装に依存しない要件
http://www.?ickr.com/photos/jermainejustice/3352100979/
実装に依存しない
セキュリティ要件
? 「必要でない情報」を
? 「必要でない対象」に
? 「流通しない」
実装に依存しない
セキュリティ要件 (Cont.)
? 「必要な情報」を
? 「必要な対象」に
? 「流通する」
? 一般的な機能要件の逆
相互作用を持たないアクターに
ユースケースが提供されないこと
情報?対象?流通?
? 概念データモデリングの段階である程度明らかに
なっているはず
? システムの外側に何をみせるか?
? プログラムは何を永続化するか?
? 平文のパスワードとか
? クレジットカードのセキュリティコードとか
実装に依存する
セキュリティ要件
? 攻撃手法が刻々編み出
される
? 既知の攻撃方法につい
て対処されている、と
いうことしか証明でき
ない
http://www.?ickr.com/photos/danorth1/315489224/
脆弱性は時系列上で
固定ではない
堅固?
? 現時点で既知の脆弱性が存在しない、
ではセキュアコーディング足り得ない
? 脆弱性のあるコードが存在しないこと
が自明な状態であることが、容易に測
定出来ること
? 考えたら负け
考えたら负け
Don’t Think...Feel!
考えたら负け
? 脆弱性有無の計測は何度も行われるこ
とになる
? その都度、考えなければ脆弱性有無を
判定できないものは「堅固」ではない
安全性を型に嵌める
いちいち考えないために
このコードは
セキュアですか?
https://gist.github.com/gjo/6473557
--- 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()
プログラム内部に
起因する脆弱性
? メモリ管理、ランタイム関係
? バッファオーバーフロー
? 不正なシステム特権取得など
? 自爆
? デバッグモード、メンテナンスモード
? エラーメッセージ
プログラムの外部
インタフェースに起因する脆弱性
? HTTP Request, HTTP Response
? Database
? MessageQueue, Cache, SMTP
? ...etc
古式ゆかしき何とやら
? SQL Injection
? Directory Traversal
OS Command Injection
? Response Header Injection
Response Header Spliting
E-mail Header Injection
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)
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
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
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>
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
.
共通する危険性
? テキストベースのプロトコル
? 構造が存在する
? 構造を実現するためにメタキャラクタが
存在する
? メタキャラクタを使用することで、
誤動作を誘発する
解法
? 入力のバリデーション
? 出力のエスケープ
入力のバリデーション
? "すべての"入力を検査すべき
? QueryString, FormDataはライブラリが
充実している
? それゆえPATH_INFOを忘れがち
? Cookie他ヘッダーも忘れがち
出力のエスケープ
? 出力先毎に異なるエスケープルール
? HTTPレスポンス
? ヘッダー: Cookie, Content-Type, Location etc...
? 本文: HTML, JSON/JSONP, CSV, etc...
? インタフェース
? 永続化等: データベース, ファイルシステム, キャッシュ
? 外部接続: SMTP,Web-API Request, etc...
なぜ両方必要か?
AppINPUT OUTPUTValidate Escape
入力をAppで使用できる形式に
(Byte strem to Python type)
OUTPUTをプロトコルに合わせる
(Python type to Byte strem)
つまらない结论を
フレームワークの
ルールに従って
コーディング
しましょう
なぜ?
? ひとつの問題を解決する手法は
ひとつであるべき
? 同じことを何度も書かない
どこかで見たような
?Zen of Python
?DRY原則
チュートリアルの罠
? https://gist.github.com/gjo/6558690
? https://gist.github.com/gjo/6558691
# -*- 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()
# -*- 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()
フレームワークの選択
? 必要な外部インタフェースが実装され
ているか?
? フレームワークへのアドオン追加
(新規作成 or グルーコードのみ作成)
? 個別のコードでバリデートやエスケー
プを実装しない
では本题
Against SQL Injection
? SQLを動的に組み立てるな
ORM使え
? どうしても必要であれば、置換箇所は完
全一致で確認して、特殊文字を追い出せ
? Prepared Statementの場合も、値の箇所
で評価される特殊文字はハンドルしろ
SQL LIKE or REGEX
# LIKEのエスケープはORMに任せる
query.filter(MyModel.myfield.endswith(
myparam,
))
# REGEXのエスケープはまともに書ける
# レベルを超えてしまう。。。
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_)
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
? フレームワークごとで動作が違う
? django (convert)
https://docs.djangoproject.com/en/1.5/ref/request-response/#setting-headers
? ?ask (Exception on NL)
http://?ask.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
? Cookieはたいてい特別扱いだが、動作そ
のものは、通常のヘッダの場合とほぼ同
じ
? django
https://docs.djangoproject.com/en/1.5/ref/request-response/#django.http.HttpResponse.set_cookie
? ?ask
http://?ask.pocoo.org/docs/quickstart/#cookies
? pyramid
http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/api/
response.html#pyramid.response.Response.set_cookie
E-mail Header Injection
? 標準ライブラリemailでも問題なくエス
ケープを捌ける
http://docs.python.jp/2/library/email.header.html#email.header.Header
(おまけ)
流通しない情報は
取得しない
Script Injection
大事なものを忘れていないかい?
Script Injection
<html>
{{ unchecked_var|safe }}
</html>
Script Injection
<script>
var x = {{ unchecked }};
</script>
Script Injection
<style>
.some-class {
color: {{ unchecked }};
}
</style>
Script Injection
<script
src=/slideshow/pycon-apac-2013-web-secure-coding/26201565/"{{ unchecked }}">
</script>
スクリプト実行が問題にな
るのは何故か
? DOMを操作できる
? 新しくHTTP Requestを発生させられる
? iframe, img, link, script
? イベントを発生させられる
? a.click, form.submit
Some Server Attack Servers
Browser
Some
Document
Attack
Documents
何が問題か
? 同意していないリクエストが送信され
る
? パーソナライズされている情報が外部
に送信される
問題が複雑になってしまう
背景
? クライアントはサーバーを信用してはならない
? クライアントはサーバーを信用せざるを得ない
? サーバーはクライアントを信用してはならない
? サーバーはクライアントを信用せざるを得ない
クライアントはサーバーを
信用してはならない
? 正しいサーバーに接続しているのか
? サーバーに送信した情報は妥当に取り
扱われているのか
? サーバーから受信した情報は信用に足
りるのか?
クライアントはサーバーを
信用せざるを得ない
? ひとまず正しいサーバーに接続してい
ると仮定するに足る手法を使う
? ひとまずプライバシーポリシー等のデ
ータハンドリングを信じる
? ひとまずレスポンスは正しいと信じる
サーバーはクライアントを
信用してはならない
? 接続元クライアントが正しいユーザー
であるか見分けることは困難
? クライアントから受信した情報が妥当
であるか保証できない
? クライアントに送信した情報が適切に
扱われるか保証できない
サーバーはクライアントを
信用せざるを得ない
? ひとまず接続元が悪意あるユーザーでない
ことを証明するに足るチェックを行う
? ひとまずクライアントから受信した入力に
虚偽はないと信じるに足るチェックを行う
? ひとまずレスポンスデータが想定しない外
部に流出していないことを信じる
送信してしまったデータの来
方行末を保証するものはない
? クライアントはサーバーのセキュリテ
ィが保たれていることを信じるしかな
い
? サーバーはクライアントのセキュリテ
ィが保たれていることを信じるしかな
い
Same Origin Policy
The Great Wall
Same Origin Policy
? “scheme://host:port” の組み合わせが一致
するものを信じる
? Ajax Requestの範囲を制限する
? (類似の話題: 1st-party Cookie)
[横道] Cookie
? 適切な範囲のCookieか
domain, path, secure, httpOnly, expire
? (セッション値がhttpOnlyでないのは...)
? 3rd-Party Cookie
? DNT, EU Cookie法
Browsing Context
? (超省略形) JavaScriptが、どのページで動
作するか
? 通常は表示しているページ
? script要素で読み込んだ外部Originの
JavaScriptも、表示しているページの
Contextで動作する
Browsing Context
(Cont.)
? iframe配下のdocumentは別のContext
? Context間でのデータ流通にもSame
Origin Policyが適用される
本当に信じていいの?
? Ajax Requestにしか作用しない
? iframe, img, link, script要素の挿入による
Requestは送信される
? hostsファイル汚染/DNS汚染
? 中間のネットワーク汚染 [New!]
[横道] Content Security
Policy
? HTTP Response Headerで指定
? サーバーが返却するリソースに何を使
えるかを厳密に指定できる
? 対応ブラウザーでないと効果なし
(Firefox-23, Chrome-25, IE-10
(experimental))
モダンな何とやら
? XSS (cross site scripting)
? CSRF (cross site request forgery)
? Click-jacking
? Mitigation, BEAST, CRIME, BREACH
XSS
? Script injection等によって攻撃用のJavaScriptをscript
要素で挿入する
? 挿入手段による多くのバリエーション
? HTTP Response Header Spliting
? Response Content-Type誤認バグの利用
? script要素の代わりにimg要素やlink要素を利用
? とにかく意図しないScript実行を抑止するしかない
CSRF
? 直接POST Requestを送りつける
? 通常はXSSとの併用で踏み台ページを経由する
? すべてのFormにtokenパラメータを付与し、同
時にCookieやSessionでもtokenを保持してPOST
受付時に、外部から割り込んだPOSTを抑止す
る
Click-jacking
? iframe等を利用して攻撃対象ページを操作する
? 通常はSame Origin Policyによって別Contextの
Documentは操作できない
? 攻撃対象ページにXSS脆弱性があると、踏み台
ページのSameOriginでiframeが作成されやりた
い放題に
BEAST, CRIME, BREACH
? SSL保護下にあるデータを総当り的な計
算を使用して盗む
? ルーター等でのパケット観測が必要
(カンファレンスの無線LANとか)
? 総当り的計算を行うためのRequestを
XSSに生成する
モダンな何とやら (再訪)
? モダンな攻撃の多くはXSSとの組み合わせ
? HTTP Response以外のインタフェースへ
のエスケープは多くの場合対処済み
? HTTP Response以外では攻撃するための
シーケンスが長くなりがち
? 今更SQL injectionとか恥ずかしいだけ
まとめ
? 日々新しい攻撃方法を考えている人がいます
? 多くは既知の攻撃方法の巧妙な組み合わせ
にエッセンスを加えたものです
? セキュアであるということは、時系列にお
けるスナップショットです
? よって何度も確認するハメになります
間違えようのないやり方が
ひとつだけあるのがいいね
― Zen of Python
EOF

More Related Content

PyCon APAC 2013 Web Secure Coding