์ฐพ์๋ณด๊ฒ ๋ ๊ณ๊ธฐ
: ํ๋ก์ ํธ ์งํ ์ค์ ์ด๋๋ฏผ ์ชฝ์์ ์๋ํฐ์ ์คํฌ๋ฆฝํธ ํ๊ทธ ์ฝ์
์ ํ์ฉํด๋ฌ๋ผ๋ ์์ฒญ์ด ๋ค์ด์๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ์ผ๋ UI ์๋ํฐ์์๋ ์คํฌ๋ฆฝํธ ํ๊ทธ๊ฐ ์
๋ ฅ๋๋ฉด k-script๋ก escape ์ฒ๋ฆฌ๋ฅผ ํ๊ฑฐ๋ ์์ ์คํฌ๋ฆฝํธ ํ๊ทธ๋ฅผ ์ญ์ ์์ผ ๋ฒ๋ ค์ ์คํฌ๋ฆฝํธ ํ๊ทธ ์
๋ ฅ์ด ์ด๋ ค์ด ์ํฉ์ด์๋ค.
ํด๊ฒฐํ ๋ฐฉ๋ฒ
: ์ฐ์ kendo Editor ์ต์
์ค์์ serialization: { scripts: true, }
๋ฅผ ์ ์ฉํด์ script ํ๊ทธ์ ์
๋ ฅ์ ํ์ฉํ๋ค. (๊ด๋ จ ๋ฌธ์)
: ์ผ๋ ์๋ํฐ๋ ๊ธฐ๋ณธ์ ์ผ๋ก XSS ๊ธฐ๋ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด์ ์
๋ ฅ๋ ์คํฌ๋ฆฝํธ ํ๊ทธ๋ฅผ serialization ํ์ง ์์๊ธฐ ๋๋ฌธ์, ๊ฐ์ ๋ก์ด ์ต์
์ true๋ก ๋ณ๊ฒฝํด์ค์ผ ํ๋ค. (๊ด๋ จ ๋ฌธ์)
: ํ์ง๋ง ๊ทธ ๋ค์์ ๋ฐ์ํ ๋ฌธ์ ๋ ์ฒ์ ์
๋ ฅ์์๋ scriptํ๊ทธ๊ฐ ๋ ์๊ฐ์ง ์๊ณ , ์๋ฒ๋ก ์ ์ ์ก์ด ๋์๋๋ฐ ์๋ํฐ์ ์๋ฒ์์ ๋ฐ์์จ ๊ฐ์ ๋ฃ์ ๋ script๋ผ๋ ๋จ์ด๊ฐ ์ ๋ถ x-script๋ก ๋ณ๊ฒฝ๋๋ ์ด์๊ฐ ๋ฐ์ํ๋ค.
: ์๋ฒ์์ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ ๋ฌธ์ ์๋ ๊ฒ์ผ๋ก ๋ณด์ ์๋ฒ๋ก ์ ์ก๋ ๋ ๋ณ๊ฒฝ๋๋ ๊ฒ์ด ์๋๋ผ, kendo Editor์ ๊ฐ์ผ๋ก ๋ฃ๊ธฐ ์ ์ฆ, textarea์ value์ ๊ฐ์ ๋ฃ์ ๋ ๋ฌธ์ ๊ฐ ๋๋ ๊ฒ์ ์์๋ค.
: ๋ฐ๋ผ์, ๊ธฐ์กด์ textarea.val()์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๋ ๊ฒ์ด ์๋๋ผ $(textarea).data("kendoEditor").value()
์ ์ง์ ๊ฐ์ ์ ๋ฌํ๋ค.
: ๋ํ, ๊ธฐ์กด์๋ ํ๊ทธ๋ค์ด <์ ๊ฐ์ด ์ธ์ฝ๋ฉ๋์ด์ ์๋ฒ์ ์ ์ก๋์๋๋ฐ, ์ธ์ฝ๋ฉ์ ํ์ง ์๊ณ , ํ๊ทธ ์์ฒด๋ฅผ ๋ฌธ์์ด๋ก ๋ฌถ์ด์ ์ ์กํ๊ณ ๊ทธ๋๋ก ๋ฐ์์์ ํด๊ฒฐํ๋ค.
๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ(๊ธฐ๋ฒ)
: ์
๋ ฅ ๊ฐ(html ์์ค)๋ฅผ ๊ฒ์ฌํด์ ์
์์ ์ธ ์ฝ๋๊ฐ ์์ผ๋ฉด ์ ์ ํ ์ฒ๋ฆฌ๋ฅผ ํด์ค๋ค.
XSS๋ฅผ ์ฐํํด์ ์คํฌ๋ฆฝํธ ํ๊ทธ๋ฅผ innerHTML๋ก ์ฝ์ ํ๋ ๋ฐฉ๋ฒ
<!-- ์ฝ์
ํ ์คํฌ๋ฆฝํธ -->
<script id="test-script" type="text/javascript">alert(123);</script>
// ์คํฌ๋ฆฝํธ ํ๊ทธ์ ๋ด์ฉ์ ๋ฌธ์์ด๋ก ๊ฐ์ ธ์จ๋ค.
const content = document.querySelector("#test-script").innerHTML; // "alert(123);"
// ๊ฐ์ ธ์จ ๋ฌธ์์ด์ eval ํจ์์ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ๋ฉด ํด๋น ์คํฌ๋ฆฝํธ์ ๋ด์ฉ์ ์คํ์ํฌ ์ ์๋ค!
eval(content);
var arr = [];
$('.detail').children.forEach( v=> {
if(v.tagName.toUpperCase() == 'SCRIPT') {
var tag = document.createElement('script');
tag.innerHTML = v.innerHTML;
v.attributes.forEach( attr => {
tag.setAttribute(attr.name, attr.value);
});
arr.push(tag);
v.remove();
}
});
arr.forEach( tag => {
$('.detail').appendChild(tag);
})
์ฌ์ฉํ ์ด์ : ํ์ด์ง์์ API ์กฐํ๊ฐ ์๋ฃ๋ ํ ํ๋ฉด์ ๋ ๋๋ง๋ ์ดํ์ ํน์ ๋ก์ง์ ์คํํด์ผ ํ๋๋ฐ, ์ต์ด 1ํ๋ง ์คํ์ํค๊ธฐ ์ํด์ ํ๋๊ทธ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ค.
์ฌ์ฉํ ๋ฐฉ๋ฒ: isInit
์ด๋ผ๋ ๋ณ์๋ฅผ ์์ฑ, ์ต์ด Vue ์ธ์คํด์ค๊ฐ ์์ฑ๋์์ ๋๋ false๋ก ์ค์ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ํน์ ๋ก์ง์ ์คํํ๊ธฐ ์ ์, isInit
์ ๊ฐ์ด true์ธ์ง ์ฒดํฌ ํ true๋ฉด ํด๋น ํจ์๋ฅผ ์คํํ์ง ์๊ณ ์ข
๋ฃํ๋ค.
isInit
์ด false๋ผ๋ฉด ์์ง ๋ก์ง์ ์คํํ ์ ์ด ์์๋ค๋ ๋ป์ด๋ฏ๋ก ํด๋น ๋ก์ง์ ์คํํ ํ์ isInit์ ๊ฐ์ true๋ก ๋ณ๊ฒฝํ๋ค.
data : {
isInit: false,
}
updated() {
if( this.isInit ) return;
// ... ์ํ๋ ๋ก์ง ์คํ
this.isInit = true;
}
arguments๋ ๋ฐฐ์ด์ด ์๋๋ค!
// 1.
Array.from(arguments);
// 2.
Array.prototype.slice.call(arguments);