サーバー・セキュリティの用語
攻撃から守る仕組みと、使っているソフトウェアを安全に保つための言葉。
それぞれ「なぜそれがあるのか」と「つまずきやすい点」まで書いています。実際にこのサイトを作る中で出てきた具体例も入れました。
依存関係 / 環境変数 / セマンティックバージョニング / 脆弱性 / ロックファイル / EOL / XSS / CORS / CSRF / Dependabot / nonce / WAF
依存関係(いぞんかんけい)
あるプログラムが動くために必要な、他のプログラムのこと。連鎖的に増えていく。
自分のプログラムが動くために必要な、外部のプログラムのことです。そしてその外部のプログラムにも、また別の依存があります。
連鎖する
直接指定したのが10個でも、実際にインストールされるのは数百個ということが普通に起こります。自分が選んでいないものが、大量に入っている状態です。
知っておくべき性質
同じ名前のパッケージが、違うバージョンで同時に入ることがあります。AがBの1.0を要求し、CがBの2.0を要求する場合、両方を満たすために入れ子にして共存させます。
この性質のため、自分が直接指定したものを更新しても、他のパッケージが内側に抱えている古い版は残ります。「上げたのに脆弱性の警告が消えない」という現象は、たいていこれが原因です。
環境変数(かんきょうへんすう)
プログラムの外側から渡す設定値。パスワードやAPIキーをコードに書かないために使う。
プログラムの外側から渡す設定値です。接続先のURLやパスワード、APIキーなどを、コードに直接書かずに済ませるために使います。
なぜコードに書いてはいけないのか
コードはリポジトリに保存され、履歴として残り続けます。一度書いてしまうと、あとから消してもコミット履歴には残ります。公開リポジトリなら世界中から読めますし、非公開でも共有範囲が広がります。
もうひとつの利点
開発環境と本番環境で違う値を使い分けられます。同じコードのまま、接続先だけを切り替えられるわけです。
フロントエンドでの注意
Next.jsでは、NEXT_PUBLIC_ という接頭辞が付いた環境変数はブラウザに送られます。つまり誰でも読めます。秘密の値にこの接頭辞を付けてはいけません。
「環境変数だから安全」ではなく、どこまで届くのかを理解して使い分ける必要があります。
関連記事: ローカルのサイトを本番公開するまでにハマった話
セマンティックバージョニング(せまんてぃっくばーじょにんぐ)
バージョン番号を「1.2.3」の3つに分け、どこが変わったかで互換性を示す取り決め。
バージョン番号を「メジャー.マイナー.パッチ」の3つに分け、それぞれの意味を決めた取り決めです。SemVer とも書きます。
| 位置 | 上がるとき | 影響 |
|---|---|---|
| 1.2.3 パッチ | 不具合の修正 | そのまま上げてよい |
| 1.2.3 マイナー | 機能の追加 | 基本は安全 |
| 1.2.3 メジャー | 互換性のない変更 | 移行作業が必要 |
実務でどう効くのか
更新の提案が来たとき、どこの数字が変わったかを見るだけで危険度が判断できます。パッチ更新はそのまま入れてよく、メジャー更新は移行作業として扱う。ボタンの見た目は同じでも、やっていることがまったく違います。
ただしこれは取り決めであって強制ではありません。守っていないパッケージもあるので、重要なものは変更内容を確認する価値があります。
脆弱性(ぜいじゃくせい)
悪用されると被害につながる、ソフトウェアの弱点。修正版が出たら更新して塞ぐ。
ソフトウェアに含まれる「悪用されると被害につながる弱点」のことです。バグの一種ですが、特にセキュリティに関わるものを指します。
なぜ次々に見つかるのか
ソフトウェアは膨大な組み合わせで動くため、作った時点では想定できない使われ方が後から見つかります。世界中の研究者が探しているので、有名なソフトほど多く報告されます。
報告が多いことは、必ずしも品質が低いことを意味しません。見られている量が多いとも言えます。
実務での付き合い方
警告の件数に圧倒されないことが大事です。件数と、必要な作業の数は一致しません。1つのパッケージに対する報告が積み上がっているだけ、というケースがよくあります。
また、開発時にしか動かないもの(開発用の依存)は、公開されるサイトには含まれません。同じ「重要度:高」でも、緊急度は変わります。
関連記事: 公開したあとの話 — 脆弱性検知と監視の仕組みを作る
ロックファイル(ろっくふぁいる)
実際にインストールされた全パッケージのバージョンを記録したファイル。環境を再現するために使う。
package-lock.json のような、「実際に何が、どのバージョンで入ったか」を全部記録したファイルです。
なぜ必要なのか
設定ファイル(package.json)には「1.2以上」のような幅を持った指定を書きます。この幅があると、インストールする時期によって違うバージョンが入ります。
それでは「自分の環境では動くが、本番では動かない」が起こります。そこで、実際に入った正確なバージョンを記録しておき、他の環境でも同じ組み合わせを再現できるようにします。
実務での扱い
ロックファイルは必ずリポジトリに含めます。「自動生成されるファイルだから」と除外すると、環境の再現性が失われます。
また、依存の実態を調べたいときは、このファイルを見るのが確実です。入れ子になった古いバージョンも、ここには正確に記録されています。
EOL(イーオーエル)
End of Life。そのバージョンへのサポートが終了し、脆弱性が見つかっても修正されなくなること。
End of Life の略。そのバージョンの提供が終わり、以後は修正が出なくなることを指します。
なぜ重要なのか
EOLを迎えたバージョンは、新しい脆弱性が見つかっても修正されません。使い続けている限り、危険は増える一方になります。
「動いているから問題ない」と思いがちですが、動くことと安全であることは別です。
作業前に調べる価値がある
更新作業の前にEOLを確認すると、「上げる先」が判断できます。実際にこのサイトでは、手元のバージョンがすでに最終版で、上げる余地が存在しないことが分かりました。調べずに作業していたら、何も起きないコマンドを実行するところでした。
あわせて、次のバージョンのサポート期限も見ておくと、「2か月後にまた同じ作業をする」ことを避けられます。
XSS(クロスサイトスクリプティング)
サイトに悪意のあるスクリプトを埋め込み、閲覧者のブラウザで実行させる攻撃。
Cross-Site Scripting の略。投稿欄などから悪意のあるプログラムを送り込み、それを見た人のブラウザで実行させる攻撃です。
何が起きるのか
実行されると、その人のログイン情報を盗んだり、意図しない操作を行わせたりできます。攻撃されるのはサイトではなく、サイトを見に来た人という点が特徴です。
なぜ起きるのか
受け取った文字列を、そのままHTMLとして出力してしまうと起こります。<script> という文字列が、文字ではなく命令として解釈されるためです。
防ぎ方
外部から来た文字列を出力するときは、記号を「ただの文字」に変換します。これをエスケープといいます。
構造化データのようにスクリプトタグの中へ文字列を出す場合も同じで、< をユニコード表記に置き換えるといった対処が必要になります。自分で書いた文章しか入らない場所でも、塞いでおくコストは低いです。
CORS(コルス)
ブラウザが、別のドメインへの通信を既定で制限する仕組み。許可はサーバー側が出す。
Cross-Origin Resource Sharing の略。ブラウザ上のプログラムが、別のドメインのサーバーと通信することを制限する仕組みです。
なぜ制限するのか
もし自由に通信できると、悪意のあるサイトを開いただけで、あなたがログイン中の別のサイトに勝手に操作を送れてしまいます。それを防ぐため、ブラウザは既定で他ドメインへの通信をブロックします。
誰が許可を出すのか
重要な点として、許可を出すのは通信される側のサーバーです。呼び出す側でいくら書いても解決しません。サーバーが「このドメインからの通信は許可します」という応答ヘッダーを返して、初めて通ります。
そのため「自分のコードを直しても直らない」ことがよくあります。直す場所が自分の手元にないのが、この仕組みの分かりにくさです。
補足
サーバー同士の通信には、この制限はありません。ブラウザの中でだけ働くルールです。
関連記事: 書いた記事をWordPressに届けるまで — CORS・nonce・CSSの壁
CSRF(シーサーフ)
ログイン中のユーザーに、意図しない操作を実行させる攻撃。nonceなどで防ぐ。
Cross-Site Request Forgery の略。日本語では「クロスサイトリクエストフォージェリ」、俗に「リクエスト強要」とも呼ばれます。
どういう攻撃か
あなたが銀行のサイトにログインしたまま、別のタブで悪意のあるページを開いたとします。そのページが裏で「送金する」という要求を銀行に送ると、ブラウザはログイン情報を自動で付けて送ってしまいます。銀行から見れば、本人からの正規の要求に見えます。
なぜ成立するのか
原因は、ブラウザがログイン情報を自動的に付けるという仕様にあります。便利さのための仕組みが、そのまま攻撃の入口になっています。
防ぎ方
「正規の画面を開いた人にしか分からない情報」を操作に添えさせます。nonceがその代表です。外部サイトはその値を知り得ないため、偽の要求を作れなくなります。
Dependabot(ディペンダボット)
GitHubの機能。依存パッケージの脆弱性を検知し、更新のプルリクエストを自動で作る。
GitHubに組み込まれている仕組みで、使っているパッケージに脆弱性が見つかると知らせ、更新用のプルリクエストを自動で作ってくれます。
便利だが、そのまま入れてはいけない
自動で作られたプルリクエストには、危険度のまったく違うものが混ざっています。パッチ更新もあれば、メジャーバージョンを2つ跨ぐものもあります。
そしてブラウザ上でマージすると、手元で動作確認する機会が飛びます。通るかどうかは本番のデプロイが動いてから分かる、という状態になります。パッチ更新なら許容できますが、メジャー更新でこれをやるのは賭けです。
使いこなし方
バージョン番号の変化を見て、そのまま入れてよいものと、移行作業として扱うものに分けるのが基本です。後者は手元にブランチを作り、ビルドと動作確認を挟んでから入れます。
関連記事: 公開したあとの話 — 脆弱性検知と監視の仕組みを作る
nonce(ナンス)
一度きりの使い捨ての文字列。その操作が正規の画面から行われたことを確かめるために使う。
number used once の略で、一度だけ使う使い捨ての文字列のことです。WordPressをはじめ、多くのシステムで使われています。
何のためにあるのか
ログイン中のユーザーが、悪意のあるサイトを開いてしまったとします。そのサイトが「記事を削除する」という要求を裏で送ったら、ブラウザはログイン情報を自動的に付けて送ってしまいます。これが成立すると、本人の意図しない操作が実行されます。
そこで、正規の画面を開いたときにだけ発行される文字列を、操作のたびに一緒に送らせます。外部のサイトはこの文字列を知り得ないので、なりすましを防げます。
実務で出会う場面
WordPressのREST APIをプログラムから叩くとき、ログインしているだけでは足りず、nonceも必要になることがあります。「認証は通っているのに403が返る」場合、ここを疑う価値があります。
関連記事: 書いた記事をWordPressに届けるまで — CORS・nonce・CSSの壁
WAF(ワフ)
Webサイトへの通信を手前で見張り、攻撃らしいものを遮断する仕組み。
Web Application Firewall の略。サイトに届く前の段階で、怪しい通信を止める仕組みです。レンタルサーバーでは契約時から有効になっていることが多いです。
なぜあるのか
Webサイトは誰でもアクセスできる場所にあります。入力欄に不正な文字列を送り込んでデータベースを操作しようとしたり、管理画面のパスワードを総当たりで試したりする通信が、常に混ざってきます。
これをサイト側で1つずつ防ぐのは大変なので、手前で機械的に弾く役割を専門の仕組みに任せます。
つまずきやすい点
WAFは「攻撃らしいもの」を機械的に判定するため、正しい操作なのに止められることがあります。これを誤検知といいます。
このサイトでも起きました。記事をプログラム経由で投稿しようとしたら403で拒否され、原因は記事本文にプログラムのコードが含まれていたことでした。WAFから見ると、コードが送り込まれているように見えたわけです。
厄介なのは、止められた側に理由が表示されないことです。「なぜか失敗する」としか分からないので、原因にたどり着くまで時間がかかります。