Upgrade to Pro — share decks privately, control downloads, hide ads and more …

フロントエンドエンジニアのためのセキュリティ対策 ~XSS編~ / #frontkansai 2019

フロントエンドエンジニアのためのセキュリティ対策 ~XSS編~ / #frontkansai 2019

FRONTEND CONFERENCE 2019( https://2019.kfug.jp )でセキュリティ、主にXSSについて話をしました。
demo: https://shisama.dev/xss-test

# Technical Topics
- 3 types of XSS ( Reflected XSS, Stored XSS, DOM based XSS)
- XSS with React
- DOMPurify
- Content Security Policy
- Trusted Types

Masashi Hirano

November 02, 2019
Tweet

More Decks by Masashi Hirano

Other Decks in Programming

Transcript

  1. https://www.ipa.go.jp/security/vuln/report/vuln2019q3.html ෼ྨ ݄ʙ݄ ಧग़ड෇։͔࢝Βͷྦྷܭ ιϑτ΢ΣΞ੡඼ ݅  ݅ ΢ΣϒαΠτ ݅

     ݅ ߹ܭ ݅  ݅ 8FC͕ιϑτ΢ΣΞʹ ൺ΂ͯഒҎ্ͷಧग़਺ ಧग़݅਺ (2019೥7݄ʙ9݄)
  2. https://www.ipa.go.jp/security/vuln/report/vuln2019q3.html ΢ΣϒαΠτͷ੬ऑੑͷछྨผͷಧग़        XSS

    (ΫϩεαΠτɾεΫϦϓςΟϯά) DNSͷઃఆෆඋ SQLΠϯδΣΫγϣϯ HTTPͷෆਖ਼ར༻ σΟϨΫτϦɾτϥόʔαϧ ϑΝΠϧͷޡެ։ ͦͷଞ
  3. https://www.owasp.org/index.php/Japan " ΠϯδΣΫγϣϯ 42-ΠϯδΣΫγϣϯɺίϚϯυΠϯδΣΫγϣϯͳͲ " ೝূͷෆඋ Ϣʔβʔͷೝূ৘ใͷ࿙Ӯ " ػඍͳ৘ใͷ࿐ग़ ࡒ຿৘ใ΍ݸਓ৘ใͳͲ઄औɺվ͟Μ

    " 9.-֎෦ΤϯςΟςΟࢀর 99& ֎෦ΤϯςΟςΟʹΑΔϦϞʔτίʔυͷ࣮ߦͳͲ " ΞΫηε੍ޚͷෆඋ ଞͷϢʔβʔͷσʔλ΍ݖݶͷมߋ " ෆద੾ͳηΩϡϦςΟઃఆ ҆શͰͳ͍ઃఆʹΑΔ໰୊ " ΫϩεαΠτεΫϦϓςΟϯά 944 ϒϥ΢β্ͰͷεΫϦϓτ࣮ߦʹΑΔ৘ใ࿙͍͑ͳͲ " ҆શͰͳ͍σγϦΞϥΠθʔγϣϯ ϦϞʔτ͔Βͷίʔυ࣮ߦ " ط஌ͷ੬ऑੑͷ͋Δίϯϙʔωϯτͷ࢖༻ ੬ऑੑͷ͋ΔϥΠϒϥϦͳͲʹΑΔ߈ܸ΍ѱӨڹ " ෆे෼ͳϩΪϯάͱϞχλϦϯά ϞχλϦϯάͷෆඋʹΑΔ߈ܸݕ஌࿙Ε OWASP Top10 2017
  4. XSS

  5. Reflected XSS (൓ࣹܕXSS) <input type="hidden" name="token" value=#{qs.token} /> <input type="hidden"

    name="token" value=""/><script>alert("XSS")</script><div /> ΫΤϦετϦϯάͷ஋Λͦͷ··HTMLʹ൓ө
  6. Reflected XSS (൓ࣹܕXSS) <input type="hidden" name="token" value=#{qs.token} /> <input type="hidden"

    name="token" value=""/><script>alert("XSS")</script><div /> ΫΤϦετϦϯάͷ஋Λͦͷ··HTMLʹ൓ө
  7. DOM Based XSS const hash = decodeURIComponent(location.hash.slice(1)); document.querySelector('#result').innerHTML = hash;

    <img src=x onerror=alert('XSS')> ϩέʔγϣϯϋογϡͷ஋Λ JavaScriptͰૠೖ
  8. DOM Based XSS const hash = decodeURIComponent(location.hash.slice(1)); document.querySelector('#result').innerHTML = hash;

    <img src=x onerror=alert('XSS')> ϩέʔγϣϯϋογϡͷ஋Λ JavaScriptͰૠೖ
  9. DOM Based XSS const hash = decodeURIComponent(location.hash.slice(1)); document.querySelector('#result').innerHTML = hash;

    ιʔε (Source) γϯΫ (Sink) ୅දతͳSink - innerHTML - location.href - document.write - jQuery() ͳͲ ୅දతͳSource - location.hash - location.href - document.referrer - IndexedDB ͳͲ
  10. 3छྨͷXSS • Reflected XSS • Stored XSS • DOM Based

    XSS ࠷ऴతʹ͸ඃ֐ऀͷϒϥ΢β্Ͱ JavaScript͕࣮ߦ͞ΕΔ
  11. จࣈྻͷΤεέʔϓɾ࡟আॲཧͷ࣮૷ function sanitizer(str) { return str .replace(/&/g, "&amp;") .replace(/</g, "&lt;")

    .replace(/>/g, "&gt;") .replace(/"/g, "&quot;") .replace(/'/g, "&#039;") ... } ࣮૷͢Δ͕େม ςετ΋େม ࢓༷࿙Ε͍ͯͳ͍͔ෆ҆
  12. const App = props => ( <div className="App"> <script>alert(“xss")</script> </div>

    ); React DOM escapes any values by default 944͸ى͖ͳ͍
  13. const App = (props) => ( <div className="App"> <a href=“javascript:alert(“xss")">click

    me!</a> </div> ); React DOM escapes any values by default
  14. const App = (props) => ( <div className="App"> <a href=“javascript:alert(“xss")">click

    me!</a> </div> ); React DOM escapes any values by default
  15. ReactͷXSS • javascript:εΩʔϜͳͲଐੑ஋͸Τεέʔϓ͞Εͳ͍ • Ϣʔβʔ͕ೖྗͰ͖Ε͹XSS͸ൃੜ͢Δ <div className=“App"> <form> <input value={title}

    onChange={onChangeTitle} /> <input value={url} onChange={onChangeUrl} /> </form> <a href={url}>{title}</a> </div>
  16. const Link = props => { const protocol = new

    URL(props.url).protocol; const safeUrl = /^https?:/.test(protocol) ? props.url : ""; return <a href={safeUrl}>{props.children}</a>; }; const App = () => { const { url, title, onChangeUrl, onChangeTitle } = useInput(); return ( <div className="App"> <form> <input value={title} onChange={onChangeTitle} /> <input value={url} onChange={onChangeUrl} /> </form> <Link url={url}>{title}</Link> </div> ); };
  17. const Link = props => { const protocol = new

    URL(props.url).protocol; const safeUrl = /^https?:/.test(protocol) ? props.url : ""; return <a href={safeUrl}>{props.children}</a>; }; const App = () => { const { url, title, onChangeUrl, onChangeTitle } = useInput(); return ( <div className="App"> <form> <input value={title} onChange={onChangeTitle} /> <input value={url} onChange={onChangeUrl} /> </form> <Link url={url}>{title}</Link> </div> ); }; IUUQ·ͨ͸IUUQTϓϩτίϧͷΈڐՄ͢Δ͜ͱ ͰɺzKBWBTDSJQUz͔Β࢝·ΔจࣈྻΛແ֐Խ <a href="">click me!</a>
  18. DOMPurify const untrustedStr = location.hash; // <img src=x onerror="javascript:alert(1)" />

    const trustedStr = DOMPurify.sanitize(untrustedHTML); $('#foo').innerHTML = trustedStr; // <img src=x />
  19. DOMPurify const untrustedStr = location.hash; // <img src=x onerror="javascript:alert(1)" />

    const trustedStr = DOMPurify.sanitize(untrustedHTML); $('#foo').innerHTML = trustedStr; // <img src=x /> MPDBUJPOIBTIͷ஋͕ ͦͷ··4JOL͞ΕͨΒ εΫϦϓτ͕࣮ߦ͞ΕΔ
  20. DOMPurify const untrustedStr = location.hash; // <img src=x onerror="javascript:alert(1)" />

    const trustedStr = DOMPurify.sanitize(untrustedHTML); $('#foo').innerHTML = trustedStr; // <img src=x /> %0.1VSJGZTBOJUJ[FʹΑΓ ةݥͳจࣈྻ͚ͩআڈ͞ΕΔ
  21. ▼ Response Headers content-security-policy: script-src ‘self’ *.trusted.com σΟϨΫςΟϒ ιʔε •

    HTTPϨεϙϯεϔομʔʹ௥Ճ͢Δ • Ϧιʔεͷऔಘઌ΍εΫϦϓτͷ࣮ߦΛࢦఆͨ͠ιʔεͷΈʹ੍ݶͰ͖Δ • script-src ‘self’; img-src *.trusted.comͷ ͷΑ͏ʹෳ਺ઃఆ΋Մೳ script-src ‘self’; img-src *.trusted.com Content Security Policy
  22. <meta http-equiv="Content-Security-Policy" content="script-src 'self'"> Content Security Policy • HTMLͷ<meta>λάͰࢦఆͰ͖ΔͷͰϑϩϯτΤϯυ͚ͩͰ΋࣮૷Մೳ •

    <meta>ΑΓઌʹઃఆ͞Εͨ஋ͷ্ॻ͖ෆՄ • <meta>ΑΓઌʹಡΈࠐ·ΕͨϦιʔε΍εΫϦϓτʹ͸ద༻Ͱ͖ͳ͍ • <meta>Ͱ͸ઃఆͰ͖ͳ͍σΟϨΫςΟϒ͕͋Δ e.g. report-only
  23. ▼ Response Headers content-security-policy-report-only: script-src ‘self’ *.trusted.com report-uri /csp-report •

    ࣮ࡍͷCSPΛద༻ͤͣʹϨϙʔτ͚ͩૹΔ͜ͱ͕Մೳ • CSPͰڐՄ͞Ε͍ͯͳ͍৔߹ɺϨϙʔτΛPOST͢Δ • CSPͷຊ൪ಋೖલʹӨڹΛ֬ೝ͢Δ͜ͱ͕Մೳ • <meta>λάͰͷઃఆ͸ෆՄ Content-Security-Policy-Report-Only
  24. { "csp-report": { "document-uri": "http://example.com/index.html", "referrer": "", "blocked-uri": "http://invalid-cdn.com/js/react.js", "violated-directive":

    "script-src self *.trusted.com*", "original-policy": "script-src 'self' *.trusted.com; report-uri /csp-report/", "disposition": "report" } } Ϩϙʔτͷྫ
  25. content-security-policy: script-src ‘nonce-EDNnf03nceIOfn39fn3e9h3sdfa’ ‘strict-dynamic’ <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa"> const script = document.createElement('script');

    script.src = ‘/static/js/main.js’; document.head.appendChild(script); </script> CSP Lv.3 strict-dynamic QBSTFSJOTFSUFEͰͳ͍TDSJQUͷಈతੜ੒͕Մೳ ※parser-inserted = HTMLύʔαʔ΍XMLύʔαʔʹΑͬͯૠೖ͞ΕΔ͜ͱ
  26. Trusted Types URL String HTML String Script String Scritp URL

    TrustedURL TrustedHTML TrustedScript TrustedScriptURL TrustedTypes จࣈྻΛ҆શͳܕʹม׵ͯ͠ݕূ͢Δ
  27. content-security-policy: require-trusted-types-for ‘script’; trusted-types *; script-src ‘nonce-EDNnf03nceIOfn39fn3e9h3sdfa’ ‘strict-dynamic’ <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">

    const script = document.createElement('script'); script.textContent = location.hash.slice(1); document.head.appendChild(script); </script>
  28. content-security-policy: require-trusted-types-for ‘script’; trusted-types *; script-src ‘nonce-EDNnf03nceIOfn39fn3e9h3sdfa’ ‘strict-dynamic’ <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">

    const script = document.createElement('script'); script.textContent = location.hash.slice(1); document.head.appendChild(script); </script> 944͸ى͖ͳ͍
  29. content-security-policy: require-trusted-types-for ‘script'; trusted-types my-policy; const myPolicy = trustedTypes.createPolicy('my-policy', {

    createHTML: (s) => { return customSanitize(s) }, createURL: (s) => { /* ΤεέʔϓॲཧͳͲ */ }, createScript: (s) => { /* εΫϦϓτ಺༰ͷνΣοΫͳͲ */ }, }) Trusted Types Policy จࣈྻΛ҆શͳܕʹม׵͢ΔϙϦγʔΛੜ੒͢Δ
  30. Trusted Types Policy ϙϦγʔʹΑͬͯੜ੒͞Εͨܕ͸࣮ߦՄೳʹͳΔ const hash = decodeURIComponent(location.hash.slice(1)); const trustedHtml

    = myPolicy.createHTML(hash) document.body.innerHTML = trustedHtml; content-security-policy: require-trusted-types-for ‘script’; trusted-types my-policy;
  31. const myPolicy = trustedTypes.createPolicy("my-policy", { createHTML: (s) => { return

    DOMPurify.sanitize(s); } }); Trusted Types & DOMPurify const hash = decodeURIComponent(location.hash.slice(1)); const trustedHtml = myPolicy.createHTML(hash) document.body.innerHTML = trustedHtml; <img src=x onerror=“javascript:alert(‘xss’)”/> <img src=x />
  32. <script src="https://w3c.github.io/webappsec-trusted-types/dist/es5/trustedtypes.build.js" data-csp="trusted-types my-policy“></script> <script> trustedTypes.createPolicy('my-policy', ...); trustedTypes.createPolicy('unknown', ...); //

    throws document.body.innerHTML = 'foo'; // throws Trusted Types Polyfill npm CDN $ npm install trusted-types import {tt} from 'trusted-types' tt.createPolicy('my-policy', ...);
  33. • Demo Page: https://shisama.dev/xss-test/ • GitHub: https://github.com/shisama/xss-test/ • DOM Based

    XSS • CSP • Trusted Types ※devToolsͷconsoleΛ։͖ͳ͕Β֬ೝ͍ͯͩ͘͠͞
  34. ҎԼͷ੬ऑੑ͕هࡌ͞Ε͍ͯΔ • ̍ʣ SQL ΠϯδΣΫγϣϯ • ̎ʣ OS ίϚϯυɾΠϯδΣΫγϣϯ •

    ̏ʣ ύε໊ύϥϝʔλͷະνΣοΫʗ σΟϨΫτϦɾτϥόʔαϧ • ̐ʣ ηογϣϯ؅ཧͷෆඋ • ̑ʣ ΫϩεαΠτɾεΫϦϓςΟϯά • ̒ʣ CSRFʢΫϩεαΠτɾϦΫΤετɾ ϑΥʔδΣϦʣ • ̓ʣ HTTP ϔομɾΠϯδΣΫγϣϯ • ̔ʣ ϝʔϧϔομɾΠϯδΣΫγϣ ϯ • ̕ʣ ΫϦοΫδϟοΩϯά • ̍̌ʣόοϑΝΦʔόʔϑϩʔ • ̍̍ʣΞΫηε੍ޚ΍ೝՄ੍ޚͷܽ མ
  35. ηΩϡϦςΟνΣοΫʹ࢖͑Δ΋ͷҰྫ • Ϧετ • ΢Σϒ݈߁਍அ࢓༷ • OWASP Cheat Sheet Series

    • HTML5 Security Cheat Sheet • πʔϧ • OWASP ZAP • Vuls • VAddy
  36. ࢀߟ • ͦΖͦΖCSP Lv.2 nonce΍Ζ͏ - teppeis blog • Masato

    Kinugawa Security Blog: CVE-2018-5175: FirefoxͰCSPͷstrict-dynamic όΠύε • ҆શͳจࣈྻͰ͋ΔͱܕͰݕূ͢Δ Trusted Types ʹ͍ͭͯ - Jxck • Avoiding XSS in React is Still Hard - javascript-security - Medium • Securing Web Apps with Modern Platform Features (Google I/O ’19)