いろいろ備忘録

雑記です。

オープンリダイレクタ対策の正規表現によるドメインのチェック

preg_match('/example.com/',$url);

これは$urlにexample.comが含まれることを確認する正規表現だが、

このように、example.comをファイル名に含むファイルを作っておくことで回避できる。
http://wana.com/example.com.php

 

次に、/dir/test.phpのような、スラッシュで始まるファイルパス参照かどうかを確認する正規表現だが、
preg_match ('/^\//',$url);

このように、スラッシュを2つ連続させ、ネットワークパス参照と呼ばれる形にすることで、すり抜けてしまう。
//wana.com/wana.php

 

正解はこれ。
preg_match('/^https?:\/\/example.jp\//',$url);

http(s)://example.jp/で始まることをチェックする。

ハットは文字列の最初を表す。

ハテナは直前の一文字の有無を許容する。

クリックジャッキング

例えば、ショッピングサイトの場合

正規サイトの内容をiframeで偽懸賞サイトの上にかぶせる。
このとき、偽懸賞サイトのの応募と正規サイトの購入ボタンの位置を合わせる事で、
もし正規サイトにログインしているユーザがボタンを押したら
意図せず商品を購入させられてしまう。

また、iframeを透明にすることでユーザに気付かせないようにする。

 

対策は、正規サイトがX-Frame-OptionsをSAMEORIGINまたはDENYにする。
提携のサイトであるexample.jpの埋め込みだけ許可するときは、ALLOW-FROM http://example.jpとする。

 

 

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);

他テーブルをUNIONで結合するとき、列名にNULLを用いる理由

他テーブルをUNIONで結合するとき、列名にNULLを用いる理由は

UNIONは列の数を合わせる必要があるため。

NULLでなくても、'a'とか0でも構わない。
たぶん文字列やDATE型等にはNULL、数値には0を使う。

ブラインド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導入時に攻撃を検出しても監視するだけで通信を遮断しないテスト期間を設ける。 この期間で攻撃が正しく遮断されるか、正規の通信が遮断されないかという確認を行う。