21.6K Views
March 13, 21
スライド概要
2020-07015 OWASP Sendai ミーティング資料
2020-07-15 OWASP Sendai Node.js の色々 OWASP Kansai board member はせがわようすけ
長谷川陽介 (はせがわようすけ) (株)セキュアスカイ・テクノロジー 取締役CTO [email protected] https://utf-8/jp/ 千葉大学 非常勤講師 OWASP Kansai ボードメンバー OWASP Japan ボードメンバー CODE BLUEカンファレンス レビューボードメンバー
OWASP Kansai Chapter 自分たちの直面するWebセキュリティの問題を 自分たちの手で解決したい! 日本で2番目の OWASP Local Chapter Webセキュリティの悩み事を気楽に相談し情報共有できる場 スキル、役職、業種、国籍、性別、年齢に関係なし vol.16 OWASP Kansai 森田 智彦 代表 | 地域のキーパーソンに聞く、経営課題としての セキュリティ | 2020年サイバーセキュリティ月間企画 (近畿経済産業局) https://www.kansai.meti.go.jp/2-7it/k-cybersecurity-network/interview2020/kp16.html OWASP Sendai / 2020-07-15 t #owasp_sendai
OWASP Kansaiの活動 OWASP API Security Top 10の翻訳 たいがいの認証メカニズムは正しく 実装されてへんので、こすい奴は認 証トークンをパクったり、実装の欠陥 を悪用して他のユーザーIDを一時的 または恒久的に推測したりしよんねん。 OWASP Sendai / 2020-07-15 t #owasp_sendai
今日のテーマ 「Node.jsなWebアプリのセキュリティ」 OWASP Sendai / 2020-07-15 t #owasp_sendai
Node.jsなWebアプリケーション V8 JavaScriptエンジンを採用したサーバーサイドのJS環境 JSは(スクリプト言語としては)速い [要出典] 非同期イベント駆動 ファイルやネットワークの入出力が非同期に行われる Webアプリとしては多数のクライアントを効率的に処理可能 リクエストごとにプロセスやスレッドが生成されない 少し前に話題になったC10K問題へのひとつの解答 OWASP Sendai / 2020-07-15 t #owasp_sendai
Node.jsなWebアプリのセキュリティ OWASP Nodejs Security cheat sheet https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html 他の言語やフレームワーク同様 にNode.js製アプリケーションも 増加しています。[要出典] ●Java ● PHP ● Ruby ● nodejs from Google Trends, 5years, Worldwide OWASP Sendai / 2020-07-15 t #owasp_sendai
OWASP Nodejs Security cheat sheet アプリケーションセキュリティ 非同期処理はPromiseでフラットに書こう リクエストのサイズを制限しよう イベントループのブロックを避けよう 入力の検証をきちんとしよう 出力のエスケープをきちんとしよう イベントループでネットワーク帯域も監視し よう 総当たり攻撃に備えよう CSRFトークンを使おう 不要なURLルーティングを消しておこう HTTPパラメータ汚染に備えよう 必要な情報のみを返すようにしよう Objectのプロパティ記述子を活用しよう アクセスコントロールリストを使おう エラーと例外のハンドリング uncaughtExceptionのハンドリング EventEmitter使用時はエラーを確認する 非同期呼び出しのエラーを捕捉する サーバーのセキュリティ Cookieの属性を適切に セキュリティ関連のHTTPヘッダーを適宜つけ よう プラットフォームのセキュリティ パッケージの更新を維持しよう 危険な関数を使わないようにしよう 危険な正規表現を避けよう セキュリティ検査ツールを定期的にかけよう Strictモードを使おう 一般的なセキュリティ原則を守ろう https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html OWASP Sendai / 2020-07-15 t #owasp_sendai
OWASP Nodejs Security cheat sheet アプリケーションセキュリティ 非同期処理はPromiseでフラットに書こう リクエストのサイズを制限しよう イベントループのブロックを避けよう 入力の検証をきちんとしよう 出力のエスケープをきちんとしよう イベントループでネットワーク帯域も監視し よう 総当たり攻撃に備えよう CSRFトークンを使おう 不要なURLルーティングを消しておこう HTTPパラメータ汚染に備えよう 必要な情報のみを返すようにしよう Objectのプロパティ記述子を活用しよう アクセスコントロールリストを使おう エラーと例外のハンドリング uncaughtExceptionのハンドリング EventEmitter使用時はエラーを確認する 非同期呼び出しのエラーを捕捉する サーバーのセキュリティ Cookieの属性を適切に セキュリティ関連のHTTPヘッダーを適宜つけ よう プラットフォームのセキュリティ パッケージの更新を維持しよう 危険な関数を使わないようにしよう 危険な正規表現を避けよう セキュリティ検査ツールを定期的にかけよう Strictモードを使おう 一般的なセキュリティ原則を守ろう https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html OWASP Sendai / 2020-07-15 t #owasp_sendai
イベントループのブロックを避けよう OWASP Sendai / 2020-07-15 t #owasp_sendai
イベントループのブロック Node.jsは非同期型のイベント駆動 イベントループにてイベントの発生を継 続的に監視 I/Oなどは非同期に実行される イベントループが停止するとアプリ ケーション全体が止まる イベントループを止めないよう各処理は 速やかに処理を終えるか非同期に実行 する必要がある OWASP Sendai / 2020-07-15 Node.js イベントループ libuv / OS 出力よろしく! まかせろ! 入力きた? きたよ! コールバック関数 出力終わった? まだだよ! t #owasp_sendai
イベントループのブロック イベントループが止まるとアプリケー ション全体が止まる CPUのみで行う処理などは同期的に実 行され他のイベントが実行されない 膨大な計算処理、時間のかかる正規表 現、巨大な配列の処理、JSON.parseな ど 攻撃者が意図的にそのような状況を 作り出すことができるとDoSを発生さ せられる OWASP Sendai / 2020-07-15 Node.js イベントループ libuv / OS 出力よろしく! まかせろ! 入力きた? きたよ! コールバック関数 出力終わった? まだだよ! t #owasp_sendai
イベントループのブロック - ReDoS 正規表現によるDoS (ReDoS) 正規表現のパターンによっては内部的に膨大な照合処理が行われ る const re = /([a-z]+)+$/; re.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!'); //秒単位で処理時間がかかる https://gist.github.com/watson/7682ee718b9cf4aa2a5a605a3fb4a552 より 意図的にこの状態を攻撃者が作り出すことでパフォーマンスが低下 する OWASP Sendai / 2020-07-15 t #owasp_sendai
イベントループのブロック - ReDoS 対策 量指定子の回数を制限する 「+」や 「*」 ではなく 「{0,20}」 など safe-regexモジュールなどを用いて事前にパターンの検査を行う 正規表現を使わなくていい箇所では使わない String.prototype.includes や String.prototype.startWith などを 使用 OWASP Sendai / 2020-07-15 t #owasp_sendai
イベントループのブロック - JSON.parse JSON.parseは同期的に処理され、パース中は他のイベント処理は 実行されない 巨大な文字列のJSON.parseはアプリケーション全体をブロックさ せる可能性がある const text = req.body.param const obj = JSON.parse(text) OWASP Sendai / 2020-07-15 // パースが完了するまで処理がブロック t #owasp_sendai
イベントループのブロック - JSON.parse 対策 JSON.parseの前にテキストのサイズを確認 1 2 3 4 5 const text = req.body.param // サイズが4k以下の時だけJSON.parseする if (typeof text === 'string' && text.length < 4096) { const obj = JSON.parse(text) } asyncなJSONパーサーの導入 使ったことがないので何とも言えず… 他のライブラリで内部的にJSON.parseが呼び出されている場合には対応 できない OWASP Sendai / 2020-07-15 t #owasp_sendai
イベントループのブロック - JSON.stringify JSON.stringifyでも同じことはあり得る 巨大な配列を含むJSONをシリアライズするときにブロック等 参考:https://techblog.yahoo.co.jp/advent-calendar-2018/goodbyemym/#uopoo 配列を含むJSONをstringifyするときは事前に配列の長さを確認 1 function foo (obj) { 2 let text 3 // 配列の長さが1k以下の時だけJSON.stringifyする 4 if (obj.items instanceof Array && obj.items.length < 1024) { 5 text = JSON.stringify(obj) 6 } 7 ... 8 } OWASP Sendai / 2020-07-15 t #owasp_sendai
OWASP Nodejs Security cheat sheet アプリケーションセキュリティ 非同期処理はPromiseでフラットに書こう リクエストのサイズを制限しよう イベントループのブロックを避けよう 入力の検証をきちんとしよう 出力のエスケープをきちんとしよう イベントループでネットワーク帯域も監視し よう 総当たり攻撃に備えよう CSRFトークンを使おう 不要なURLルーティングを消しておこう HTTPパラメータ汚染に備えよう 必要な情報のみを返すようにしよう Objectのプロパティ記述子を活用しよう アクセスコントロールリストを使おう エラーと例外のハンドリング uncaughtExceptionのハンドリング EventEmitter使用時はエラーを確認する 非同期呼び出しのエラーを捕捉する サーバーのセキュリティ Cookieの属性を適切に セキュリティ関連のHTTPヘッダーを適宜つけ よう プラットフォームのセキュリティ パッケージの更新を維持しよう 危険な関数を使わないようにしよう 危険な正規表現を避けよう セキュリティ検査ツールを定期的にかけよう Strictモードを使おう 一般的なセキュリティ原則を守ろう https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html OWASP Sendai / 2020-07-15 t #owasp_sendai
Objectのプロパティ記述子を 活用しよう OWASP Sendai / 2020-07-15 t #owasp_sendai
Objectのプロパティ記述子 生成したオブジェクトのプロパティの書き込みや列挙を禁止できる Object.defineProperty / Object.defineProperties 意図しないオブジェクトの書き替えを阻止 strictモードと組み合わせることで早い段階でバグを検出できる 1 2 3 4 5 6 const user = { name: 'hasegawa' } console.log(user.name) // 'hasegawa' user.name = 'yosuke' console.log(user.name) // 'yosuke' OWASP Sendai / 2020-07-15 1 2 3 4 5 6 7 8 9 'use strict' const user = {} Object.defineProperty(user, 'name', { value: 'hasegawa', writeable: false }) console.log(user.name) // 'hasegawa' user.name = 'yosuke' // 例外発生 console.log(user.name) t #owasp_sendai
Objectのプロパティ記述子 既存オブジェクトのプロパティ変更も禁止できる Object.preventExtensions / Object.seal / Object.freeze 1 2 3 4 5 6 7 'use strict' const user = { name: 'hasegawa' }) Object.seal(user) user.name = 'yosuke' // 既存のプロパティの値は変更可能 user.mail = '[email protected]' // プロパティ追加で例外発生 1 2 3 4 5 6 'use strict' const user = { name: 'hasegawa' }) Object.freeze(user) user.name = 'yosuke' OWASP Sendai / 2020-07-15 // 既存のプロパティの値変更で例外 t #owasp_sendai
OWASP Nodejs Security cheat sheet アプリケーションセキュリティ 非同期処理はPromiseでフラットに書こう リクエストのサイズを制限しよう イベントループのブロックを避けよう 入力の検証をきちんとしよう 出力のエスケープをきちんとしよう イベントループでネットワーク帯域も監視し よう 総当たり攻撃に備えよう CSRFトークンを使おう 不要なURLルーティングを消しておこう HTTPパラメータ汚染に備えよう 必要な情報のみを返すようにしよう Objectのプロパティ記述子を活用しよう アクセスコントロールリストを使おう エラーと例外のハンドリング uncaughtExceptionのハンドリング EventEmitter使用時はエラーを確認する 非同期呼び出しのエラーを捕捉する サーバーのセキュリティ Cookieの属性を適切に セキュリティ関連のHTTPヘッダーを適宜つけ よう プラットフォームのセキュリティ パッケージの更新を維持しよう 危険な関数を使わないようにしよう 危険な正規表現を避けよう セキュリティ検査ツールを定期的にかけよう Strictモードを使おう 一般的なセキュリティ原則を守ろう https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html OWASP Sendai / 2020-07-15 t #owasp_sendai
まとめ OWASPにはNode.jsアプリのチートシートがある セキュリティだけでなく堅牢なコードにするための項目も多い イベントループのブロックがNode.jsの問題としては大きい ReDoSはOSSの脆弱性としてもよく見つかっている チートシートに載ってない注意点もけっこうある NoSQLインジェクションとか(Node.jsと組み合わせてよく使われる) const / let 、Strictモードでコードを堅牢にする eslintでありがちなミスを減らす TypeScriptで型を厳密にする等 OWASP Sendai / 2020-07-15 t #owasp_sendai
One more thing ...
prototype汚染 OWASP Sendai / 2020-07-15 t #owasp_sendai
prototype汚染 __proto__経由でObject.prototypeが汚染されてしまう問題 prototype.js時代のprototype汚染とは異なる あれは意図的にObject.prototypeに便利メソッドを追加しているが迷惑だ という問題 攻撃者が__proto__プロパティを宣言することでグローバルの Object.prototypeeを汚染する 存在しないはずのプロパティが全オブジェクトに存在してしまう OWASP Sendai / 2020-07-15 t #owasp_sendai
OWASP Sendai / 2020-07-15 t #owasp_sendai
prototype汚染 https://jovi0608.hatenablog.com/entry/2018/10/19/083725 より ① 外から来た文字列をJSON.parse 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function main (source) { const x = JSON.parse(source) // 外からきた文字列 const y = clone(x) const z = {} console.log(z.polluted) } ② そのオブジェクトを複製 ③ 空のオブジェクトを生成 ④ ここで表示されるのは? function clone(obj) { return merge({}, obj) } function merge (a, b) { for (const key in b) { if (isObject(a[key]) && isObject(b[key])) { merge(a[key], b[key]) } else { a[key] = b[key] } } return a } OWASP Sendai / 2020-07-15 オブジェクトを複製する関数 プロパティを列挙して再帰的にコピーするだけ t #owasp_sendai
prototype汚染 https://jovi0608.hatenablog.com/entry/2018/10/19/083725 より ① 外から来た文字列をJSON.parse 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function main (source) { const x = JSON.parse(source) // 外からきた文字列 const y = clone(x) const z = {} console.log(z.polluted) } function clone(obj) { return merge({}, obj) } function merge (a, b) { for (const key in b) { if (isObject(a[key]) && isObject(b[key])) { merge(a[key], b[key]) } else { a[key] = b[key] } } return a } OWASP Sendai / 2020-07-15 ② そのオブジェクトを複製 ③ 空のオブジェクトを生成 ④ ここで表示されるのは? source: '{"__proto__": {"polluted": "1"}}' 1 オブジェクトを複製する関数 プロパティを列挙して再帰的にコピーするだけ t #owasp_sendai
prototype汚染 https://jovi0608.hatenablog.com/entry/2018/10/19/083725 より {"__proto__": {"polluted": "1"}} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function main (source) { const x = JSON.parse(source) // 外からきた文字列 const y = clone(x) const z = {} console.log(z.polluted) } function clone(obj) { return merge({}, obj) } function merge (a, b) { for (const key in b) { if (isObject(a[key]) && isObject(b[key])) { merge(a[key], b[key]) } else { a[key] = b[key] } } return a } OWASP Sendai / 2020-07-15 __proto__の書き込み(複製)でグローバルな Object.prototypeが汚染される prototype経由で存在しないプロパティが存在 するかのようにふるまう valueOf()やtoString()なども書き替えられる可 能性。最悪の場合はコード実行につながる t #owasp_sendai
prototype汚染 - 対策 外部からのJSONには__proto__のように動作に影響をあたえる キーが含まれている可能性がある 外部からのJSONのキーを列挙してそのまま使用しない キー名が想定されているものか確認する 最低限、キーを列挙する際には __proto__ を除外する 1 function merge (a, b) { 2 for (const key in b) { 3 if (key === '__proto__') continue 4 if (isObject(a[key]) && isObject(b[key])) { 5 merge(a[key], b[key]) 6 } else { 7 a[key] = b[key] 8 } 9 } 10 return a 11 } OWASP Sendai / 2020-07-15 t #owasp_sendai
prototype汚染 - 他の対策方法 オブジェクトリテラル「{}」ではなくObject.create(null)を使う prototypeがnullのオブジェクトが生成される ただしprototypeチェインがたどれなくなるので、hasOwnPropertyなどが直接は 呼び出せなくなる(そもそもObject.prototype.hasOwnProperty.callするほうが いい) Object.freezeを用いてObject本体およびObject.prototypeを凍結する 副作用で動かなくなるモジュールが発生する可能性がある JSON Schemaなどを用いて入力をより厳密に検証する 入力されるJSONのキー名や型などを厳密に検証する ObjectではなくMapを使う 外部からのデータはObjectではなくMapに格納して使用する MapからObjectへコピーする際には__proto__をコピーしないよう注意が必要 参考: https://techblog.securesky-tech.com/entry/2018/10/31/ OWASP Sendai / 2020-07-15 t #owasp_sendai
質問? [email protected] @hasegawayosuke https://utf-8.jp/ OWASP Sendai / 2020-07-15 t #owasp_sendai