---
title: CSPを無意味にする残念なServiceWorker
tags:  #security #shibuyaxss #javascript #xss  
author: [はせがわようすけ](https://www.docswell.com/user/hasegawa)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/LJLZ23YV7R.jpg?width=480
description: Shibuya.xss techtalk #12
published: July 24, 23
canonical: https://www.docswell.com/s/hasegawa/KRXEGG-serviceworker
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/LJLZ23YV7R.jpg)

CSPを無意味にする
残念なServiceWorker
はせがわようすけ @hasegawayosuke


# Page. 2

![Page Image](https://bcdn.docswell.com/page/47MV8DNN7W.jpg)

Ｃ
Ｓ
Ｐ
突
破
テ
ク
ニ
ッ
ク
が
つ
い
に
公
開
‼
Shibuya.xss
#12
こ
こ
で
提供
Shibuya.XSS
UTF-8.jp
し
か
見
ら
れ
な
い
Ｓ
Ｗ
に
よ
2023-07-25
る
#shibuyaxss


# Page. 3

![Page Image](https://bcdn.docswell.com/page/P7R254D4J9.jpg)

0. 予備知識
ServiceWorkerって？
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 4

![Page Image](https://bcdn.docswell.com/page/PJXDK21ZEX.jpg)

ServiceWorkerって？
 Webページ内(ブラウザー上)のバックグラウンドで動作するJavaScript環
境
 バックグラウンドでキャッシュを制御しオフライン時でもコンテンツへのアクセスを提供す
る
 Webアプリからのプッシュ通知を受け取りユーザーに伝える等が実現できる機能
ブラウザー
SW
キャッシュ
Shibuya.xss #12 2023-07-25
ネットワーク
レンダラー
document.addEventListener(&#039;DOMContentLoaded&#039;, (event) =&gt; {
if (&#039;serviceWorker&#039; in navigator) {
try {
navigator.serviceWorker.register(&#039;/sw.js&#039;, {
&#039;scope&#039;: &#039;./&#039;
})
} catch (e) {
console.error(e)
}
}
))
#shibuyaxss


# Page. 5

![Page Image](https://bcdn.docswell.com/page/3JKX5M2VED.jpg)

ServiceWorkerって？
 レンダラーからのfetchイベント(サーバーへのリクエスト)をフッ
クしリクエスト/レスポンスを加工することができる
 主にキャッシュの制御に使われる
// sw内でfetchイベントを捕捉
self.addEventListener(&#039;fetch&#039;, async (evt) =&gt; {
const url = new URL(evt.request.url)
if (url.pathname === &#039;/hello&#039;) {
// キャッシュからレスポンスを探す
res = await caches.match(evt.request)
// レスポンスを返す
evt.respondWith(res)
}
}
Shibuya.xss #12 2023-07-25
sw.js
#shibuyaxss


# Page. 6

![Page Image](https://bcdn.docswell.com/page/LE35KYYQJ5.jpg)

ServiceWorkerって？
 HTTPでの通信箇所が増えた！
 レンダラーとSWの間、SWとサーバーの間
 サーバーがCSPを返しても、レンダラーに伝わらないときがある
んじゃない？
 …というのが今日のテーマ
レンダラー
ServiceWorker
CSP?
Shibuya.xss #12 2023-07-25
ネットワーク
ブラウザー
CSP！
#shibuyaxss


# Page. 7

![Page Image](https://bcdn.docswell.com/page/8EDD355WEG.jpg)

1. SWがCSPを削除して
レンダラーに返すパターン
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 8

![Page Image](https://bcdn.docswell.com/page/V7PX4GGXE8.jpg)

SWがCSPを削除してレンダラーに返すパターン
 SWはレンダラー側のfetchのリクエスト/レスポンスに介入する
ことができる
 SWがNWからのCSPを削除した場合どうなる？
Content-Type: text/html
Server: nginx
Content-Length: 12345
&lt;!doctype html&gt;
....
ブラウザー
レンダラー
Shibuya.xss #12 2023-07-25
Content-Type: text/html
Content-Security-Policy: script-src &#039;none&#039;;
Server: nginx
Content-Length: 12345
&lt;!doctype html&gt;
....
ServiceWorker
#shibuyaxss


# Page. 9

![Page Image](https://bcdn.docswell.com/page/2JVLX6637Q.jpg)

SWがＣＳＰを削除してレンダラーに返すパターン
self.addEventListener(&#039;fetch&#039;, async (evt) =&gt; {
// sw内でfetchイベントを捕捉
sw.js
const url = new URL(evt.request.url)
if (url.pathname === &#039;/foo/demo.html&#039;) {
evt.respondWith(
(async () =&gt; {
const respondFromNetwork = await fetch(evt.request) // サーバーにfetchする
const cloned = respondFromNetwork.clone() // 直接ヘッダーを改ざんはできないので複製
const { status, statusText } = cloned
const headers = new Headers(cloned.headers)
headers.delete(&#039;Content-Security-Policy&#039;) // CSPヘッダーを削除
const responseToBrowser =
new Response(cloned.body,
{ status, statusText, headers })
return responseToBrowser // CSPを削除したレスポンスをレンダラーに返す
})()
)
}
}
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 10

![Page Image](https://bcdn.docswell.com/page/5EGXVNNYJL.jpg)

SWがCSPを削除してレンダラーに返すパターン
SWが生成した
レスポンス
ネットワークからの
レスポンス
生成したレスポンスではCSP
ヘッダーが削除されている
ネットワークからのレスポンスでは
CSPがついている
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 11

![Page Image](https://bcdn.docswell.com/page/4JQ86QQ67P.jpg)

SWがCSPを削除してレンダラーに返すパターン
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 12

![Page Image](https://bcdn.docswell.com/page/K7424NNMJ1.jpg)

SWがCSPを削除してレンダラーに返すパターン
 さすがにこんな不自然なコードはなさそう
 swは通信に介入できるが、直接的にレスポンスヘッダーを改ざんするこ
とはできない
 レスポンスヘッダーをコピーした上でCSPを削除する（CSP以外のレスポ
ンスヘッダーをコピーする）のような意図的なコードが必要
➡ もうちょっと現実にありそうなコードを考える！
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 13

![Page Image](https://bcdn.docswell.com/page/LJ1M455YEG.jpg)

2. SW内でリダイレクトさせる
パターン
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 14

![Page Image](https://bcdn.docswell.com/page/GJW3X551J2.jpg)

SW内でリダイレクトさせるパターン
 サーバー側のリソースの配置が換わったのをSWを使ってクライア
ント側でURLを書き換える
// SW内でURLを書き換えてfetch
https://site-1.example/old/module.js
https://site-1.example/module.js
ブラウザー
レンダラー
Shibuya.xss #12 2023-07-25
ServiceWorker
#shibuyaxss


# Page. 15

![Page Image](https://bcdn.docswell.com/page/4EZV6NNXJ3.jpg)

SW内でリダイレクトさせるパターン
sw.js
self.addEventListener(&#039;fetch&#039;, async (evt) =&gt; {
// sw内でfetchイベントを捕捉
const url = new URL(evt.request.url)
if (url.pathname.startWith(&#039;/old/&#039;) {
const newUrl = url.pathname.replace(&#039;/old/&#039;, &#039;/&#039;) // 「/old/」を「/」に置換
const res = await fetch(newUrl) // 書き換えたURLでfetch
evt.respondWith(res)
}
}
const script = &#039;https://site-1.example/old//site-2.example/alert.js&#039;
const module = await import(script)
レンダラー
 レンダラーから見るとsite-1.exampleへの通信のためCSP:
script-src &#039;self&#039; 等には違反しない？
➡ 駄目でした。レンダラーはレスポンスを受け取ってからもCSP検証
してるっぽい
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 16

![Page Image](https://bcdn.docswell.com/page/Y765288PEV.jpg)

3. SW内でHTMLを
生成するパターン
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 17

![Page Image](https://bcdn.docswell.com/page/G75Y299P74.jpg)

SW内でHTMLを生成するパターン
 SWはレスポンスそのものを生成してレンダラーに返すことができ
る
 &lt;script&gt;タグの含まれるHTMLを生成しレスポンスとして返せる
GET /foo/bar HTTP/1.1
Host: https://site-1.example/
ブラウザー
レンダラー
&lt;!doctype html&gt;
&lt;html&gt;
&lt;script&gt;...&lt;/script&gt;
&lt;/html&gt;
Shibuya.xss #12 2023-07-25
ServiceWorker
#shibuyaxss


# Page. 18

![Page Image](https://bcdn.docswell.com/page/9J2G422ZJR.jpg)

SW内でHTMLを生成するパターン
self.addEventListener(&#039;fetch&#039;, async (evt) =&gt; {
// sw内でfetchイベントを捕捉
sw.js
const url = new URL(evt.request.url)
if (url.pathname === &#039;/demo3&#039;) {
const name = url.searchParams.get(&#039;name&#039;) || &#039;Anonymous&#039; // URL内nameパラメータの値
const html = `&lt;html&gt;&lt;body&gt;Hello, ${name}&lt;/body&gt;&lt;/html&gt;` // HTMLを生成
const res = new Response(html, { headers: { &#039;Content-Type&#039;: &#039;text/html&#039; } })
evt.respondWith(res) // 生成したHTMLをレンダラーに応答
}
}
`https://site-1.example/?name=&lt;script&gt;alert(1)&lt;/script&gt;`
などでSCRIPT要素の含まれたHTMLが生成されレンダラー上で表示される
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 19

![Page Image](https://bcdn.docswell.com/page/DEYVMYYNEM.jpg)

SW内でHTMLを生成するパターン
SWからの応答のみでネットワークへの通信がない
(どうでもいい小ネタ）
DOM操作ではないので `&lt;script&gt;` が効く
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 20

![Page Image](https://bcdn.docswell.com/page/VJNMWPPV78.jpg)

3.まとめ
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 21

![Page Image](https://bcdn.docswell.com/page/YE9NX33VE3.jpg)

まとめ
 CSPが設定されていてもSWの使い方が悪いと任意のJSが動く
 SW自体がCSPで保護されている場合であってもJSの注入が可能
 「CSPが設定されているのでXSSがあっても平気」とは言えない
 とはいえこういうSWの使い方はたぶん普通はしない
 「SW内でHTMLを生成する」のパターンはあり得るかもしれない
 SW内でレスポンスを生成する場合はCSPもあわせて生成すること！
 脆弱性診断で探すのも難しい
 サーバーにリクエストが送信されない（≒DOM-based XSS)
 直接的にDOMを操作するコードはない
 DOM Invaderでも検出できず
Shibuya.xss #12 2023-07-25
#shibuyaxss


# Page. 22

![Page Image](https://bcdn.docswell.com/page/GE8K2MM47D.jpg)

まとめ
 サーバーの返すネットワーク上のレスポンスに加え、SWが生成す
る(仮想的な)レスポンスが増えた！
 これまでの挙動の齟齬が出る箇所があるかも？
 ブラウザーの脆弱性を探す人にはもしかしたら狙い目かも
レンダラー
ServiceWorker
res?
Shibuya.xss #12 2023-07-25
ネットワーク
ブラウザー
res！
#shibuyaxss


# Page. 23

![Page Image](https://bcdn.docswell.com/page/LELZ233VJR.jpg)

Question?
@hasegawayosuke
@hasegawa.bsky.social
Shibuya.xss #12 2023-07-25
#shibuyaxss


