アオカケスの鳥かご

日々の出来事を綴っていきたい

OWASP ZAPで検出された脆弱性をゼロにする

大学のとある講義でログイン機能があるwebサイトを自作してレポートを書くという課題が課されました。

レポートには脆弱性検査の結果も書かなければならないため、OWASP ZAPでスキャンします。
サンプルのレポート的には検査した結果を書くぐらいで十分だと思うのですが、どうせなので検出された脆弱性をがんばって潰していきます。

検証環境

サイト構成

  • login.php(ログインページ)
  • logout.php(ログアウト時に踏むページ。login.phpに自動移動)
  • register.php(ID、PW登録ページ)
  • mypage.php(ログイン後に飛ばされるページ)
  • change.php(PW変更ページ)
  • dbconnect.php(DB接続用情報)

スキャン結果(1回目)

f:id:aokakes:20190119183517p:plain

まさかのSQLインジェクション脆弱性
プレースホルダ使ってるので大丈夫だと思っていました。

頑張って潰していきます。

脆弱性対策(1回目)

とりあえずリスクが高いものから対策していきます。

SQLインジェクション

' AND '1'='1' -- を入力したら同じ値が返ってきたというもの。
とりあえずその通りに入力してみます。

f:id:aokakes:20190119184449p:plain

私の想定通りの動きをしています。
一応プレースホルダを使ってSQLインジェクションの対策はしています。

f:id:aokakes:20190119184526p:plain

「Data was returned for the original parameter.」とありますし、入力した文字列がそのまま表示されているのがダメということでしょうか。
とりあえず「このユーザーは登録されていません」と表示するようにしておきます。

f:id:aokakes:20190119185000p:plain

X-Frame-Optionsヘッダーの欠如

クリックジャッキングの対策が出来てないよというもの。
正直に申し上げますと、全く考えていませんでした。以後気を付けます。

以下の一文をhttpd.confに追記しておきます。

Header set X-FRAME-OPTIONS "DENY"

PHPで直接書く方法もあるようです。この場合は全てのページに追記する必要があります。

<?php header("X-FRAME-OPTIONS: DENY"); ?>

こちらを参考にさせて頂きました。
www.deep-blog.jp


アプリケーションエラーの開示

エラーが発生したファイルの場所とかが見えてしまうというもの。

ひとまずphpのエラー文を表示しないようにしてみます。

検証のためにDB接続の際に必要な情報が入っているdbconnect.phpの名前を変えてエラーになるようにしておきます。
見事にエラーが表示されます。

f:id:aokakes:20190119192220p:plain

PHPのエラーを表示させないために全ページに以下を追記します。

ini_set('display_errors', 0);

f:id:aokakes:20190119192423p:plain

表示されなくなりました。
0の部分を1に変えると表示されるようになるので、弄るときはこの部分を変えれば良さそうですね。

また、php.iniを書き変える方法もあります。

display_errors = Off

ディレクトブラウジング

ディレクトリの一覧を表示できてしまうというもの。

現状index.phpが存在しないのでlocalhost/sec_db/kadai/にアクセスするとディレクトリの一覧が表示されます。

f:id:aokakes:20190119193920p:plain

httpd.confのIndexsを削除します。

Options Indexes FollowSymLinks Includes ExecCGI
↓
Options FollowSymLinks Includes ExecCGI

403が表示されるようになりました。

f:id:aokakes:20190119191329p:plain


ついでにapacheのバージョンも表示しないようにしておきます。
httpd.confに追記。

ServerTokens Prod

f:id:aokakes:20190119191555p:plain

バージョンが表示されなくなりました。
どうせなのでindex.phpも作っておきます。login.phpに飛ばすだけですが。

<?php
header("Location: login.php");
?>

パラメータ改ざん

パラメータを改ざんすることでエラーページが表示されるというもの。
既に「アプリケーションエラーの開示」の対策でエラーページは表示されなくなっているはずなので一旦保留します。

2回目のスキャンでまた出れば対策します。

WebブラウザXSS防止機能が有効になっていません

これに関しては無視します。
xssのテストをするときにchromeとかだと面倒なので。

X-Content-Type-Optionsヘッダの設定ミス

古いバージョンのIEChromeを使っているとContent-Typeの値に関わらず勝手に解釈してしまうというもの。

以下の一文をhttpd.confに追記しておきます。

Header set X-Content-Type-Options nosniff

PHPで直接書くときはこちら。X-Frame-Optionsのときと同じで全てのページに追記する必要があります。

<? header("X-Content-Type-Options: nosniff"); ?>

こちらを参考にさせて頂きました。
qiita.com

なんというか、この通りに全部やっておけばよさそうですね。
ついでに盛り込んでおきます。

# バージョン情報の隠蔽
ServerTokens Prod 
Header unset "X-Powered-By"

# httpoxy 対策
RequestHeader unset Proxy

# クリックジャッキング対策
Header append X-Frame-Options SAMEORIGIN

# XSS対策
Header set X-XSS-Protection "1; mode=block"
Header set X-Content-Type-Options nosniff

# XST対策
TraceEnable Off

ブラウザによるパスワードのオートコンプリート

入力フォームでオートコンプリートが有効になっているというもの。
全く気にしていませんでした。今思えばたしかに大事なような気がします。

ただ、現行のブラウザではautocomplete 属性を"off"にしていても機能しないことがあるようです。
developer.mozilla.org

とはいえ、何もしなければOWASP ZAPで検出されてしまうので対策しておきます。
全ての入力フォームにAUTOCOMPLETE='OFF'を追加するだけです。

<input type="text" name="user" AUTOCOMPLETE='OFF' placeholder="ユーザー名">


これで1回目のスキャンで検出された全ての脆弱性(ブラウザ以外)の対策が終わりました。
ちゃんと対策できているのか確認するために再度スキャンしてみます。

スキャン結果(2回目)

f:id:aokakes:20190119214308p:plain
f:id:aokakes:20190119214317p:plain

完全勝利。
正直1回で全部消えるとは思っていませんでした。

なんだか呆気ないですね。

まとめ

最初は全部やるのめんどくさいな~とか思っていましたが、いざやってみると案外面白いもので結構楽しみながらやっていました。
今後は最初から脆弱性を意識して、1回目のスキャンでゼロになるようにしていきたいと思います。