"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

Webアプリケーションの脆弱性/基本的なところをざっくり整理する

f:id:arakan_no_boku:20210807185857p:plain

目次

Webアプリ脆弱性が発生しやすい入出力に絞って整理

Webアプリの脆弱性とは「情報セキュリティ上の欠陥によってできる、悪意ある攻撃がつけいる隙」のことです。

悪意ある攻撃はWebアプリケーション外部から行われるので、必然的に、攻撃ポイントは、入力と出力(処理後にネットワーク上を流れている間を含む)に集中します。

そこで、今回は「入出力時の各局面で発生しやすい脆弱性と気を付けるべきポイント」に絞って整理することにしました。

まず、以下の図を描いてみました。

f:id:arakan_no_boku:20210809132342p:plain

図の①から⑦は、Webアプリの入力/出力(外部アクセス含む)における各局面を示しています。

以後、各局面ごとに発生しやすい脆弱性と注意点をまとめていきます。

① 認証/認可

認証は、利用者が本人であることを、なんらかの手段で確認することです。

クライアント証明書(個人や組織を認証し発行される電子証明書)を使った方法もありますけど、Webアプリケーションで多いのは、やっぱり、フォームでIDとパスワードを入力させるフォーム認証です。

フォーム認証を行う際のポイントとしては。

  • パスワードの強度をあげる工夫をする
  • パスワード間違いを繰り返した場合のアカウントロック(10回程度が適当?)
  • パスワードおよび暗号化のキーなどの保存方法
  • ログイン状態を保持する場合に安全な実装方法を採用する
  • ID/パスワード以外の認証と組み合わせた二段階認証の採用可否

です。

ここを詳しく書くとボリュームが大きくなるので、別記事でまとめます。

arakan-pgm-ai.hatenablog.com

認可は、認証されたユーザに対して権限を与えることです。

権限によって参照できる画面や項目、データ範囲が変わるので、脆弱性につながる以下のようなミスがないかを十分にチェックする必要があります。

  • 認可処理の実装不備でURLを直接たたくと、権限外のページが表示される
  • URLのパラメータを変更すると権限外の情報が見えてしまうような実装になっている
  • 権限有無をメニューの表示・非表示のみで制御するような実装になっている
  • hiddenパラメータやクッキーに権限情報を保持してしまっている
  • ロールを使わず管理者を示すユーザを作るような仕様にしてしまっている。

ちなみに「ロール」とは権限に紐づく役割に名前をつけたものです。

ロールと権限を紐づけておいて、そのロールをユーザに割り当てるように使います。

② 入力

入力が必ず正しく、悪意がない・・ことを期待することはできません。

だから、最低限

  • 文字エンコーディングの妥当性検証と必要であれば変換処理
  • 入力値(パラメータ文字列)の妥当性検証と必要であれば変換処理

は必要です。

文字エンコーディングは、簡単に言えば「どのコードにどの文字・意味をわりあてるかの決め事(符号化)」のことで、その組み合わせに「UTF8」とか「SJIS」とか名前がついています。

これが違うと文字化けしたり、変な動作になったりしますので、入力ではいってきたのがシステムが期待しているものと同じかを検証し、必要なら正しいものに変換してやる必要があります。

入力値の妥当性検証は、簡単にいうなら、その入力がシステムが期待しているデータであり、かつ、安全なものであることを検査して、OKなら通し、NGならエラーにするか、OKな形に変換(修正)することです。

どんな検証(検査)をするかは、処理の仕様やデータによって違いますが、以下の2つを意識して仕様をきめます。

  • 入力を格納するデータベースや入力を受けた処理にとって想定通りのデータか
  • 後続処理で不正動作を引き起こす可能性のあるものは排除できているか

以下、個別ケースで例を見ていきます。

③ DB(データベース)アクセス

データベースアクセスには「SQLRDBのデータを操作するための言語)」を使うことが多く、それに伴う脆弱性SQLインジェクション」に注意が必要です。

たとえば、パスワードに入力された「' OR '1' = '1'」みたいな文字列をそのままSQLに渡すことで条件が常に成立する状態(パスワードを知らなくてもログインできてしまう)する有名な例のように、入力やパラメータに入力された悪意ある文字列によってSQLが誤動作させられてしまうものです。

これを防ぐアプローチとしては。

  1. プレースホルダ(可変の変数や式の場所に?を埋め込む)によってSQL文を組み立てるように徹底する
  2. SQLの構文として解釈される文字(例えば「'」「<」「>」「=」「(」「}とか」)を無害な文字に変換するなどして、SQLが変更されないようにする。

 になります。

後者の部分が入力のところの「後続処理で不正動作を引き起こす可能性のあるものは排除できているか」にあたります。

④ シェル他

シェルはOSに付属するコマンドと読み替えてもいいです。

Webアプリケーション開発に使う言語には、OSコマンドを呼び出す機能を持っているものが多いので、それが悪用されると意図しないOSコマンドを実行されてしまう・・のが「OSコマンドインジェクション」という脆弱性です。

これによっておきる問題は、たとえば、別の攻撃用ツールのダウンロードやファイルの改ざん、管理者権限の不正取得などによって、別のサーバーを攻撃する踏み台にされたりとかになります。

どれも、かなりヤバいです。

こいつでやられる可能性がでてくるのは。

  • シェル(OSコマンド)を呼び出す機能のある関数を実装で使用している
  • その関数にパラメータを渡して実行している
  • そのパラメータでシェルで意味を持つ文字(メタ文字)の無害化が不十分

の3つの条件がそろった場合なので、防ぐにはその逆をやればいいことになります。

つまり。

  • シェル(OSコマンド)の呼び出し機能のある関数の利用は極力避ける
  • 外部から入力された文字列をコマンドラインのパラメータに渡さない。
  • 渡すパラメータの文字列のメタ文字の無害化(エスケープ)を行う

ですね。

⑤ ファイル(アクセス)

WEBサーバー内のファイルに対する不正アクセスが可能になる脆弱性があります。

大きくわけて2つの可能性があります。

ひとつは、公開するつもりのないファイルが見えてしまう・・というやつです。

この原因としては。

  • ディレクトリ・リスティングを無効にするのを忘れている
  • 公開フォルダにファイルを置いていて、URL指定でファイルが閲覧できてしまう。
  • エラーメッセージなどにうっかりファイルのパス名などの情報をのせてしまっている
  • 公開したくないファイルに対するアクセス権限をかけていない

などがあります。

これらは、なんらかのミスに起因するものなので、防ぐためには、公開したくないファイルは非公開ディレクトリにおくとかの、基本的なことを設計段階できっちりおさえておくしかないです。

あと、もうひとつ。

ディレクトリ・トラバーサルという外部からのパラメータでファイル名などを指定できる機能の脆弱性があります。

この脆弱性が生まれる原因としては。

  • ファイル名を外部から指定できる仕様・実装になっている
  • ファイル名として異なるディレクトリを指定できる実装になっている
  • 組み立てたファイル名に対するアクセス可否をチェックしていない

が考えられます。

なので、防ぐには当然、その逆をいけばよくて。

  • 外部からファイル名を指定できる仕様・実装はしない。
  • ファイル名にディレクトリ名が含まれないように実装する。
  • 組み立てられたファイル名に対するアクセス可否は必ずチェックする

なのですが、さらに

  • ファイル名を英数字に限定する仕様にしておく

も、ディレクトリトラバーサルの有効な対策になりえるといわれています。

⑥ 取り消しできない処理(の呼び出し)

取り消しできない処理というのは、例えば。

  • クレジットカード決済
  • 利用者の口座からの送金
  • パスワードやメールアドレスの変更

みたいな処理のことです。

これらを意図しない形で、Webアプリケーションから実行したら、もう取り返しがききませんので、ここで発生する脆弱性を発生させないのは、ものすごく重要です。

この脆弱性の代表的なものが「クロスサイト・リクエストフォージェリ(CSRF)」と「クリックジャッキング」です。

 

CSRFは「利用者の意図したリクエストであることの確認をすりぬけて、取り消しできない処理をWebアプリケーションから実行してしまう」脆弱性です。 

CSRFは、Webアプリケーション特有の以下の性質

  • form要素のaction属性にはどのドメインのURLでも指定できる
  • クッキーに保存されたセッションIDが対象サイトに自動的に送信される 

を悪用したものです。

なので、注意して実装すればなんとかなる・・ものではないのが厄介なところです。

対策は「CSRF対策な必要なページを認識して、そのページでは利用者の意図したリクエストであることを確実に確認する」ことです。

リクエストを確認する代表的な方法には。

  1. 利用者の意図したリクエストだと確認できる秘密情報(トークン)埋め込み
  2. 処理の実行前にパスワードの再入力を即す
  3. 利用者の意図したリクエストだと確認するためRefererのチェックする

があります。

2の「パスワード再入力」はユーザの手間が増えるので使える局面が限られますし、3の「Refererのチェック」はReferer(直前のウェブページのアドレスが含まれるリクエストヘダー情報)がオフにされてたら使えません。

ということで、必然的に1の「トークン埋め込み」が一番使いやすいとされています。

幸い、CSRF対策は言語レベルで仕組みとして用意されていることがほとんどなので、それらを適切に使う・・ことを忘れないことですね。

 

クリックジャッキングは「攻撃対象のサイトを罠のサイトの上に重ねて、攻撃対象フォームを透明にすることで、利用者は罠サイトの無害なボタンをクリックしたつもりで、実は攻撃対象のヤバいボタンをクリックさせてしまう」脆弱性です。

HTMLの機能(iframe要素とCSSのz-indexなど・・)を巧妙に悪用しています。

これは「iframe」の中で自身のWebアプリケーションを表示させられてしまうと、Webアプリケーション単体では対策するのは困難です。

なので、「X-Frame-Options」(レスポンス)ヘッダに、DENY(拒否)を指定して、iframeなどの内側で表示されなくするのが、この脆弱性を防ぐ方法となります。

あと、保険的に「取り消しできない処理などの実行後に、登録済メールアドレスに通知メールを送り、利用者が気づきやすくする」方法もしといたほうが良いです。。

最近では、パスワードなどの重要な情報を変更したり、いつもと違う端末でログインしたりするとメールがとんでくるサイトも増えてきてますが、まさに、それです。

⑦ レスポンス

ブラウザに対して結果を返す部分で、「HTML出力(JavaScript含む)」と「HTTPヘッダ出力」があります。

それぞれで発生する脆弱性の種類としては。

があります。

 

XSS(クロスサイト・スクリプティング)は有名です。

なりすまし、機能の悪用、フィッシング・・などなど、ニュースで目にする機会のあるセキュリティ被害はXSS脆弱性をつかれているものが多いです。

XSSは「Webアプリケーションで攻撃者によって悪意ある結果(レスポンス)を生成されてしまう」脆弱性です。

XSSのバリエーションはものすごく多いです。

Webアプリの処理とかJavaScriptとか発生元も違えば、悪用される局面も「クッキー値盗み出し」や「JavaScriptによる攻撃」や「画面の書き換え」・・いろいろです。

それらをこまごまと書き出すと収集つかなくなるので、今回は対策の基本だけおさらいします。

XSS対策の基本は。

  • HTMLやCSS要素を入力から受け取るような仕様にそもそもしない。
  • 入力値に対して「<」「>」「&」「"」などのHTML・CSSJavaScriptなどで使われる危険性のある文字を無害に変換(エスケープ)する。
  • レスポンスの文字エンコーディング指定をきちんとする

を徹底することです。

これも2番目・3番目のあたりは、入力のところに書いた「後続処理で不正動作を引き起こす可能性のあるものは排除できているか」にあたります。

あと追加でやっといたほうがいいのが。

  • X-XSS-Protectionレスポンスヘッダを使用する
  • クッキーのHttpOnly属性を付与してJavaScriptからの読み出しを禁止する
  • TRACEメソッドを無効化する(今は、デフォルトがそうなっているはず)

です。

 

HTTPヘッダインジェクションは、リダイレクトやクッキー出力で出力するHTTPヘダーへの悪意あるヘダーの追加やレスポンスボディを偽造するものです。

それにより意図しないURLへリダイレクトされたり、表示内容を改変されたりします。

対策は。

  • 外部からのパラメータをHTTPレスポンスヘッダとして出力する仕様にしない。
  • リダイレクトやクッキー生成は専用APIにまかせ、リダイレクト先は極力固定に。
  • URL中の改行をエラーにし、クッキー値の改行は必ず無害化(エンコード)する

ということになります。

おまけ:メール

Webアプリケーションからメール送信する機能があるとき、そのメールのヘッダや本文に悪意ある追加・変更ができてしまう脆弱性もあります。

メールヘッダ・インジェクションといいます。

迷惑メールやウイルスメール送信の踏み台に使われると、知らないまに犯人扱いされていやなので、これは防いでおきたいです。

対策としては。

  • メール送信には専用APIや安全が保障されているライブラリを使う
  • 外部からのパラメータをメールヘッダに含ませない。
  • 外部からのパラメータに改行を含ませないようにチェック・無害化する

なので、HTTPヘッダ・インジェクションと同じですね。

基本的なところを抑えるだけで防げる範囲は馬鹿にならない

正直なところ、脆弱性うんぬんの話題にはきりがありません。

日々、新しい脆弱性が報告されますし、それを悪用する工夫を日々やっている犯罪者もいるみたいですし。

だから、今回書いたのは、氷山のほんの一部で、セキュリティに詳しい方から見ると「不十分」・・とは十分自覚してます。

ですが、経験上、こういう超基本的なところをしっかり意識できているかどうかだけで情報セキュリティのリスクは大きく下げることができます。 

攻撃する側も全員がすごいスキルの持ち主ではありませんし、面倒なことは誰しも嫌いですから、攻撃に手間暇がかかるところで頑張るより、もっとガードの甘いところを探しに行こうと考えるからです。

まずは初心に立ち返って、ひとつひとつのことを確実にする。

これに勝るものはありません。

今回はこんなところで。

ではでは。