XSSとは?クロスサイトスクリプティングの仕組みと対策を初心者向けに解説

学習ロードマップ

XSSは、Webサイトの画面に悪意あるスクリプトを実行させてしまうセキュリティ上の問題です。

正式には、クロスサイトスクリプティングと呼ばれます。

Webフォーム、コメント欄、検索結果、管理画面など、ユーザーが入力した内容を画面に表示する場所で起きる可能性があります。

Webプログラマーを目指すなら、フォーム処理やデータベース表示を学ぶ段階で、XSSの基本も知っておく必要があります。

この記事では、XSSとは何か、どのような場面で起きるのか、PHPで画面に表示するときにどう対策するのかを初心者向けに解説します。

XSSとは

XSSとは、Webページに悪意あるスクリプトが埋め込まれ、ユーザーのブラウザ上で実行されてしまう攻撃です。

JavaScriptは、本来Webページに動きをつけるために使われます。

しかし、入力内容をそのまま画面に表示してしまうと、攻撃者が入力したJavaScriptもページの一部として実行される可能性があります。

たとえば、掲示板やコメント欄に入力された内容を、そのままHTMLとして表示している場合です。

本来は文章として表示したいだけなのに、ブラウザがHTMLやJavaScriptとして解釈してしまうと危険です。

XSSが起きると、次のような問題につながる可能性があります。

  • 偽の表示をページ内に出される
  • ユーザーの操作を不正に誘導される
  • Cookieや認証情報に関わる被害につながる
  • ログイン中のユーザーとして不正な操作をされる
  • サイトの信用を失う

初心者のうちは、まず「ユーザーが入力した内容をそのまま画面に出すのは危険」と覚えておくと分かりやすいです。

XSSが起きやすい場所

XSSは、ユーザーが入力した内容を画面に表示する場所で起きやすいです。

たとえば、次のような場所です。

  • お問い合わせフォームの確認画面
  • 掲示板の投稿一覧
  • ブログのコメント欄
  • 検索キーワードの表示
  • 会員プロフィール
  • 管理画面の一覧表示
  • URLパラメータを画面に表示するページ

フォームから送信された内容だけでなく、URLに含まれる値、データベースに保存された値、外部APIから受け取った値も注意が必要です。

自分が書いた固定のHTMLなら問題になりにくいですが、外部から入ってくる値を表示するときは対策が必要です。

危険な表示例

PHPでフォームの入力内容を画面に表示する例を見てみます。

次のコードは、入力された名前をそのまま表示しています。

<p>
  入力された名前:
  <?php echo $_POST["name"]; ?>
</p>

この書き方では、入力内容がそのままHTMLに出力されます。

通常の名前だけなら表示できますが、HTMLタグのような文字列が入力された場合、ブラウザがHTMLとして解釈してしまう可能性があります。

そのため、ユーザーが入力した内容を画面に表示するときは、そのままechoしないようにします。

フォーム処理を作るときは、入力値を受け取ることだけでなく、表示するときの安全性も考える必要があります。

基本対策はエスケープして表示すること

XSS対策の基本は、ユーザー入力を画面に表示するときにエスケープすることです。

エスケープとは、HTMLとして解釈される可能性がある文字を、文字として表示できる形に変換することです。

PHPでは、画面に表示するときにhtmlspecialcharsを使います。

<p>
  入力された名前:
  <?php echo htmlspecialchars($_POST["name"], ENT_QUOTES, "UTF-8"); ?>
</p>

htmlspecialcharsを使うと、HTMLタグとして解釈される可能性がある文字を変換できます。

たとえば、<>などが、そのままHTMLタグとして扱われにくくなります。

フォームから受け取った値を画面に出すときは、基本的にこのような処理を入れます。

htmlspecialcharsの使い方

htmlspecialcharsは、PHPでXSS対策をするときによく使う関数です。

基本形は次の通りです。

htmlspecialchars(表示したい値, ENT_QUOTES, "UTF-8")

たとえば、名前を安全に表示したい場合は、次のように書きます。

<?php
  $name = $_POST["name"];
  $safe_name = htmlspecialchars($name, ENT_QUOTES, "UTF-8");
?>

<p><?php echo $safe_name; ?>さん、こんにちは。</p>

ENT_QUOTESを指定すると、シングルクォートとダブルクォートの両方を変換対象にできます。

"UTF-8"は文字コードの指定です。

HTML側もUTF-8にしておきます。

<meta charset="UTF-8">

PHPでユーザー入力を表示する場合は、次のように考えると分かりやすいです。

  • 受け取るだけなら$_POST$_GET
  • 画面に表示するときはhtmlspecialchars
  • データベースに保存するときはSQL対策も別に必要

XSS対策とSQLインジェクション対策は別物です。

フォームを作る場合は、両方を分けて考える必要があります。

フォーム確認画面での対策例

お問い合わせフォームでは、入力内容を確認画面に表示することがあります。

その場合も、入力された内容をそのまま表示しないようにします。

例として、名前、メールアドレス、本文を受け取る場合を考えます。

<?php
  $name = $_POST["name"];
  $email = $_POST["email"];
  $message = $_POST["message"];

  $safe_name = htmlspecialchars($name, ENT_QUOTES, "UTF-8");
  $safe_email = htmlspecialchars($email, ENT_QUOTES, "UTF-8");
  $safe_message = htmlspecialchars($message, ENT_QUOTES, "UTF-8");
?>

<h1>入力内容の確認</h1>

<p>お名前:<?php echo $safe_name; ?></p>
<p>メールアドレス:<?php echo $safe_email; ?></p>
<p>お問い合わせ内容:<?php echo nl2br($safe_message); ?></p>

nl2brは、改行を<br>に変換するために使っています。

ここで重要なのは、先にhtmlspecialcharsでエスケープしてから表示していることです。

お問い合わせ内容のような自由入力欄は、特に注意して扱います。

入力チェックだけではXSS対策にならない

入力チェックは大切ですが、入力チェックだけではXSS対策として不十分です。

たとえば、名前欄が空欄でないか確認する処理は、入力チェックです。

if ($name === "") {
  echo "名前を入力してください。";
}

これは、未入力を防ぐための処理です。

しかし、入力された内容を安全に表示する処理ではありません。

XSS対策としては、表示するときのエスケープが必要です。

つまり、フォーム処理では次のように考えます。

処理目的
入力チェック空欄や形式の間違いを確認する
エスケープ画面に安全に表示する
SQL対策データベース操作を安全にする

入力チェックをしたからといって、画面表示時のエスケープを省略しないようにします。

JavaScriptでHTMLを入れるときの注意

XSSは、PHPだけの問題ではありません。

JavaScriptで画面を書き換えるときにも注意が必要です。

たとえば、ユーザー入力を使ってHTMLを組み立て、innerHTMLに入れる書き方は注意が必要です。

const name = input.value;
result.innerHTML = name;

innerHTMLは、文字列をHTMLとして解釈します。

そのため、外部から入ってきた値をそのまま入れるのは避けた方がよいです。

単純に文字として表示したい場合は、textContentを使います。

const name = input.value;
result.textContent = name;

textContentは、入力された内容を文字として扱います。

JavaScriptでDOM操作をするときも、「HTMLとして入れるのか、文字として入れるのか」を分けて考えることが大切です。

XSS対策で覚えておきたいこと

XSS対策では、次の点を覚えておくと分かりやすいです。

  • ユーザー入力をそのまま画面に表示しない
  • PHPで表示するときはhtmlspecialcharsを使う
  • HTML側の文字コードはUTF-8にする
  • JavaScriptではinnerHTMLの使い方に注意する
  • 文字として表示するならtextContentを使う
  • 入力チェックとエスケープは別の処理として考える
  • SQLインジェクション対策とは別に考える

また、フレームワークを使っている場合でも、すべて自動で安全になるとは限りません。

Vue.jsやReactなどは、通常のテキスト表示ではエスケープされる仕組みがあります。

しかし、HTMLを直接差し込む機能を使う場合は注意が必要です。

どの技術を使っていても、外部から入ってきた値を画面に出すときは慎重に扱います。

よくある危険な考え方

XSS対策でよくある危険な考え方は、「自分のサイトには攻撃されるほどの価値がない」と考えることです。

小さなサイトでも、フォームやコメント欄があれば攻撃対象になる可能性があります。

また、「入力欄にscriptと書けないようにすればいい」と考えるのも不十分です。

危険な文字列の形は一つではありません。

特定の単語だけを禁止する方法では、すべてのパターンに対応しきれません。

そのため、表示時に適切にエスケープすることが基本になります。

もう一つの危険な考え方は、「管理画面だから大丈夫」と考えることです。

管理画面であっても、外部から入ったデータを表示するなら対策は必要です。

まとめ

XSSは、Webページに悪意あるスクリプトが埋め込まれ、ユーザーのブラウザ上で実行されてしまう問題です。

フォーム、コメント欄、検索結果、プロフィール、管理画面など、ユーザー入力を表示する場所で起きる可能性があります。

PHPでユーザー入力を画面に表示する場合は、htmlspecialcharsを使います。

htmlspecialchars($value, ENT_QUOTES, "UTF-8")

JavaScriptで文字として表示したい場合は、innerHTMLではなくtextContentを使う方が安全です。

result.textContent = input.value;

入力チェックは、空欄や形式の確認をするための処理です。

XSS対策は、画面に表示するときのエスケープです。

この2つは別の処理として考えます。

Webプログラマーを目指すなら、フォーム処理やデータベース表示を学ぶ段階で、XSS対策もセットで覚えておきましょう。

タイトルとURLをコピーしました