Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Code Reliability

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Code Reliability

Security.any #08 目指しているセキュリティLTの登壇スライドです。

https://security-any.connpass.com/event/377000/

Avatar for ryoAccount

ryoAccount

January 30, 2026

More Decks by ryoAccount

Other Decks in Programming

Transcript

  1. RYO Hi!!👋 👨 SRE / Security Engineer / 説得係 🏢

    某人材系企業にて転職支援・案件紹介サービスの開発に従事 📖 主にAWS環境のセキュリティ品質の改善や雑務・雑用を担当 💡 好きな技術は 🚗 趣味はドライブ・ランニング・Webサイト構築・英語学習など... 👨‍👩‍👧‍👦 でも子供が生まれてからは自分の時間をあまり持ててないのが悩み...
  2. Question 1 次のコードで 🎉 success!! を出力させるには、 question() に渡す文字列は何でしょう? "A" 以外にも正解があります。

    const question = (str: string) => { if ("A" === str) { console.log("🎉 success!!") } else { console.log("❌ failed!!") } } question(?) ❌ failed!!
  3. Unicode Normalization Attacks Unicode正規化攻撃(Unicode Normalization Attack)はUnicodeを巧妙に使い分けて、アプリケーションの文字列比較 やフィルタリング処理をすり抜けて不正な入力を通す手法です。 タグの有無を判定するバリデーションチェックを例に、脆弱な実装と適切な実装を比べてみます。 // 改善後

    const validation = (str: string) => { // 正規化 const s = str.normalize("NFKC") // s = "<script>" // < と > を含む文字列はNG とするチェック const pattern: RegExp = /[<>]/; if (pattern.test(s)) { throw new Error("❌ failed!!") } else { console.log("🎉 success!!") } // ... }; validation("\uFE64" + "script" + "\uFE65") 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  4. Question 2 次のコードで 🎉 success!! を出力させるには、 /* add 1 code

    here */ の部分に何と実装すればいいでしょうか? const question = () => { const target = {} if (target.isAdmin) { console.log("🎉 success!!") } else { console.log("❌ Failed!!") } } /* add 1 code here */ question() ❌ Failed!!
  5. Answer Object.prototype.isAdmin = true 、 ({}).__proto__.isAdmin = true 、 Object.defineProperty(Object.prototype,

    "isAdmin", { value: true }) などが正解。 オブジェクトはプロパティに目的のキーがない場合、親である Object.prototype を参照します。 全てのオブジェクトは親を継承しているため、たとえ空オブジェクトであっても親を汚染すれば子も影響を受けます。 const answer = () => { console.log({}.isUser) }; answer() // 汚染前 Object.defineProperty(Object.prototype, "isUser", { value: true }) answer() // 汚染後 undefined true
  6. Prototype Pollution URLのクエリパラメータを解釈する際に、不正にブラケット記法が送られてきた場合、プロトタイプ汚染に繋がるリスクがある。 import { qs } from "qs"; const

    query = "?name=test3&__proto__[isAdmin]=true"; // 汚染されるケース const parsed = qs.parse(query) console.log(parsed) // { name: 'test3' } console.log({}.isAdmin) // true 1 2 3 4 5 6 7
  7. Monkey Patch モンキーパッチ(Monkey Patch)は、プログラムをその時その場の実行範囲内で拡張または修正するというテクニックです。(こ れ自体は脆弱性ではない。) ただ、使いすぎるとコードの予測が困難で保守性を低下させるリスクがあり、Node の セキュリティのベストプラクティス でも非推奨 となっている。

    プロトタイプ汚染を防ぐには、 🙅‍♂️ オブジェクトをマージする際は、 __proto__ 、 constructor 、 prototype の利用を防止する 🛠 Object.create(null) で親を持たないオブジェクトを生成する 🗝️ Object.freeze(Object.prototype) で prototype 自体をロックする(効果絶大だが破壊的副作用も大き い) などの方法がある。 Array.prototype.push = function (item) { // overriding the global [].push };
  8. Question 3 次のコードで 🎉 success!! を出力させるには、 question() に渡す数値は何でしょう? const question

    = (num: number) => { const value = 0.1 + 0.2 if (value === num) { console.log("🎉 success!!") } else { console.log("❌ Failed!!") } } question(?) ❌ Failed!!
  9. Answer 0.30000000000000004 、 0.300000000000000039 、 0.3000000000000000444 などが正解。 算数の世界では 0.1 +

    0.2 は 0.3 ですが、ほとんどのプログラミング言語が採用している IEEE 754(浮動小数点数) の世界で は話が変わります。 世の中には、浮動小数点数の仕組みを悪用した攻撃や脆弱性がある。 const sum = 0.1 + 0.2; console.log(sum === 0.30000000000000004) console.log(sum === 0.3000000000000000444) true true
  10. Salami Slicing Attack サラミ法(Salami Slicing Attack)は、気づかれない程 度の少額を組織的に繰り返し抜き取り、最終的に多額の 金銭を不正に得る手口です。 浮動小数点の悪用は主に丸め誤差を利用して行われま す。

    金融取引での利息計算などで発生する僅かな端数 (例:0.0001円)を内部の攻撃者が隠し口座に振り込 むように実装する。 何千/何万もの取引が繰り返されると最終的に攻撃者の 口座には巨額の資金が蓄積されます。 Subnormal Number 非正規化数(Subnormal Number)は、ゼロに極めて近いがゼロではない表現可能な最小の数よりも小さい数のことです。 未満の値が該当する。 演算に使われると通常の数値に比べて処理時間が大幅に長くなる傾向があります。 非正規化数は浮動小数点の一部であり、これ自体は脆弱性ではないがパフォーマンス低下を招く可能性があります。 浮動小数点数による不具合を回避するには、 📚 decimal.js 、 bignumber.js などのライブラリの活用 ⌛️ 微細な誤差であれば、Number.EPSILONを使って浮動小数点数の等価性を検証する Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON // true などが有効。 2.225 × 10−308
  11. Question 4 次のコードで 🎉 success!! を出力させるには、 /* add 1 code

    here */ の部分に何と実装すればいいでしょうか? const REG_CHECK = /test/g const question = (str: string) => { if (str === "test" && !REG_CHECK.test(str)) { console.log("🎉 success!!") } else { console.log("❌ Failed!!") } } question("test") /* add 1 code here */ ❌ Failed!!
  12. Answer question("test") が正解。 これは実装されている正規表現に脆弱性があり、呼び出す度に結果が変わります。 正規表現は使いこなせば便利ですが、バグや脆弱性の原因にもなりやすい。 const REG_CHECK = /test/g const

    question = (str: string) => { if (str === "test" && !REG_CHECK.test(str)) { console.log("🎉 success!!") } else { console.log("❌ Failed!!") } } question("test") question("test") ❌ Failed!! 🎉 success!!
  13. g const REG_CHECK = /test/g のように「g」を付けると、ステートフルな正規表現となってしまう。 具体的には lastIndex という「照合を開始する位置」をこっそり保持することになります。 const

    REG_CHECK = /test/g const str = "test for test and test" REG_CHECK.test(str) console.log(REG_CHECK.lastIndex) // 4 REG_CHECK.test(str) console.log(REG_CHECK.lastIndex) // 13 REG_CHECK.test(str) console.log(REG_CHECK.lastIndex) // 22 // 4 回目: REG_CHECK.test(str) // 22 文字目から検索する -> "" console.log(REG_CHECK.lastIndex) // 0 (ここでfalse となる) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  14. Regular expression Denial of Service (ReDoS) ReDoS(レドス)は正規表現の処理の仕組みを悪用し、計算リソースを大量に消費させてサービス停止や遅延を引き起こす攻 撃です。 主に正規表現にマッチしない入力をされた場合に発生し、再度ひとつ戻って文字を照合するというバックトラックの仕組みを悪用 します。

    const REG_CHECK = /^(([a-zA-Z0-9])+)+$/ // 計算時間を計測するサンプル const example = (str: string) => { const start = performance.now() REG_CHECK.test(str) const end = performance.now() console.log(`${(end - start).toFixed(2)} ms`) } example("abcdef123456@") // 文字を増やすと計算時間が増える 1.20 ms
  15. Regular expression Denial of Service (ReDoS) /^(([a-zA-Z0-9])+)+$/ のように、パッと見ただけでは問題があるかどうかわかりにくい。 正規表現を悪用した攻撃を防ぐには、 🙅‍♂️

    /(a+)+$/ や /(a|b+)+$/ のような「繰り返しの中に繰り返し」がある構造を避ける の計算量を招く恐れがある ✏️ 入力可能な文字数を制限する 🛠️ node-re2などの正規表現用のライブラリを活用する などの方法がある。 O(2 ) n