コース目次 / 第1章

source と sink のカタログ

Node.jsアプリでよく出会うsourceとsinkを、これまでのコースの穴と対応づけて一覧にします。コードを開く前に、この地図を頭に入れておくと読む速度が上がります。

第1章 / 全6章目安 約10分この章のゴール: 主要なsourceとsinkを、これまでの穴と結びつけて一覧できるようになる

コードを開く前に、探すべきものを先に知っておくと、読む速度がまるで違います。この章は、その地図です。

source — 入力が生まれる場所

Node.js の HTTP サーバでよく出会う source は、限られています。すべて、これまでのコースで見てきたものです。

source 例
URL のクエリ url.searchParams.get('q')
URL のパス req.url / パスの正規表現マッチ
リクエストボディ req.on('data', ...) で集めた POST の中身
ヘッダー req.headers.cookie / req.headers.origin
ファイル 外部から読み込む設定ファイルやアップロードされたファイル

sink — 脆弱性クラスごとの、危険な関数

これまでのコースで見た穴を、sink の型で整理し直します。

XSS の sink — res.end(文字列連結でHTML)、innerHTML =。エスケープを経ずに source が届くと危険。
SQLi の sink — db.prepare(文字列連結でSQL)、db.exec(…)。プレースホルダを経ずに source が届くと危険。
コマンドインジェクションの sink — exec(文字列連結でコマンド)。execFile の引数配列を経ずに source が届くと危険。
SSTI の sink — Function(…)、eval(…)、テンプレートエンジンの動的コンパイル。
パストラバーサル/SSRFの sink — fs.readFile(path.join(…))、fetch(url)。許可リストでの検証を経ずに source が届くと危険。
安全でないデシリアライズの sink — eval(‘(’ + body + ‘)’)のような、コードとして評価する復元処理。
認可の sink(少し特殊)——資源を返す res.end(…) の直前に、所有者・ロールの確認コードが無いこと自体が sink です。

読み方の型 — 逆から辿る

実務では、コードベース全体を頭から読むのは非効率です。sink を先に見つけ、そこから source へ逆にたどるのが定石です。

1. sink を grep する。exec(・eval(・innerHTML・SQL文の組み立てらしき箇所を、まず機械的に検索する。
2. そこに流れ込む変数を、上に辿る。その関数に渡されている変数が、どこから来たかを、代入元へ代入元へと遡る。
3. 途中に「無害化」があるか確かめる。エスケープ・プレースホルダ・許可リストとの照合・型検証——これらを一度でも通っていれば、安全な可能性が高い。一度も通っていなければ、source までの経路が“汚染されたまま”です。

sec-securecoding で作った4原則は、まさにこの「無害化」が何であるべきかの答えでした。カタログ(何が sink か) と 4原則(何が無害化か) を組み合わせると、初めて読むコードでも当たりをつけられます。

持ち帰る一言

探すものを、先に知っておく。 source(入力の生まれる場所)と sink(危険な関数)を一覧にしておき、sink から逆に source へ辿る。これまでの攻撃コースで見た穴は、すべてこのカタログに収まります。次の章で、実際にこの読み方を、初めて見るコードに使ってみます。

こうなっていればOK

卒業まであと4章です。

この章はまだ完了していません。