Webサイトを開発するときやサーバー構築時に気をつけないといけない点を一覧化しました。
開発時の留意点
SQLインジェクション (重要度: ★★★)
ユーザーから受け取ったパラメーターを元にSQLを組み立てて実行する処理があった場合に、パラメーターを工夫することで、意図しないSQLが実行されてしまう可能性がある問題。
$sql = "SELECT 会員ID FROM 会員一覧 WHERE 会員ID = {$_POST['id']}";
例えば、上記のような処理を入れていると、id値に1; DELETE FROM 会員一覧
というパラメーターが送られたとき、会員一覧が全部消えてしまう。
対策
プレースホルダー機構に対応したSQL実行の仕組みを採用する。以下、一例。
$sth = $dbh->prepare("SELECT 会員ID FROM 会員一覧 WHERE 会員ID = :id");
$sth->execute(['id' => $id]);
上記のような仕組みを採用したとしても、例えばプレースホルダーの数が可変の処理があるなどの理由で、コーダーによって手動でSQLが組み立てられてしまうことがよくあるので、ソースレビュー等、他の対策も必要になってくる。
なお、LIKE
検索は通常のSQLエスケープ
だけでは不十分で、\
を \\
に、%
を\%
に、_
を\_
に置換する必要がある。
$str = strtr($str, ['\\' => '\\\\', '%' => '\%', '_' => '\_']);
XSS - クロスサイトスクリプティング (重要度: ★★★)
ユーザーからの投稿を元に、HTMLに投稿内容をそのまま反映してしまうことで、任意のHTMLタグを埋め込むことができ、サイトが改竄されてしまう問題。
<div><?php echo $row['ユーザーが入力した本文']; ?></div>
こんな処理を入れていると、<script type="text/javascript">alert('このサイトは乗っ取った');</script>
といった本文を投稿されたら、ページを表示しただけでアラートダイアログが表示されてしまう。
単にサイトが改竄できてしまうだけでなく、アクセスしてきたユーザーのセッションIDを盗んで攻撃者に送るみたいなこともできてしまうので危険。
対策
ユーザーの入力を元にしたパラメーターは全てHTMLエスケープ
を行う。と言うか、ユーザーの入力でなくても一律HTMLエスケープ
しておくぐらいの方が良い。
テンプレートエンジンによってはデフォルトでHTMLエスケープし、エスケープしない場合に逆に特殊な記述をする必要があるものがあるので、活用できる。
ユーザーの入力した改行を反映させたい場合は、改行を<br>
タグに変換する処理も必要。
<div><?php echo nl2br(htmlspecialchars($row['ユーザーが入力した本文'])); ?></div>
また、JavaScript
でも同様の問題が起こり得るので注意する。
どうしてもHTMLタグの入力を受け付ける必要がある場合は、
- バリデーションでHTMLタグの構文チェックをする。
- 特定のタグや特定の属性のみを許容する。
- マークダウン記法など、別の方式で入力してもらうようにする。
などを検討する。(どれも難しいのでできるだけ避ける)
また、以下の設定を入れることも有効。(古いブラウザで、画像などのファイルにJavaScriptを埋め込んでも働かないようにする設定)
Apache
Header always set X-Content-Type-Options nosniff
nginx
add_header X-Content-Type-Options nosniff;
CSRF - クロスサイトリクエストフォージェリー (重要度: ★★★)
画面遷移のチェックを正しく行わないことにより、入力・確認画面を経ずに直接投稿完了・購入完了などの処理が行えてしまう問題。
別のユーザーから悪意のあるリンクを設置されると、それを踏んだユーザーに意図しない投稿・購入を行わせることができてしまう。例えば、完了画面のURLを /item/finish
とし、
<form method="POST" action="https://ドメイン/item/finish">
<input type="hidden" name="商品ID" value="買わせたい品" />
<input type="submit" value="詳しくはこちら" />
</form>
というタグを外部に設置されてしまうと、「詳しくはこちら」を押したユーザーに意図しない品を買わせることができてしまう。
対策
セッション
やCookie
を活用し、正規の画面遷移で進んだ場合のみ正しいセッション情報などを記録するようにする。完了画面では確認画面を経て遷移されたことを確認する。
パスワードのハッシュ化 (重要度: ★★★)
ユーザーのパスワードを平文でDBに保存してしまうと、万が一DBの情報が流出したときにユーザーのパスワードも漏れてしまう。その結果、同じパスワードを使っている他サイトにも被害が拡大してしまう。また、流出しなかったとしても内部犯にパスワードを使われてしまう。
対策
ユーザーのパスワードを保存するときは、必ず一方向ハッシュ化関数
を使って、ハッシュ化した値を保存する。単にハッシュ化するだけでなく、サイト独自の Salt値
も使って保存した方が、流出時にハッシュ値を照合されるリスクを減らせる。
PHP
の場合は、md5
やsha1
などの関数はパスワード保存には向かず、Salt値
の自動生成まで含めてやってくれる password_hash
関数が推奨されるとのこと。ただし、この関数を使った場合は、パスワードの認証時に password_verify
関数を使う必要がある。
メール送信ミス (重要度: ★★★)
メールを誤った人に送ったり、宛先と本文内に記載の氏名が一致しなかったり、メール内にパスワードなどの重要情報を含めてしまう、等のトラブルを起こしてしまう。
対策
完全な対策は無いが、
- メール本文内に個人を特定できる情報はできるだけ含めない。理想は、個人情報は一切メールに含まず、Webサイトに行かないと確認できないこと。(「確認メール」等の用途では現実的に難しい場合もあるが)
- メール本文にパスワードの情報を含めない。
- 一連の処理内で複数通のメールを送る際は、前に送った宛先や本文が必ずクリアされた状態で次のメールを送る。
- ユーザーからのアクションをトリガーにしないような、こちらから一方的に送るメールは送信時間に気をつける (深夜に送らない)
GETメソッドでの更新処理 (重要度: ★★)
データの更新が伴う処理をGET
メソッドでも受け付けてしまうと
- うっかりユーザーが他人にURLを共有したときにユーザーの入力内容がバレてしまう。
- アクセスログにユーザーの個人情報などの投稿内容が残ってしまう。
- CSRF攻撃がより行いやすくなる。
対策
データの更新が伴う処理は、POST
(場合によってはPUT
、DELETE
)のメソッドでのみ受け付ける。
メールアドレス認証 (重要度: ★★)
ユーザーにメールアドレスを入力してもらうサイトで、
- 本人でないメールアドレスで会員登録される、もしくはダミーのメールアドレスで会員登録される。
- いたずら目的または、営業メールを他人に送りつける目的で、他人のメールアドレスを入力される。
例えば、問い合わせフォームに自動返信機能をつけた場合、
差出人: メールを送りつけたい相手
本文: Tシャツが50%オフ http://~~~/
と入力することで、他人にメールを送り付けることができる。
対策
- 会員登録は一度認証メールを送り、踏まれたときに正式に登録する。
-
ソーシャルログイン
機能を使う。 - 問い合わせフォームなど、1回限りの処理については現実問題許容せざるを得ないことが多い。(「このメールに心当たりが無い場合は」のような文言を入れるのは有効)
二重送信 (重要度: ★★)
投稿画面や購入画面で、ボタンを二度押ししたり、完了画面でF5キーを押すことで、投稿・購入が二重に行われてしまう。
対策
- 一度ボタンを押されたら、二度目の押下を
JavaScript
で無効化する。(100%の対策ではない) - 完了時に別URLにリダイレクトさせる。(戻るボタンで戻られたときに再処理が行われてしまう問題は残る)
- 投稿・購入完了後、セッションを終了させる。
ディレクトリトラバーサル (重要度: ★★)
ユーザーの入力を元に読み込むファイルを決定する処理があった場合に、親ディレクトリを辿られるなど意図しないファイルを参照される問題。親ディレクトリにあるサーバー設定ファイルやパスワードファイルなどを参照されてしまう可能性がある。
echo file_get_contents("/var/www/hoge.jp/img/{$_GET['filename']}");
この場合、filenameに../../../../etc/passwd
と指定されるとアカウント情報を参照されてしまう。
対策
ユーザーの入力を元にファイルパスを決定するような処理を行わない。どうしても必要な場合、必要な文字種を限定するなどのバリデーションを行う。
OSコマンドインジェクション (重要度: ★★)
ユーザーの入力を元にOSのコマンドを組み立てて実行することにより、任意のコマンドを実行できてしまう問題。
exec("mail -s '{$_POST['title']}' sitemaster@hoge.co.jp");
上記のような処理を組んでしまうと、titleが'; rm -rf /;
だったときに、全消去コマンドが実行されてしまう。
対策
そもそも、OSコマンドを実行するような処理は組まない。大抵の処理は、プログラミング言語内に類似の処理をする関数が存在する。
何らかの理由でどうしても必要な場合はバリデーションをして、特定の文字列のみ受け付ける。
パラメーター偽装 (重要度: ★★)
JavaScriptで入力値のチェックをしているサイトで、ブラウザの開発機能を使って直接パラメーターを改竄して、JavaScriptのチェックをすり抜ける問題。
対策
JavaScriptでバリデーションをしていたとしても、サーバー側でもバリデーションを実施しなければならない。
画面に見えていない hidden
パラメーターも同様。
メールヘッダーインジェクション (重要度: ★)
メール送信時に、ユーザーの入力値を利用してメールを組み立てることで、任意のメールヘッダーが挿入されてしまう問題。
$mail = "Subject: {$_POST['subject']}";
という処理を入れると subject値が a\r\nCc: hogehoge@hoge.jp
だった場合に、勝手にCcヘッダーが付加されてしまう。
対策
自力でメールを組み立てるような原始的な実装はしない。ただし、既存のメール関数にも脆弱性があるものがあるので注意する。
HTTPヘッダーインジェクション (重要度: ★)
ユーザーから受け取った情報を元に、HTTPレスポンスヘッダー
を組み立てることで、任意のHTTPヘッダーが挿入されてしまう問題。特にHTTPヘッダーの組み立てまで自力で実装するPerl
のような言語では注意が必要。
$cgi = new CGI;
$url = $cgi->param('url');
print "Location: $url\n\n";
urlが/\nSet-Cookie: hoge
などとなっているとSet-Cookie
ヘッダーを埋め込めてしまう。
対策
できるだけヘッダーの生成にユーザーの入力値を利用しない。パラメーターのバリデーションを正しく行う。
HTTPヘッダー偽装 (重要度: ★)
ユーザーから受け取る Referer
や User-Agent
などのヘッダーは、ユーザーによっていくらでも偽装可能。
対策
基本的には問題は特に無いが、偽装値が入れられる場合があることは留意する。Referer
を元に戻り先URLを決めるなどの処理は避けた方が無難。また異常値が入力されていたとしてもバグらないように気をつける。
リダイレクト踏み台化 (重要度: ★)
前にいた画面のURLを受け取り、処理完了後にそのURLにリダイレクトする、といった処理を実装した場合に、任意のリダイレクト先URLを埋め込まれてしまい、サイトを踏み台として利用されてしまう問題。
header('Location: {$_GET['url']}');
といった処理をしていると、/register?url=http%3A%2F%2F~~
というパラメーターにより任意のサイトに飛べてしまう。
対策
「/」で始まり、かつ「//」では始まらないURLのみ受け入れるようにすれば、サイト内のURLに限定できる。
Webサーバー構築時の留意点
nginxキャッシュ (重要度: ★★★)
nginx
には一定期間生成されたHTMLなどをキャッシュする機能があるが、誤ってマイページ等をキャッシュすると、ユーザーに別人のマイページが表示されてしまい、個人情報の漏洩になってしまうので、絶対に設定を誤ってはならない。
対策
ユーザーに応じた情報が表示されるページはキャッシュしないようにする。
set $do_not_cache 0;
if ($request_uri ~ "^/(mypage|contact)(\?|/|$)") {
set $do_not_cache 1;
}
proxy_no_cache $do_not_cache;
proxy_cache_bypass $do_not_cache;
HTTPS化 (重要度: ★★★)
WebサイトをTLS
(HTTPS
)に対応しないことにより、個人情報等の重要な情報が平文のままネットワーク上を流れてしまう。
対策
SEO
の観点からも、サイト全体をHTTPS化するのが良い。現在はLet's Encrypt
等の無料で導入できるSSL証明書もあるし、無料CDN
を活用することでもHTTPS化が可能。
尚、HTTPS化する際は、SSL 2.0
、SSL 3.0
、TLS 1.0
などの古い方式は避ける。また、証明書も鍵長や暗号方式の推奨値が頻繁に変わっているので注意。
ディレクトリリスティング (重要度: ★★)
Apache
の設定により、ディレクトリにあたるURL
にアクセスしたときに、ディレクトリ内のファイル一覧が表示されてしまう問題。不必要な情報を与えてしまう。
対策
ディレクトリの中身一覧の表示を無効化する。
Options -Indexes
クリックジャッキング攻撃 (重要度: ★★)
他サイト内の iframe
に勝手に自分のWebサイトを埋め込まれてしまう問題。サイト内に巧妙に被せることにより、ユーザーに意図しない操作をさせることができてしまう。
対策
他サイトに埋め込まれることを禁止する。
Apache
の設定
Header always set X-Frame-Options DENY
nginx
の設定
add_header X-Frame-Options DENY;
Webサーバー情報の隠蔽 (重要度: ★)
出す必要の無いWebサーバーのバージョン情報などが晒されてしまう問題。
対策
Apache
の設定
ServerSignature Off
nginx
の設定
server_tokens off;
HTTPメソッド制限 (重要度: ★)
TRACE
などのHTTPメソッド
のリクエストを受け付けることにより、クロスサイトトレーシング
(XST
)などの脆弱性を生んでしまう問題。
対策
通常使うHTTPメソッド
は、GET
, POST
, PUT
, DELETE
, HEAD
だけなので、それ以外の不要なHTTPは無効化する。
Apache
の設定
TraceEnable Off
<LimitExcept HEAD GET POST PUT DELETE>
Order deny,allow
Deny from all
</LimitExcept>
nginx
はTRACE
はデフォルトで無効になっているとのこと。
その他 (重要度: ★)
-
Apache
では、.htaccess
は無効化した方が良い。(何らかの原因でファイルを外部から置かれた場合に誤動作の原因になる。 -
PHP
の場合open_basedir
を指定して、PHPでアクセスできる範囲を最小化する。 -
chown
やchmod
でパーミッションを適切に設定する。
サーバー構築時の留意事項
パッケージの最新化 (重要度: ★★★)
サーバー内のパッケージが古いことにより、セキュリティホールができるため、定期的にパッケージをアップデートする。
WAFの導入 (重要度: ★★★)
SQLインジェクション
等を狙った攻撃をブロックする。
FW/iptablesの設定 (重要度: ★★★)
必要最小限のポート以外は塞ぐ。主に必要なことが多いポートは以下の通り。
ポート番号 | プロトコル | 方向 | 用途 |
---|---|---|---|
80 | TCP | IN | HTTPアクセスの受け入れ |
443 | TCP | IN | HTTPSアクセスの受け入れ |
25 | TCP | OUT | メール送信 |
53 | UDP | OUT | DNS問い合わせ |
80 | TCP | OUT | 外部へのHTTP通信 (外部APIとのやり取り、パッケージのアップデートなど) |
123 | UDP | OUT | NTPでの時刻同期 |
443 | TCP | OUT | 外部へのHTTPS通信 |
CDN
を導入している場合、CDN
のIPアドレス
以外からのHTTP/HTTPS
アクセスを受け入れないと尚良い。
セキュリティ診断サービスの利用 (重要度: ★★★)
他社にセキュリティ診断サービス
でサイトの脆弱性をチェックしてもらう。
IDS/IPSの導入 (重要度: ★★)
侵入検知、侵入防止
ウイルススキャンの実施 (重要度: ★★)
ClamAV
などを使って定期的にチェックを実施する。
ログ分析 (重要度: ★★)
logwatch
や商用のサービスなどを使ってログを集約して傾向を把握する。