Vue.jsでWYSIWYGを実装する
WYSIWYGはたくさんパッケージがありますが、シンプルで無料なQuillを今回利用することにしました。
Quillの公式ドキュメントを翻訳したのは下記になります。
今回はVue.jsということもあり、QuillをVueパッケージ化させたvue-quill-editor
を利用していきます。
環境
- Laravel8
- Vue2
インストール
npmインストールの場合
npm install vue-quill-editor --save
// or
yarn add vue-quill-editor
CDNの場合
<link rel="stylesheet" href="path/to/quill.core.css"/>
<link rel="stylesheet" href="path/to/quill.snow.css"/> // テーマ:snow
<link rel="stylesheet" href="path/to/quill.bubble.css"/> // テーマ:bubble
<script type="text/javascript" src="path/to/quill.js"></script>
<script type="text/javascript" src="path/to/vue.min.js"></script>
<script type="text/javascript" src="path/to/dist/vue-quill-editor.js"></script>
<script type="text/javascript">
Vue.use(window.VueQuillEditor)
</script>
Mount
グローバルでマウント
import Vue from 'vue'
import VueQuillEditor from 'vue-quill-editor'
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme
Vue.use(VueQuillEditor, /* { default global options } */) // 共通オプション設定が不要な場合、第二引数は不要。componentでオーバーライド可能
Laravelのapp.jsの場合は下記になります。
import Vue from "vue";
// Quill
import VueQuillEditor from 'vue-quill-editor'
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme
Vue.use(VueQuillEditor) // importとこれだけで利用可能
ローカルでマウント
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import { quillEditor } from 'vue-quill-editor'
export default {
components: {
quillEditor
}
}
モジュールの登録
import Quill from 'quill'
import yourQuillModule from '../yourModulePath/yourQuillModule.js'
Quill.register('modules/yourQuillModule', yourQuillModule)
Component
<template>
<!-- Two-way Data-Binding -->
<quill-editor
ref="myQuillEditor"
v-model="content"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@ready="onEditorReady($event)"
/>
<!-- Or manually control the data synchronization -->
<quill-editor
:content="content"
:options="editorOption"
@change="onEditorChange($event)"
/>
</template>
<script>
// You can also register Quill modules in the component
import Quill from 'quill'
import someModule from '../yourModulePath/someQuillModule.js'
Quill.register('modules/someModule', someModule)
export default {
data () {
return {
content: '<h2>I am Example</h2>',
editorOption: {
// Some Quill options...
}
}
},
methods: {
onEditorBlur(quill) {
console.log('editor blur!', quill)
},
onEditorFocus(quill) {
console.log('editor focus!', quill)
},
onEditorReady(quill) {
console.log('editor ready!', quill)
},
onEditorChange({ quill, html, text }) {
console.log('editor change!', quill, html, text)
this.content = html
}
},
computed: {
editor() {
return this.$refs.myQuillEditor.quill
}
},
mounted() {
console.log('this is current quill instance object', this.editor)
}
}
</script>
実例
<template>
<quill-editor
name="body"
ref="myQuillEditor"
v-model="State.body"
:options="editorOption"
/>
</template>
<script>
import { Store } from '@/stores/Store.js';
export default {
data () {
return {
State: Store.state,
editorOption: {
theme: 'snow',
debug: false,
placeholder: 'お知らせの本文を入力してください',
modules: {
toolbar: [
['bold', 'italic', 'underline', 'strike'], // ボタンの表示オプション
[{ 'color': [] }, { 'background': [] }], // 選択肢が必要なカラーと背景色の表示オプション
[{ 'header': [1, 2, 3, 4, 5, 6, false] }], // Hタグの表示オプション
[{ 'size': ['small', false, 'large', 'huge'] }], // 文字サイズの表示オプション
[{ 'align': [] }], // 中央寄せなどの文字並びの表示オプション
]
},
}
}
},
computed: {
editor() {
return this.$refs.myQuillEditor.quill
}
},
}
</script>
XSS対策をしよう
さてここから問題です。
WYSIWYGはHTMLタグを許可しているので、XSS対策をしないといけません。
blade側での対策
blade側では特定のHTMLタグだけを許可し、他は文字として扱うようにしましょう。
例えば下記のようにHTMLタグのエスケープを解除したとしまよう。
<div>{!! $wysiwygText !!}</div>
HTMLのエスケープを解除しただけだと、scriptタグを入れることができてしまいます。
そこから好きなように悪さできてしまいます。
そこで、許可するhtmlタグを指定しましょう
<div>{!! strip_tags($wysiwygText, ['p', 'a', 'span', 'u', 'em', 'strong']) !!}</div>
validationでDB保存を防ぐ
先ほどblade側に表示するHTMLタグを制限しましたが、とはいえ何かの拍子にコードを変えて、scriptタグを許可してしまう瞬間があるかもしれません。
そうなると場合によっては情報流出になりかねません。
ということで、そんな爆弾を抱えないように、validationで弾いてしまいましょう!
PHPでは正規表現による判定は、preg_match()
を利用します。
preg_match('/正規表現のルール/', '対象の文字');
引数などの詳細は公式ドキュメントにて確認をしてください。
それでは<script>
をvalidationで弾きたいと思います。
preg_match('/<\s*script/', '<script');
少し分解して解説すると、下記になります。
< = <
\s = スペースキー
* = 一つ前のものが0回以上の繰り返し
script = script
これによる、<script
を検知しました。
じゃあ、これを実際にvaliationに組み込むと検知されません。
'not_regex:/<\s*script/'
なぜvadalidationで弾けないのでしょうか?
それは<
の表示が異なるためです。
<
は<
として投稿されます 。
なので、<
に合わせて正規表現のルールを変えなければなりません。
'not_regex:/\<\s*script/'
これだとscriptタグを弾くことができます。
コメント