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

フロントエンド開発のためのセキュリティ入門

 フロントエンド開発のためのセキュリティ入門

Developers Summit 2023 10-A-4 「フロントエンド開発のためのセキュリティ入門」の発表資料です。
https://event.shoeisha.jp/devsumi/20230209/session/4176/

「HTTPS化」「CORS」「XSS」「脆弱なライブラリのチェック」について説明しています。

Masashi Hirano

February 10, 2023
Tweet

More Decks by Masashi Hirano

Other Decks in Programming

Transcript

  1. WebΞϓϦέʔγϣϯͰൃੜ͢ΔηΩϡϦςΟϦεΫ 08"415PQ " ΞΫηε੍ޚͷෆඋ " ҉߸Խͷࣦഊ " ΠϯδΣΫγϣϯ " ҆શ͕֬ೝ͞Εͳ͍ෆ҆ͳઃܭ

    " ηΩϡϦςΟͷઃఆϛε " ੬ऑͰݹ͘ͳͬͨίϯϙʔωϯτ " ࣝผͱೝূͷࣦഊ " ιϑτ΢ΣΞͱσʔλͷ੔߹ੑͷෆ۩߹ " ηΩϡϦςΟϩάͱϞχλϦϯάͷࣦഊ " αʔόʔαΠυϦΫΤετϑΥʔδΣϦ
  2. • ҉߸Խͷࣦഊ ➡ ௨৴σʔλͷ҉߸Խ • ΞΫηε੍ޚͷෆඋ ➡ CORSΛ࢖ͬͨΞΫηε੍ޚ • ΠϯδΣΫγϣϯ

    ➡ XSSରࡦ • ੬ऑͰݹ͘ͳͬͨίϯϙʔωϯτ ➡ ੬ऑͳϥΠϒϥϦͷνΣοΫ ࠓճ͓࿩͢ΔηΩϡϦςΟϦεΫ
  3. • ҉߸Խͷࣦഊ ➡ ௨৴σʔλͷ҉߸Խ • ΞΫηε੍ޚͷෆඋ ➡ CORSΛ࢖ͬͨΞΫηε੍ޚ • ΠϯδΣΫγϣϯ

    ➡ XSSରࡦ • ੬ऑͰݹ͘ͳͬͨίϯϙʔωϯτ ➡ ੬ऑͳϥΠϒϥϦͷνΣοΫ ࠓճ͓࿩͢ΔηΩϡϦςΟϦεΫ
  4. Webϖʔδͷදࣔ͸ϒϥ΢βͱαʔόͷ௨৴͔Β͸͡·Δ HTML JS WebαΠτͷαʔό 
 site.example ଞͷWebαΠτ΍CDNͷαʔό JSON Ϣʔβ͕WebαΠτ΁ΞΫηε HTMLͳͲͷϦιʔεΛૹ৴

    WebαΠτͱ͸ผͷαʔό΁ϦΫΤετ ϦιʔεΛૹ৴ ௨৴ʹ͸)551ͱ͍͏ϓϩτίϧ ͕࢖ΘΕ͍ͯΔ ௨৴్தͰ߈ܸΛ͞ΕΔͱɺϒϥ΢β ΍αʔόͰରࡦ͍͍ͯͯ͠΋ແҙຯ
  5. • ҉߸Խͷࣦഊ ➡ ௨৴σʔλͷ҉߸Խ • ΞΫηε੍ޚͷෆඋ ➡ CORSΛ࢖ͬͨΞΫηε੍ޚ • ΠϯδΣΫγϣϯ

    ➡ XSSରࡦ • ੬ऑͰݹ͘ͳͬͨίϯϙʔωϯτ ➡ ੬ऑͳϥΠϒϥϦͷνΣοΫ ࠓճ͓࿩͢ΔηΩϡϦςΟϦεΫ
  6. • ҉߸Խͷࣦഊ ➡ ௨৴σʔλͷ҉߸Խ • ΞΫηε੍ޚͷෆඋ ➡ CORSΛ࢖ͬͨΞΫηε੍ޚ • ΠϯδΣΫγϣϯ

    ➡ XSSରࡦ • ੬ऑͰݹ͘ͳͬͨίϯϙʔωϯτ ➡ ੬ऑͳϥΠϒϥϦͷνΣοΫ ࠓճ͓࿩͢ΔηΩϡϦςΟϦεΫ ଞʹ΋42-ΠϯδΣΫγϣϯͳͲ͕ ͋Δ͕ࠓ೔͸944͚ͩઆ໌͠·͢
  7. ൓ࣹܕXSS (Re fl ected XSS) ᶃ ߈ܸऀ͕༻ҙͨ͠ϖʔδʹΞΫηε ᶄ εΫϦϓτΛύϥϝʔλʹ ͯ͠ର৅αΠτʹભҠ

    <script>alert("XSS")</script> ?token=“”/><script>alert(“XSS")</script><div ᶅ εΫϦϓτ͕ 
 ຒΊࠐ·ΕͨHTML
  8. ஝ੵܕXSS (Stored XSS) ᶃ ϑΥʔϜ͔ΒPOST ᶄ POST͞Εͨ ஋Λͦͷ··อଘ <script>alert(“xss")</script> <li

    class="item"><script>alert(1)</script></li> ᶅ ଞͷϢʔβʔ͕αΠτʹΞΫηε ᶆ DBͷ஋ΛHTMLʹ൓ө
  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. javascript:εΩʔϜͷURLΛຒΊࠐ·ͳ͍ • ͨͱ͑͹ɺϦϯΫͷURLΛೖྗͰ͖ΔϑΥʔϜ͕͋ͬͨͱ͢Δ • ϑΥʔϜʹhttps://example.comͱೖྗͨ͠Βhttps://example.com ΁ͷϦϯΫ͕ੜ੒͞ΕΔͱ͢Δ <div className=“App"> <form> <input

    value={title} onChange={onChangeTitle} /> <input value={url} onChange={onChangeUrl} /> </form> <a href={url}>{title}</a> </div> ೚ҙͷ63-ΛઃఆՄೳͱ͢Δ
  11. javascript:εΩʔϜͷURLΛຒΊࠐ·ͳ͍ • ϑΥʔϜʹjavascript:alert(“xss”)ͱೖྗ͞ΕΔͱɺΫϦοΫͨ͠ͱ͖ ʹalert(“xss”)͕࣮ߦ͞ΕΔϦϯΫ͕Ͱ͖ͯ͠·͏ const App = (props) => (

    <div className=“App"> <form> <input value={title} onChange={onChangeTitle} /> <input value={url} onChange={onChangeUrl} /> </form> <a href=“javascript:alert(“xss")">click me!</a> KBWBTDSJQUεΩʔϜΛ࢖͑͹೚ҙͷ εΫϦϓτΛૠೖͰ͖Δ
  12. javascript:εΩʔϜͷURLΛຒΊࠐ·ͳ͍ 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 = (props) => { return ( <div className="App"> <Link url={url}>{title}</Link> </div> ); }; IUUQ·ͨ͸IUUQT͔Β͸͡·Δ63-͚ͩΛ ઃఆ͢ΔΑ͏ʹϑΟϧλϦϯά͢Δ
  13. DOMPurifyΛ࢖ͬͨةݥͳจࣈྻͷআڈ • DOMPurify͸OSSͷϥΠϒϥϦ • npm΍CDN͔Βμ΢ϯϩʔυͯ͠࢖͏͜ͱ͕Ͱ͖Δ • จࣈྻ͔ΒXSSΛى͜͢ةݥͳจࣈྻΛ࡟আͯ͘͠ΕΔ const untrustedStr =

    location.hash; // <img src=x onerror="javascript:alert(1)" /> const trustedStr = DOMPurify.sanitize(untrustedStr); $('#foo').innerHTML = trustedStr; // <img src=x />
  14. Sanitizer APIΛ࢖ͬͨةݥͳจࣈྻͷআڈ • ϒϥ΢βʹ૊Έࠐ·Ε͍ͯΔAPI • Ͱ͖Δ͜ͱ͸DOMPurifyͱࣅ͍ͯͯةݥͳจࣈྻΛআڈ͢Δ const untrustedStr = location.hash;

    // <img src=x onerror="javascript:alert(1)" /> 
 const sanitizer = new Sanitizer(untrustedStr); const target = document.getElementById(untrustedStr); target.setHTML(untrustedStr, { sanitizer }); // <img src=x />
  15. Content Security Policyͷ࢖༻ํ๏ • HTTPϨεϙϯεϔομʔʹ௥Ճ͢Δ • Ϧιʔεͷऔಘઌ΍εΫϦϓτͷ࣮ߦΛࢦఆͨ͠ιʔεͷΈʹ੍ݶͰ͖Δ • script-src ‘self’;

    img-src *.trusted.comͷ ͷΑ͏ʹෳ਺ઃఆ΋Մೳ ▼ Response Headers content-security-policy: script-src ‘self’ *.trusted.com σΟϨΫςΟϒ ιʔε script-src ‘self’; img-src *.trusted.com
  16. strict-dynamic 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> QBSTFSJOTFSUFEͰͳ͍TDSJQUͷಈతੜ੒͕Մೳ 
 JOOFS)5.-ͳͲγϯΫʹͳΔؔ਺͸ېࢭ͞Ε͍ͯΔ ※parser-inserted = HTMLύʔαʔ΍XMLύʔαʔʹΑͬͯૠೖ͞ΕΔ͜ͱ
  17. Ϩϙʔτͷྫ { "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" } }
  18. • ҉߸Խͷࣦഊ ➡ ௨৴σʔλͷ҉߸Խ • ΞΫηε੍ޚͷෆඋ ➡ CORSΛ࢖ͬͨΞΫηε੍ޚ • ΠϯδΣΫγϣϯ

    ➡ XSSରࡦ • ੬ऑͰݹ͘ͳͬͨίϯϙʔωϯτ ➡ ੬ऑͳϥΠϒϥϦͷνΣοΫ ࠓճ͓࿩͢ΔηΩϡϦςΟϦεΫ
  19. npm auditΛ࢖͏ • npmʹඪ४Ͱ૊Έࠐ·Ε͍ͯΔίϚϯυπʔϧ • ར༻͍ͯ͠Δnpmύοέʔδͷ੬ऑੑΛ਍அͯ͘͠ΕΔ • npm installΛ͢Δͱ͖ʹࣗಈͰ࣮ߦ͞ΕΔ •

    CIͰ࣮ߦ͢Ε͹ࣗಈͰ੬ऑੑͷ͋ΔϥΠϒϥϦͷར༻Λ๷͙͜ͱ͕Ͱ͖Δ • npm audit fi xίϚϯυͰ੬ऑͳϥΠϒϥϦͷΞοϓσʔτΛҰׅͰߦ͏͜ͱ ͕Ͱ͖Δ