主な脆弱性
(脆弱性への対処法①)
・OSコマンド・インジェクション
・SQLインジェクション
・ディレクトリ・トラバーサル
・セッションハイジャック
(脆弱性への対処法②)
・クロスサイト・スクリプティング(XSS)
・クロスサイト・リクエストフォージェリー(CSRF)
・HTTPヘッダインジェクション
・クリックジャギング
脆弱性の具体例と対策
クロスサイト・スクリプティング(XSS)
XSSとは、動的にHTMLを生成する機能(JavaScriptなどによってHTMLが生成されている機能)において、悪意を持ったユーザーにHTML、JavaScript、CSSの変更がなされてしまうことです。
例えば、掲示板など自分の書いた情報が反映されるアプリがあるとした場合、
<script>window.onload=function(){document.getElementsByTagName('h1')[0].innerHTML='たろいも';}</script>
以上のようなスクリプトを投稿し、仮に掲示板にXSS対策がなされていない場合、
HTMLのh1タグの中身がすべて"たろいも"となってしまいます。
XSSの対処法は、X-XSS-Protectionを設定することです。
Internet Explorer, Chrome, Safariでは、X-XSS-Protectionレスポンスヘッダを設定することができます。これは、クロスサイトスクリプティング (XSS) 攻撃を検出したときに、ページの読み込みを停止するためのものです。
引用:X-XSS-Protection
Node.jsの場合は、Expressフレームワークのhelmetというモジュールを利用すれば簡単に、X-XSS-Protectionを導入できます。
//helmetモジュール
const helmet = require('helmet')
//X-XSS-Protectionの設定
app.use(helmet.xssFilter())
参考:XSS Filter
クロスサイト・リクエストフォージェリー(CSRF)
CSRFとは、掲示板や問い合わせフォームなどを処理するWebアプリケーションが、本来拒否すべき他サイトからのリクエストを受信し処理してしまいます。
参考:トレンドマイクロ
例えば、ヤフーニュースに脆弱性があったならば、以下の私の電話番号は〇〇-〇〇-〇〇ですという文が、ボタンを押したらコメント欄に投稿されてしまいます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>偽物の投稿フォーム</title>
</head>
<body>
<h1>偽物の投稿フォーム</h1>
<form method="post" action="https://headlines.yahoo.co.jp/cm/main?d=20191125-00615521-fullcount-base&expand_form">
<input type="hidden" name="content" value="私の電話番号は〇〇-〇〇-〇〇です">
<button type="submit">ボタン</button>
</form>
</body>
</html>
仮に脆弱性に問題があったならば、以上の「ボタン」が以下の「投稿する」ボタンと繋がっていることになる。
CSRFを防ぐためには、ワンタイムトークンを投稿フォームに埋め込み、トークンを消費する形で投稿をさせます。
ワンタイムトークンは一度しか利用できないため、「トークンが発行されてから使用するまで」、外部にトークンが漏れることがなければ勝手にリクエストが処理されることはありません。
HTTPヘッダインジェクション
HTTP通信では、はじめにユーザーが、ブラウザを利用してリクエストを送ります。
リクエストを受け取ったWebサーバーが、ユーザー(が利用するブラウザ)に対してレスポンスを返します。
HTTPヘッダインジェクションは、HTTPレスポンスヘッダの出力処理に対して行われます。具体的には「クッキーが勝手にセットされる」「他のURLへリダイレクトされる」などの問題が発生します。
参照:【HTTPヘッダインジェクション】の仕組みと対策のまとめ記事
以下はHTTPレスポンスの構成情報です。
改行の上がレスポンスヘッダ
改行の下がレスポンスボディ
となります。
HTTP/1.1 200 OK
Date: Wed, 18 Oct 2010 18:21:19 GMT
Server: Apache/1.3.12 (Unix) (Red-Hat/Linux)
Expires: -1
Accept-Ranges: bytes
Cache-Control: private, max-age=0
Content-Type: text/html; charset=UTF-8
<-----改行----->
<!DOCTYPE html>
<html itemscope="" itemtype="http://localhost:8000" lang="ja">
<head>
<title>たろいものごはん</title>
</head>
(以下略)
これに対して、例えば以下のURLにアクセスします。
http://localhost:8000?location=%0d%0d<script type=”text/javascript”>alert(“HTTPヘッダにインジェクション”);</script>
?location= の後に %0d%0d とありますが、これは改行を意味します。
つまり、以下のようなHTTPレスポンスとなってしまったということです。
HTTP/1.1 200 OK
Date: Wed, 18 Oct 2010 18:21:19 GMT
Server: Apache/1.3.12 (Unix) (Red-Hat/Linux)
Expires: -1
Accept-Ranges: bytes
Cache-Control: private, max-age=0
Content-Type: text/html; charset=UTF-8
Location:
<-----改行----->
<script type=”text/javascript”>alert(“HTTPヘッダにインジェクション”);</script>
<!DOCTYPE html>
<html itemscope="" itemtype="http://localhost:8000" lang="ja">
<head>
<title>たろいものごはん</title>
</head>
(以下略)
レスポンスヘッダのLocationの後に改行がなされ、
<script>がレスポンスボディと認識されてしまっています。
これはHTMLの改ざんなど、危険な状態です。
HTTPヘッダインジェクションの対策としては、
外部からのリクエストURLを、レスポンスヘッダの値へ設定しない。
リクエストURLを、レスポンスヘッダの値に設定する場合は「改行コード( %0d%0d)」をエスケープするなどの方法があります。
クリックジャギング
クリックジャギングとはiframeを使ったボタンの透明化などの設定をすることで、ユーザーの意図しないクリックを誘発することです。
<iframe>(インラインフレーム要素) は、入れ子になった閲覧コンテキストを表現し、現在のHTMLページに他のページを埋め込むことができます。
例えば、googleMapが埋め込んであるサイトはその例です。
これは、HTTPレスポンスヘッダ内のX-Frame-Optionsの設定をすることで防ぐことができます。X-Frame-Optionsは、HTMLにおけるiframeなどの「フレームというHTML内にHTMLを読み込む」機能に対して、どのようなサイトからの読み込みを許可するかという設定です。
//ページをフレーム内に表示することはできません。
X-Frame-Options: deny
//同じサイト内のページのみフレームに表示できる。
X-Frame-Options: sameorigin
//指定したURLのページのみフレームに表示できる。
X-Frame-Options: allow-from https://example.com/
Node.jsでは、Expressのhelmetモジュールを使い、X-Frame-Options:SAMEORIGINの設定をすることができます。
const helmet = require('helmet');
const app = express();
app.use(helmet.frameguard({ action: "sameorigin" }));