いろいろ備忘録

雑記です。

DOMベースのXSS

検索画面で、検索した文字列を

//keywordはクエリ
document.write(keyword);や

document.innerHTML=keyword;

こうしてエスケープ処理せずに表示するとkeywordにscriptタグが書けるのでXSSが可能な場合がある。

 

安全なやり方

文字表示用のdivを作っておき、
var div = document.getElementById('query_string');

createTextNode()によって、HTMLがエスケープ処理された子要素を作り、

var text_node = document.createTextNode(keyword);

それをdivに追加する。
div.appendChild(text_node);

ブラインドSQLインジェクション

SQLの実行結果がそのまま表示されないページでも
SQLインジェクションが可能な場合がある。

例えば会員登録フォームで、
「このIDはすでに使われています。」と出る時に
katoというIDのユーザがすでに居るとする。

kato'AND SUBSTR((SELECT pass FROM user WHERE id = 'admin'),1,1) = 'a'
とすると、kato'までは常に真なので、
管理者ユーザのパスワードの1文字目がaのときだけ「このIDは~」と表示される。

副問合せの結果がadminのパスワードで、それを1文字目から1文字切り出した結果が aと等しいなら、というSQL

ひとつめの1を、2や3などに増やしていくことで、パスワードの2文字目、3文字目と調べていく。

プレースホルダを使用していれば問題ないし、そもそもパスワードをハッシュ化していれば
試行に時間がかかる上にハッシュから元のパスワードを推測する工程が加わるので
攻撃の難易度は増す。

 

 

 

安全なWebサイトの作り方 失敗例 要約

前の記事で書いたものは省略しています。

OSコマンドインジェクション

失敗例

「"」と「;」、「'」、「<」、 「>」、「|」、「 (空白)」を削除する関数を使っていた。

失敗の理由

コマンド引数に、入力された値を使用するときは、 シェルで特別な意味を持つ文字である上記の文字群に加えて 「タブ」と「バッククォート」を削除する。 シェルには、バッククォートで囲まれた部分を実行して、 その出力をコマンドラインに反映するという機能があるため。 そして、そのコマンドとコマンド引数を分けるためには、空白に加えてタブ文字も使える。 よってバッククォートと水平タブで自由にコマンドを実行できてしまう。

修正方法

  • ライブラリを使う
  • コマンドの標準入力に値を与える

メールヘッダの差出人(From:)は標準入力で与えることができる。 この場合はコマンドラインの引数に入力された値を 使わなくなるのでOSコマンドインジェクションを解決できるが、 メールヘッダインジェクションの脆弱性が生まれる可能性があるので注意。

  • シェルを経由せずにコマンドを呼び出す

Perlの場合はカンマで区切ることでこれが可能。

不適切なセッション管理

失敗例

  • セッションIDの生成にプロセスIDとUNIX時間を使用
  • セッションIDに接続元IPアドレス、ポート番号、UNIX時間を結合しハッシュ化したものを使用

    失敗の理由

    プロセスIDやUNIX時間は推測可能である。 仮に、自分の後の一分以内にアクセスしてきたユーザが居るとする。 プロセスIDは連番であり、UNIX時間は秒単位なので、 自分の次のプロセスIDと自分の時間から1分以内のUNIX時間の組み合わせは60通りとなる。 これを総当りすればセッションハイジャックが成功する可能性がある。

後者は、 接続元IPアドレスは罠サイトに誘導して取得 接続元ポート番号はだいたい2万通り 時間は10秒前後以内だと仮定して10通り この20万通りを総当りする。

修正方法

セッションIDは暗号論的擬似乱数生成器を用いて生成する。

XSS

失敗例

  • リンクのURLのパラメータのエスケープ処理漏れ

2ページ目に飛ぶリンクのURLがtwo.htmlで、 max_numberからページごとの表示件数をクエリから取得するとき、 現在のページがone.html?max_number=10ならば 次のページのリンクが<a href="two.html?max_number=10"> のように生成されるとする。

このとき、攻撃者がone.html?max_number=<script>...というURLのリンクを掲示板に書き込む。 次のページのリンクのURLの出力時にエスケープ処理が漏れていたとき、XSSが起こる。

  • 404 Not foundで表示するURLのエスケープ処理漏れ

404用のページには、「お探しの nakatta.htmlは見つかりませんでした」 のように、URLがテキストとして出力されるWebサイトがあるとする。

example.com/<scr...というURLのリンクをメールや掲示板に書き込めば、 当然<scr...というページは存在しないので、 リンクを開いた人のブラウザには「お探しの<scr...は見つかりませんでした」と出力される。 このようにXSSが成功する可能性がある。

User-AgentRefererなど、アクセス情報を出力するページは注意が必要。

誤った対策

  • ブラックリスト方式による入力チェックのみを実装

    ヌル文字などの制御文字でマッチングが回避されてしまう可能性がある。

  • 連結処理を悪用した入力チェックの回避

都道府県や市区町村単位で区切って入力し、それらを結合したものを表示する場合は、 一つ目に<scr、二つ目にipt>のように、分割して入力する方法で、 エスケープ処理を回避出来る可能性がある。

  • 危険な文字列を単に削除する方法

単にscriptという文字列を削除する処理の場合、 scriscriptptと入力された時はその結果としてscriptが出力されてしまう。 削除ではなく無害な文字列へ置換すべき。

安全なウェブサイトの作り方 安全性向上 要約

ウェブサーバ

OS やソフトウェアの脆弱性情報を継続的に入手し、脆弱性への対処を行う

ウェブサーバをリモート操作する際の認証方法として、パスワード認証以外の方法を検討する

総当りこうげきとうにで突破されるおそれがある。 暗号技術に基づいた公開鍵認証にすべき。

パスワード認証を利用する場合は、十分に複雑な文字列を設定する

不要なサービスやアカウントを停止または削除する

不要なサービスは管理が十分でないので、 脆弱性が存在するバージョンを使用している可能性がある 不要なアカウントも管理が充分でないので不正利用される可能性がある。

公開を想定していないファイルを、ウェブ公開用のディレクトリ以下に置かない

DNS

ドメイン名およびその DNS サーバの登録状況を調査し、必要に応じて対処を行う

ドメイン名の乗っ取りを行われた場合、利用者が本物のウェブサイトの URL を指定しても、 そのドメイン名を乗っ取った人が用意したウェブサイトに接続してしまう。 登録状況を定期的に確認し、必要に応じて対処する。

DNS ソフトウェアの更新や設定を見直す

設定不備や古いバージョンが原因となって DNSキャッシュポイズニングを受けたり、 DNSリフレクション、水責め攻撃の踏み台になる可能性が高くなる。

ネットワーク盗聴

重要な情報を取り扱うウェブページでは、通信経路を暗号化する

暗号化したい情報を入力させるページは、送信先(formのPOST先など)をHTTPSにするだけでなく 入力させるページそのものもHTTPSにしなければならない。 なぜなら、入力ページが改ざんされたとき、HTTPSならブラウザに警告画面が表示されるが、 HTTPはページに電子署名が行われていないので表示されない。 よって、送信先が改ざんされると機密情報が攻撃者に渡る可能性がある。

入力ページがHTTPSであるという確認を利用者が怠る場合に備え、 ウェブサーバのレスポンスヘッダに「Strict-Transport-Security」を出力する。 そうすると、一度そのサイトを訪れた利用者のブラウザは、 それ以降そのサイトにはhttps:// でしか接続しないようになる。

フィッシング詐欺

EV SSL 証明書を取得し、サイトの運営者が誰であるかを証明する

フレームを利用する場合、子フレームの URL を外部パラメータから生成しないように実装する

リダイレクト先の URLには自サイトのドメインのみを許可する

ログインに成功したあとのリダイレクト先を、動的に クエリ文字列やhiddenパラメータで決定するウェブサイトがあるとする。 たとえば、利用者がログイン画面だけ正規のサイトであることを確認しても、 遷移後のページがログインページと見た目が同じで、 「ログインに失敗。再入力」というメッセージが表示されていたとき、 利用者は大きな疑いを抱かずに偽サイトに認証情報を入力するかもしれない。

リダイレクト先のURLとなるパラメータに自ドメインのみ許可していれば、 この攻撃を防ぐことができる。

パスワード

初期パスワードは、推測が困難な文字列で発行する

英数字や記号を含めた長い文字列で発行する。 もしも規則性があったら、調査のためのテストユーザを複数登録され、 その際に発行されたパスワードから規則性を導き出されるかもしれない。 利用者によっては初期パスワードを変更せずにそのまま使い続けるかもしれないので、 推測されやすいものは避けるべき。

パスワードの変更には、現行パスワードの入力を求める

入力後の応答メッセージが認証情報の推測のヒントとならない工夫をする

ユーザIDもしくはパスワードが違います、というように曖昧にする

入力フィールドでは、パスワードは伏せ字で表示されるようにする

パスワードをサーバ内で保管する際は、平文ではなくソルト付きハッシュ値の形で保管する

単にハッシュ化だけだと、レインボーテーブル攻撃に弱い。

ユーザごとにソルトを用意しなかった場合も、攻撃者が予め登録しておいた 弱いパスワードのユーザと一致するハッシュを探すことでパスワードを推測できる。

しかし、ユーザごとにソルトを用意しても弱いパスワードは 時間をかければ復元できるので、あえて計算の遅いハッシュ関数を使う技法がある、 それを実現する方法の一つにハッシュ化を繰り返し行う「ストレッチング」がある。

 

WAFによるウェブアプリケーションの保護

WAFにおけるフォールスポジティブ

例: HTMLの特殊文字である「\<」を検出パターンとして定義したら、 単なる数式もWAFによって遮断されてしまった。

WAFにおけるフォールスネガティブ

例: クエリ文字列やメッセージボディ、Cookieに同じ名前のパラメータがあったとき、 WAFは後のパラメータのみ検査するようになっていたが、 ウェブサーバは同じ名前のパラメータを結合するようになっていたとき、 WAFの検査をすり抜けてしまう。 これはHTTPパラメータ汚染と呼ばれている。 RFCが明確に定義していない部分において、 各ソフトウェアによって解釈、動作の差異が生じて起こる。

これらの2つを減らすには

WAF導入時に攻撃を検出しても監視するだけで通信を遮断しないテスト期間を設ける。 この期間で攻撃が正しく遮断されるか、正規の通信が遮断されないかという確認を行う。

安全なウェブサイトの作り方 脆弱性対策 要約

SQLインジェクション

根本的な対策

SQL 文の組み立ては全てプレースホルダで実装する

SQL 文を動的プレースホルダで生成する場合は、DBエンジンのAPIを用いる。

APIを使うと以下の注意をしなくて良くなる

文字列型の注意

値をエスケープしないと、値を囲むシングルクォートがエスケープされてしまう危険性

数値型の注意

値を数値型にキャストするなど、値が数値型であることを確実にする

保険的な対策

エラーメッセージをそのままブラウザに表示しない。

データベースの種類やエラーの原因、実行エラーを起こした SQL文等が知られる可能性

データベースアカウントに適切な権限を与える

OSコマンドインジェクション

根本的な対策

シェルを起動できる言語機能の利用を避ける。

PHPのexecなど、そういう関数を使わないようにする。

どうしても利用したい場合は、その引数を構成する全ての変数に対して チェックを行い、あらかじめ許可した処理のみを実行する。 チェックにはホワイトリストを利用する。 ブラックリストだと、記号に漏れが生じる場合があるので。

パス名パラメータの未チェック/ディレクトリトラバーサル

根本的な対策

外部からのパラメータでウェブサーバ内のファイル名を直接指定する実装を避ける。

例えばhiddenで指定する方式だと、そのパラメータが改ざんされることで、 任意のファイルをウェブページとして出力される可能性。

ファイルを開く際は、固定のディレクトリを指定し、かつファイル名にディレクトリ名が含まれないようにする。

例えば、open(fn)のfnに絶対パス名が渡されてしまう事で任意のディレクトリのファイルが開かれてしまう これを防ぐには、open(publicDirName + basename(fn))とする。 publicDirNameは固定のディレクトリ。 basenameは、パス名からファイル名のみを取り出す関数。

保険的な対策

ウェブサーバ内のファイルへのアクセス権限の設定を正しく管理する

ファイル名のチェックを行う。

「/」「../」「..\」など、OSのパス名解釈によってディレクトリを指定できる文字列をチェック。 しかし、それらをURLエンコードした「%2F」や、 二重エンコード(パーセント記号を%25にエンコード)した「%252F」などが有効になる場合がある。 よってデコードを終えてからチェックする必要がある。

セッション管理の不備

根本的な対策

セッション ID を推測が困難なものにする

生成アルゴリズムに暗号論的擬似乱数生成器を用いるなど、予測困難なものにする。

セッション ID を URL パラメータに格納しない

セッション ID は、Cookie に格納するか、POSTメソッドの hidden パラメータに格納して受け渡しする

HTTPS 通信で利用する Cookie には secure 属性を加える

ログイン成功後に、新しくセッションを開始する

セッションIDの固定化攻撃の対策。 セッションIDの固定化攻撃とは、 1.攻撃者がログイン前のセッションIDを渡す 2.ユーザがログインする 3.攻撃者がユーザとしてログインする

ログイン成功後に、既存のセッション ID とは別に秘密情報を発行し、ページの遷移ごとにその値を確認する

セッションIDが2つに増えるイメージ。 秘密情報(トークン)をログイン成功時に発行し、それを毎回確認する。 セッションIDはログイン前から成功した後もずっとある。秘密情報はログイン成功後から。

秘密情報を発行しなくて良い方式
  • ログイン時に新しくセッションを開始する方式(すぐ上に書いた)
  • ログイン前にはセッションIDを発行しない方式

保険的な対策

セッション IDを固定値にしない

セッションIDが利用者ごとに固有の値だと、 古いセッションIDを入手されたときセッションハイジャックされる。 セッションIDは利用者のログインごとに新しく発行する。

セッション ID を Cookie にセットする場合、有効期限の設定に注意する

expiresを短い日時に設定し、必要以上の期間、クッキーがブラウザに保存されないようにする。 expiresを省略するとブラウザの終了時に破棄となるが、 ブラウザを立ち上げっぱなしのとき破棄されない。それはそれで困る。

XSS

HTML テキストの入力を許可しない場合

根本的な対策

ウェブページに出力する全ての要素に対して、エスケープ処理を施す。

実体参照(例:<)、文字参照(例:&#0000;)を用いる。 HTML タグを出力する場合は、その属性値を必ずダブルクォートでくくる。 入力値だけエスケープする方針だと必ず対策漏れが出るため、出力の全てをエスケープする。 忘れがちだが、JSのdocument.writeやinnerHTMLで書き換える際もエスケープが必要。

URL を出力するときは、「http://」や 「https://」で始まる URL のみを許可する

入力されたURLをそのままaタグで出力すると、 javascript:と書かれた時任意のJSが実行されてしまう。 http://かhttps://で始まる文字列のみ許可するホワイトリストで実装すべき。

要素の内容を動的に生成しない

危険なスクリプトかどうかを確実に判断するのは難しいので、 入力やDBを元にJSを生成するのは良くない。

スタイルシートを任意のサイトから取り込めるようにしない

スタイルシートには、expression() 等を利用してスクリプトを記述することができる。 上と同様に、スクリプトが危険かどうかの判断は難しいので、 取り込めるような仕様は良くない。

保険的な対策

入力値の内容チェックを行う

ウェブアプリケーションの仕様が、幅広い文字種を許すときは対策にならない。 対策になるのは仕様が英数字のみ許す場合など。

HTML テキストの入力を許可する場合

根本的な対策

入力された HTML テキストから構文解析木を作成し、スクリプトを含まない必要な要素のみを抽出する

入力された HTML テキストに対して構文解析を行い、 「ホワイトリスト方式」で許可する要素のみを抽出する。 ただこの場合、複雑なコーディングが要求される上に、処理に負荷がかかる。

保険的な対策

入力された HTML テキストから、スクリプトに該当する文字列を排除する

「\<script>」や「javascript:」のサニタイズは、 「\<xscript>」「xjavascript:」のように、その文字列に適当な文字を付加する。 他の排除方法として文字列の削除があるが、 削除した結果、却って危険な文字列を形成してしまう可能性がある。

ウェブアプリケーションに共通の対策

根本的な対策

HTTP レスポンスヘッダの Content-Type フィールドに文字コード(charset)を指定する

一部のブラウザは、HTMLテキストの冒頭部分等に特定の文字列が含まれていると、 必ず特定の文字コードとして処理してしまう。 具体的には、+ADw-script+AD4-という文字列が一部のブラウザではUTF7として解釈され、 その結果<script>のように扱われてしまう。 エスケープ処理をしているときはShift_JISUTF-8などのはずなので、 上記の文字列が適切にエスケープされない。

保険的な対策

発行する Cookie に HttpOnly 属性を加える

HttpOnlyはJSからクッキーにアクセスすることを防ぐ。

ブラウザのXSS対策機能を有効にするレスポンスヘッダを返す。

  • X-XSS-Protection

HTTP レスポンスヘッダに「X-XSS-Protection: 1; mode=block」

  • Content Security Policy

HTTP レスポンスヘッダに「Content-Security-Policy: reflected-xss block」

CSRF

根本的な対策

秘密情報(トークン)を埋め込む

入力→確認→登録というページ遷移があるとする。 利用者の入力内容を確認画面として出力する際、 合わせて秘密情報をhiddenパラメータに出力する。 その後、登録処理を行う前に、秘密情報とhiddenパラメータの比較を行う。 秘密情報にはセッションIDをそのまま使うか、もしくは別の予測困難な乱数を用いる。 確認画面へのリクエストにはPOSTを用いる。GETだとRefererでバレるので

処理の直前でもう一度パスワードの入力を求める

確認画面にPOSTメソッドでのパスワードの再入力をさせる。 上の方法だとセッションではなくBasic認証を用いている場合は 新たに秘密情報を作らなければならない。実装上の手間がかかる。 この方式だと、パスワードをそのまま用いれば良いので採用しやすい。 Basic認証は自動的に認証情報が送られるので、当然そのままだとCSRF対策にならない。 あくまでPOSTで入力させることに注意。

Referer が正しいリンク元かを確認し、正しい場合のみ処理を実行する

Referer が空の場合も、処理を実行してはいけない。 これはRefererを空にしてページを遷移する方法が存在するから。 しかし、ブラウザやパーソナルファイアウォール等の設定で Referer を送信しないようにしている利用者もいるので注意。 また、攻撃者がそのウェブサイト上に罠を設置出来る場合は対策にならない。

保険的な対策

重要な操作を行った際に、その旨を登録済みのメールアドレスに自動送信する

メール本文にはプライバシーに関わる重要な情報を入れないことが重要。

HTTPヘッダインジェクション

根本的な対策

ヘッダの出力を直接行わず、ヘッダ出力用 APIを使用する

ヘッダ出力用APIウェブアプリケーションの実行環境や言語に用意されている。

ヘッダ出力用APIを利用できない場合は改行を許可しないよう処理する。

保険的な対策

外部からの入力の全てについて、改行コードを削除する

メールヘッダインジェクション

根本的な対策

メールヘッダを固定値にして、外部からの入力はすべてメール本文に出力する

「To」、「Cc」、「Bcc」、「Subject」を外部からの入力で生成すると 改行コードによってメールヘッダの挿入や メール本文の改変、任意の宛先へのメール送信に悪用される可能性がある。

メールヘッダを固定値にできない場合、メール送信用 API を使用する

メール送信用APIウェブアプリケーションの実行環境や言語に用意されている。 改行コードの対策には、

  • 改行コードの後に空白か水平タブを入れることで継続行として処理する
  • 改行コード以降の文字を削除する
  • 改行が含まれていたら処理を中止する

がある。 継続行とは、一行が長くなった時に見た目でのみ改行するもの。 システム的には改行されない。 Javaではバックスラッシュ、VBではアンダーバーが使われる。

保険的な対策

外部からの入力の全てについて、改行コードを削除する

クリックジャッキング

一例としては、 罠ページにサイトAのコンテンツを表示し、それをクリックさせることで サイトAにログイン済のユーザに意図しない処理を実行させる。

根本的な対策

HTTP レスポンスヘッダに、X-Frame-Options ヘッダフィールドを出力する

ユーザが罠サイト内のframeからサイトAにリクエストを送信するとき、 サイトAがレスポンスヘッダにX-Frame-Options: DENYのように出力すれば 対応したブラウザにおいてその読み込みは中止される。

  • DENY 全ドメイン禁止
  • SAMEORIGIN 同一オリジンのみ
  • ALLOW-FROM 指定したオリジンのみ

ALLOW-FROMは提携サイトのフレーム内に自コンテンツを表示させたいときなど。

処理を実行する直前のページで再度パスワードの入力を求める

保険的な対策

重要な処理は、一連の操作をマウスのみで実行できないようにする

キーボード操作などを挟む。

バッファオーバーフロー

根本的な対策

直接メモリにアクセスできない言語で記述する

PHP,Perl,Javaなどはできない。 しかしJavaは整数オーバフローは起こる。

直接メモリにアクセスできる言語で記述する部分を最小限にする

可能な限りC, C++, アセンブラで記述する部分を減らす。

脆弱性が修正されたバージョンのライブラリを使用する

アクセス制御や認可制御の欠落

根本的な対策

認証機能に加えて認可制御の処理を実装する

複数の利用者が存在する場合は、どの利用者に どの操作を許可するかを制御する認可(Authorization)制御の実装が必要

SC 22春 午後1問1設問2(3) volatile synchronized

グローバル変数の同期を取る方法を書くが、volatileは意味をなさないそうだ。

なぜか:

Javaにはメインメモリとスレッドごとのキャッシュ領域があり、

synchronizedはメインメモリのフィールドに直接ロックをかける。

一方、volatileはスレッドのキャッシュに変数を取得する際に最新の値を取得するので、他スレッドが値取得→自スレッドが値取得→他スレッドが値変更
となったときに変更が失われてしまう。

Javaスレッドメモ(Hishidama's Java thread Memo)