kintone セキュアコーディングガイドライン 自習1日目「クロスサイトスクリプティング(XSS)について」
cybozuのkintoneはユーザー自身が簡単な操作でアプリを作成できるサービスです。
kintoneはJavaScriptプログラムを利用してアプリをカスタマイズすることができますが、カスタマイズによってセキュリティ上の問題が発生しないように「セキュアコーディングガイドライン」に従うことが求められます。
1. クロスサイトスクリプティング(XSS)とは何か?
クロスサイトスクリプティング(XSS)については下記のリンク及び関連リンクに詳しく記載されています。
また、XSSを防ぐ対策としては下記のリンク及び関連リンクに詳しく記載があります。
1.1 発生しうる脅威についての検証
セキュアコーディングガイドラインには、XSSに脆弱なプログラムにより以下が発生する可能性があると言っています。
1. kintone のデータが盗み出される
2. 偽の画面が表示される
3. 悪意ある Cookie が Web ブラウザーに保存される
XSS脆弱性の1例として、下記のような例が記載されていますので見ていきます。
var text1 = document.getElementById('text1');
var div1 = document.getElementById('div1');
div1.innerHTML = '<input type="text" value="' + text1.value + '" />';
具体的なカスタマイズが不明ですが、想像でカスタマイズビューに入力フォームを作成して同様の動作を確認してみました。
(function() {
'use strict';
kintone.events.on('app.record.index.show', (event) => {
const body = {
'app': kintone.app.getId(),
};
kintone.api(kintone.api.url('/k/v1/app/views', true), 'GET', body, function(resp) {
console.log(resp);
if(resp.views.sample1.name === 'sample1') {
const btn = document.getElementById('send');
btn.addEventListener('click', () => {
const text1 = document.getElementById('text1');
console.log(text1);
const div1 = document.getElementById('div1');
console.log(div1);
div1.innerHTML = '<input type="text" value="' + text1.value + '" />';
});
}
}, function(error) {
console.log(error);
});
});
})();
フォームに入力すると、入力したプログラムが実行されます。
"onclick="alert(1)
1.2 何が問題なのか?
前段のプログラムでは、入力フォームの値からプログラムが埋め込まれて実行されました。前段の例ではアラートが表示されるだけでしたが、例えば以下のようにすれば新規ウィンドウを開くことができます。
"onclick="window.open('http://www.yahoo.co.jp', '_blank');
プログラムによっては、入力したデータが外部のサイトのプログラムに渡されるということもありえるということだと思います。
1.3 対策についての確認
XSSについての対策例が挙げられていますので確認します。
1.3.1 出力する全ての要素に対して、エスケープ処理を施す
この場合の出力先としては次が考えられます。
1. 外部へのリクエストに利用するURLパラメータ
(GET・POSTなどで送信されるデータ)
2. 動的にHTMLを生成する処理
1. については、encodeURIComponent() を利用してURLパラメータをエスケープできる。
2. については例えばフォームの入力値からDOMの追加・生成を行う処理がありますが、その際には innerHTML の代わりに textContent を利用する方法があります。
innerHTMLを利用しないで、動的にHTMLを生成するには、createElement() などのDOM操作用のメソッドを利用してHTMLを生成する方法があります。
(function() {
'use strict';
kintone.events.on('app.record.index.show', (event) => {
const body = {
'app': kintone.app.getId(),
};
kintone.api(kintone.api.url('/k/v1/app/views', true), 'GET', body, function(resp) {
console.log(resp);
if(resp.views.sample1.name === 'sample1') {
const btn = document.getElementById('send');
btn.addEventListener('click', () => {
const text1 = document.getElementById('text1');
const div1 = document.getElementById('div1');
const input1 = document.getElementById('newInput');
if (!input1) {
let newInput = document.createElement('input'); // DOM生成
newInput.setAttribute('id', 'newInput'); // DOM要素追加
newInput.setAttribute('type', 'text'); // DOM要素追加
newInput.value = text1.value; // 入力値の追加
div1.appendChild(newInput); // DOMに追加
} else {
input1.value = text1.value;
}
});
}
}, function(error) {
console.log(error);
});
});
})();
自前でHTMLエスケープ処理をしても良いですし、それ用のライブラリ(DOMPurify)を利用する方法やVue.js, React.js を利用してXSS対策をするなども良いかと思います。
DOMPurifyの利用例
import 'https://js.cybozu.com/dompurify/2.2.7/purify.min.js'
var dirty = '<div><p>テキスト</p><script>alert("アラート");</script></div>'
var clean = DOMPurify.sanitize(dirty)
console.log(clean); // => '<div><p>テキスト</p></div>'
1.3.2 出力する URL は「http://」または 「https://」で始まる URL だけにする
外部からの入力をもとに a タグの href 属性や img タグの src 属性などのURLを動的に生成する場合は、「javascript:」などから始まる文字列を入力してスクリプトを埋め込まれる場合があります。これを防ぐには、「http://」と 「https://」で始まる URL のみを出力するようにします。
具体的には、フォームの入力値に URL()メソッドを利用する方法があります。(※ URL() は IEに対応していません)
const url = new URL('http://www.exsample.com/');
console.log(url.hostname); // www.exsample.com
1.3.3 外部からの入力値を使用した要素の生成は避ける
以下のような実装をしてはいけません。
var tag = document.createElement(“script”);
tag.innerText = untrusted;
以下のような実装とは、JavaScriptのプログラムから script要素を作成して外部入力されたプログラムを読み込むような実装かと思われます。
1.3.4 外部からの入力値を使用したスタイルシートの生成は避ける
これは記述の通り、css要素も外部からの入力をそのまま読み込むことは避けるということかと思います。
1.3.5 信頼できない外部サイトに置かれた JavaScript や CSS を読み込まない
取り込み先のスクリプトが変わり、ある日突然、データを盗み出すためのプログラムが作動するかもしれません。外部のスクリプトなどを取り込む場合は、そのサイトを信頼できるかどうか、十分吟味してください。
外部のライブラリなどのことかと思いますが、ライブラリを利用する時はセキュリティ対策がされたバージョンを利用するように、例えばkintoneのカスタマイズの場合は、cybozuのCDNからライブラリをリンクするようにするなどが必要になります。
サンプルプログラムは本番環境に利用する前には別途開発環境を用意して動作確認をする、また動作を理解した上で利用することが必要かと思います。
参考:
この記事が気に入ったらサポートをしてみませんか?