CSRF保護

CSRF(Cross-Site Request Forgery:クロスサイトリクエストフォージェリ)は、ユーザーが意図しないリクエストを第三者が送信する攻撃手法です。攻撃者は、認証されたユーザーを利用してそのユーザーの権限で悪意ある操作を行わせることを狙います。

a-blog cms を利用すればCSRF攻撃からサイトを簡単に保護できます。

CSRF攻撃の防止

a-blog cms では、CSRFトークンを使用してCSRF攻撃に対応しています。

システムによって管理されているユーザーセッションごとにCSRF「トークン」を自動的に生成します。 このトークンはユーザーのセッションに保存され、CMSが生成するHTMLにも自動的に埋め込まれるようになっております。

ユーザーが何かリクエストする際には、テンプレートに埋め込まれたCSRFトークンも一緒に送信することで、ユーザーセッションに保存されているトークンと比較し、悪意あるリクエストを排除することが出来るようになります。

以下では、CSRFトークンが使用されるリクエストや、CSRFトークンの生成方法について解説します。

CSRFトークンが必要な操作

a-blog cms で CSRFトークンが必要な操作について列挙します。

POST操作

POSTモジュールは基本的にCSRFのチェックを行いますが、例外として以下のPOSTモジュールはCSRFトークンをチェックしません。

  • ACMS_POST_2GET
  • ACMS_POST_2GET_Ajax
  • ACMS_POST_Download
  • ACMS_POST_Login_Check
  • ACMS_POST_Member_SigninRedirect

Ajax(ポストインクルード)によるGETリクエスト

通常GETリクエストは、CSRFトークンをチェックしませんが、Ajaxリクエストの場合、CSRFトークンによる認証をする場合があります。

URLコンテキストで「tpl」が指定されている場合

通常以下のようなURLコンテキストにtplが入ったURLは404になり取得できません。

https://example.com/news/tpl/search.html

ただAjaxの場合は、CSRFトークンを使用して取得することが可能です。

取得HTMLが部分HTMLだった場合

通常a-blog cmsは、部分的なHTMLの取得を許可しませんが、Ajaxの場合は、CSRFトークンを使用することで部分的なHTML取得が可能になります。

CSRFトークンによるチェックをするには、private/config.system.yaml で「ajax_security_level」が「2」に設定されている必要があります。

ajax_security_level: 2 # ajaxリクエストのセキュリティレベルを設定します。(0: チェックなし 1: RefererとHttpヘッダーを確認 2: CSRFトークン確認)

CSRFトークンの生成条件

以下の条件の場合にユーザーセッションをスタートさせ、CSRFトークンを生成します。

  • ログインしている時
  • POSTリクエストの時
  • 「ACMS_POST_Form_」から始まる文字列がテンプレートにある時
  • 「ACMS_POST_Comment_」から始まる文字列がテンプレートにある時
  • 「ACMS_POST_Shop」から始まる文字列がテンプレートにある時
  • 「ACMS_POST_2GET_Ajax」から始まる文字列がテンプレートにある時
  • テンプレートに「check-csrf-token」文字列がある時
  • シークレットブログ・カテゴリーだった時
  • ログインページなど認証系の画面の時
  • phpで「IS_OTHER_LOGIN」定数が定義されている時

CSRFトークンを生成によりセッションスタートしてしまうことで、ブラウザキャッシュやCDNなどでキャッシュが利用できなくなってしまいます。必要な場合のみCSRFトークンを生成するようにしています。

CSRFトークンの埋め込み場所

生成されたCSRFトークンはレスポンスされるHTMLに埋め込まれます。

form要素

HTMLのform要素内の最後に、自動でinput要素が埋め込まれます。

<form method="post">
  ...
  <input type="hidden" name="formToken" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
</form>

metaタグ

HTMLのメタタグとして、head要素内に自動で埋め込まれます。

<meta name="csrf-token" content="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">

a-blog cms の基本機能を利用する場合、基本的には何も設定しなくてもCSRFトークンを含めた形でリクエストする形になっているため対応は不要です。

標準機能外でCSRFトークンを利用する

標準機能は、特に何もしなくてもCSRF対策がされていますが、独自にJavaScriptでリクエストを送ってレスポンスを受け取る場合などは、自身でCSRFトークンをリクエストに含める必要があります。

POSTリクエストの場合

POSTデータに以下情報を含める必要があります。

formToken: xxxxxxxxxxxxxxx

独自に開発したPOSTモジュールにJavaScriptからリクエストする例

const csrfToken = document.querySelector('meta[name="csrf-token"]').content; // メタタグからCSRFトークンを取得
const formData = new FormData();
formData.append('ACMS_POST_CustomModule', 'exec');
formData.append('formToken', csrfToken);

const response = await fetch(url, {
  method: 'POST',
  body: formData,
});

GETリクエストの場合

HTTPヘッダーに以下情報を必要があります。

Referer: https://example.com/xxxxxxx
X-Requested-With: XMLHttpRequest
X-Csrf-Token: xxxxxxxxxxxxxxxxxxx

HTMXライブラリ にCSRFトークンをセットする例

document.addEventListener("htmx:configRequest", function(event) {
  const csrfToken = document.querySelector('meta[name="csrf-token"]').content; // メタタグからCSRFトークンを取得
  event.detail.headers['X-CSRF-Token'] = csrfToken;
});