JavaScriptでセキュアなコーディングをするために気をつけること

著者名: 天野 祐介 (External link) (サイボウズ株式会社)

目次

はじめに

kintoneはJavaScriptを使って自由にカスタマイズできます。
カスタマイズにより独自のリッチなUIを構築したり、新しい機能を追加したりできますが、セキュアなコーディングをしないと クロスサイトスクリプティング (以下、XSS)などの脆弱性を作り込んでしまう危険性があります。

この記事では、JavaScriptでセキュアなコーディングをするための基本的なポイントを解説します。

脆弱性を生む主な原因

脆弱性を作り込む主な原因になるコードは、要素の動的な生成です。
特に、レコード情報などのユーザーが入力した値を使って要素を生成するときに脆弱性が発生しやすくなります。

対策

document.write()element.innerHTMLを使って要素を生成するときは、コンテンツとなる文字列を必ずHTMLエスケープするようにしましょう。
以下は、HTMLエスケープの実装のサンプルです。

1
2
3
4
5
6
7
8
function escapeHtml(str) {
  str = str.replace(/&/g, '&');
  str = str.replace(/</g, '&lt;');
  str = str.replace(/>/g, '&gt;');
  str = str.replace(/"/g, '&quot;');
  str = str.replace(/'/g, '&#39;');
  return str;
}

使用例は以下になります。

1
2
3
4
// 悪い例
element.innerHTML = '<span class="foo">' + value + '</span>';
// 良い例
element.innerHTML = '<span class="foo">' + escapeHtml(value) + '</span>';

適切にHTMLをエスケープすることは大原則ですが、それに加えて気を付けるべき点を以下にまとめます。

気を付けるべき点

信頼できない外部サイトからデータを取得しない

JSONPやkintone.proxy()を利用して外部サイトからデータを取得するときは、データの提供元が信頼できることを確認します。
また、以下のような場合も、必ず提供元を確認します。

  • imgタグやscriptタグのsrc属性を設定するとき
  • 外部サイトが提供するJavaScriptやCSSを利用するとき

JavaScriptコードを動的に生成しない

eval("...")<span onclick="...">のように、文字列としてJavaScriptコードを記述するのは避けます。
可読性の著しい低下やスコープの破壊、適切にエスケープできなくなるなどのリスクがあります。

JSONのパースにはJSON.parse() を使う

kintone.api()などで取得したJSONオブジェクトのパースには、JSON.parse()を使います。
JSONはJavaScriptのオブジェクトとしても評価可能なためeval()new Function()を使うこともできますが、エスケープの不備やXSSの恐れがあるため利用しないようにします。

URLのエンコードにはencodeURIComponent() を使う

aタグのhref属性(リンク先)をセットする場合やlocation.hrefを書き換える場合は、必ずencodeURIComponent()でエンコードします。

1
2
3
4
// 悪い例
location.href = '/k/1234/show?param=' + param;
// 良い例
location.href = '/k/1234/show?param=' + encodeURIComponent(param);

ユーザーに関係する情報をcookieやlocalStorageに保存しない

ユーザーの個人情報や入力したデータは、クライアントサイドストレージに保存するのは避けます。
意図せずブラウザー上にデータが残り続け、第三者に漏洩するのを防ぐためです。
たとえば、次のようなデータが該当します。

  • ログインID
  • プロフィール情報
  • レコード情報
  • アクセス権

サードパーティライブラリは常に最新版を利用する

ライブラリによっては特定のバージョンに脆弱性があり、読み込んでいるだけで攻撃可能になることがあります。
ライブラリのバージョンに起因する脆弱性を防ぐため、常に最新版を利用するようにしましょう。

脆弱性の高いデータソース

基本的にどんな要素を生成するときもエスケープ処理は必須ですが、以下のデータは特に脆弱性の原因になりやすく、注意が必要なものです。

  • レコード情報(ユーザーの入力値)
  • 外部から取得したデータ(第三者からのデータ)
  • URLパラメーター(ユーザーの入力値)

location.hreflocation.hashで取得できるURL情報から特定のパラメーターを抽出するといった処理はよくあります。
その際、URLはユーザーが任意の値を入力できるため、レコード情報などのユーザー入力値と同じように扱う必要があります。

脆弱性の高いJavaScriptのコード例

脆弱性を特に引き起こしやすいJavaScriptのコード例を挙げます。
これらを使うときは適切なエスケープが必須になります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 要素の生成
document.write('...');
element.innerHTML = '...';

// 属性の設定
element.setAttribute(name, value);
element.someAttribute = '...'; // someAttributeはhref,onclick,style,srcなど

// URL操作
location.href = '...';

また、URL操作時はエンコードに加えて遷移先が信頼できるサイトだと保証しなければなりません。

その他気を付ける点

同期XMLHttpRequestはなるべく使わない

同期XMLHttpRequestはなるべく使わないようにします。
通信中にブラウザーが固まってしまうパフォーマンスの問題や、Firefoxでは非推奨になっているためです。
詳細は、 同期と非同期のリクエスト (External link) (外部サイト)を確認してください。

サードパーティライブラリ使用時の注意点

jQueryに代表されるDOM操作を行ライブラリは、内部で操作しているDOM APIがユーザーから見えないため、知らないうちに前述の危険な操作を呼び出していることがあります。
たとえば、jQueryのhtml()メソッドは内部的にはinnerHTMLと同じなので、渡された文字列をそのままHTMLとして展開してしまいます。
そのため、コンテンツ部分はtext()を使ってエスケープするなどの対策が必要です。

1
2
3
4
// 悪い例
$(el).html($('<span class="foo">' + value + '</span>'));
// 良い例
$(el).html($('<span class="foo">').text(value));

ライブラリを利用する際も、普段と同じようにエスケープに気を配ることが大事です。

おわりに

JavaScriptでセキュアなコーディングをするために気を付けることを解説しました。
安心して利用できるJavaScriptカスタマイズを作成する参考になれば幸いです。
こちらと合わせて、 kintoneセキュアコーディングガイドライン も確認してください。