CORB がよくわからない
◆ Access-Control-Allow-Origin に "*" をつける
◆ flask-cors まかせだとダメな場合があった
◆ 拡張機能の content_scripts からのリクエストでレスポンスによってはブロックされる
◆ Access-Control-Allow-Origin は extension:// スキームが返されてる
◆ localhost からのリクエストだと拡張機能からでもブロックされてない
◆ flask-cors まかせだとダメな場合があった
◆ 拡張機能の content_scripts からのリクエストでレスポンスによってはブロックされる
◆ Access-Control-Allow-Origin は extension:// スキームが返されてる
◆ localhost からのリクエストだと拡張機能からでもブロックされてない
数ヶ月前に作ったツールを久々に使ったら動きませんでした
Python のサーバとブラウザ拡張機能の組み合わせで 拡張機能がページ内のデータをサーバに送って サーバ側で変換したデータを元に画面内の処理を行います
動いてない原因を調べると サーバから受け取ったデータが 0KB の空のデータになっていました
サーバのログを見るにちゃんとデータの変換はできていてレスポンスを返してるようです
ブラウザの console を見ると CORB の制限にかかったとかで データを受け取れないようになってました
こういう余計な制限を追加しないでほしいです
最終的には下のコードを追加してレスポンスヘッダーに Acccess-Control-Allow-Origin にすべてを指定することで動くようにできました
そのコードがこれです
9001 と 9002 のサーバがありますが CORS 系なので 2 つ用意してます
9001 が zip ファイルを返すもので 9002 のページから 9001 へアクセスします
ただその処理は拡張機能が行うので 9002 のレスポンスにスクリプトは含めません
なので結局 Python でも作って そのサーバは 9000 番ポートになってます
ボタンを用意して 押されたら fetch して console.log へ出力だけ行います
[cscript.js]
[manifest.json]
Python のサーバ (9000) と Node.js のサーバ (9001) の両方試せるようにしてます
また POST で発生したりしなかったりしたのですが GET では全然発生しないのでメソッドが関係するのかとも思って GET/POST の両方も用意しています
軽いページが良かったので VM のサーバに空の HTML を作ってそこにしてます
拡張機能の manifest.json の http://fedora-server/* がそれです
どっちもバイナリ的には zip です
9000 と 9001 のサーバで返すファイルのファイル名の拡張子を変えます
全部ダウンロードできてます
9000 番ポートの Python サーバのみ CORB で弾かれてます
ちなみに console から直接実行すると問題なく取得できます
また content_scripts ではなくページの JavaScript から実行した場合も CORB 制限には引っかかりませんでした
拡張機能の content_scripts として実行するとダメのようです
それなら content_scripts が script タグを追加してページ内の JavaScript 扱いで実行すれば回避できそうな気もします
fedora-server のところは MDN など適当なページ試しても同じで localhost だけ特別のようでした
localhost は開発用なので特別扱いしてるのでしょうか
実際に拡張機能を動かすのは localhost 以外で固定ではないどこかのサーバのウェブページになります
なので何かの回避策が必要です
content_scripts はダメでページ内の JavaScript なら問題ないところを見ると拡張機能がブロックされてるように見えます
むしろ拡張機能ならセキュリティ制約無視して何でもできてほしいのですけど
ただページ内として見せかければ動くようなのでそれで試そうと思ったのですが……なぜか Python 版のみなんですよね
サーバで対策できるならそれが一番なので Node.js 版と Python 版の違いを調べてみました
send_file が送る Content-Type などのヘッダーに問題あるのかと思ってそのあたりを色々いじってみたのですが 特に変わりませんでした
header といえば Node.js 版では Access-Control-Allow-Origin を直接指定してますが Python では CORS 許可するらしい flask_cors というパッケージに任せています
これくらいしか思いつかなかったので flask_cors をなくして直接 Access-Control-Allow-Origin を指定してみたところ 無事 Python 版でも動くようになりました
CORS 許可してくれるというからサンプルそのまま使ってましたが Access-Control-Allow-Origin に常に * を指定してくれてるわけじゃないみたいですね
基本クロスオリジンを許可したいなら全許可でいいので手動で書こうと思います
Request
Response
chrome-extension スキームがブロックされるなら content_scripts からの fetch はすべてブロックされてよさそうですが そうでもないのですよね
あとは localhost が例外なのはわかるとしても zip だとよくて xlsx だと発生するとか条件がよくわかりません
拡張機能で xlsx をダウンロードとか悪用されやすい組み合わせと Chrome が判断してブロックとかしてるのでしょうか?
ちなみに zip も xlsx も Windows のエクスプローラの右クリックの「新規作成」から作った空のファイルです
Python のサーバとブラウザ拡張機能の組み合わせで 拡張機能がページ内のデータをサーバに送って サーバ側で変換したデータを元に画面内の処理を行います
動いてない原因を調べると サーバから受け取ったデータが 0KB の空のデータになっていました
サーバのログを見るにちゃんとデータの変換はできていてレスポンスを返してるようです
ブラウザの console を見ると CORB の制限にかかったとかで データを受け取れないようになってました
こういう余計な制限を追加しないでほしいです
最終的には下のコードを追加してレスポンスヘッダーに Acccess-Control-Allow-Origin にすべてを指定することで動くようにできました
response.headers["Access-Control-Allow-Origin"] = "*"
再現しない
どうすれば回避できるのか どういう条件で発生するのかを調べようとして色々試してたのですが発生したりしなかったりで苦労しましたNode.js 版のサーバ
Python の flask がさほどわかってないので楽に試せるように必要最低限の部分だけ作るのは Node.js の koa で試しましたそのコードがこれです
const Koa = require("koa")
const fs = require("fs")
const app1 = new Koa()
app1.use(ctx => {
ctx.set("Access-Control-Allow-Origin", "*")
ctx.body = fs.createReadStream("./test.zip")
})
app1.listen(9001)
const app2 = new Koa()
app2.use(ctx => {
ctx.body = "9002"
})
app2.listen(9002)
9001 と 9002 のサーバがありますが CORS 系なので 2 つ用意してます
9001 が zip ファイルを返すもので 9002 のページから 9001 へアクセスします
ただその処理は拡張機能が行うので 9002 のレスポンスにスクリプトは含めません
Python サーバ
Node.js 版で試してみたのですが 9002 → 9001 だと全然発生しませんでしたなので結局 Python でも作って そのサーバは 9000 番ポートになってます
from flask import Flask, send_file
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/", methods=["GET"])
@app.route("/", methods=["POST"])
def check():
return send_file("test.zip")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=9000)
拡張機能
拡張機能ですが 必要な部分は別オリジンのページにリクエストを送りレスポンスを確認する処理ですボタンを用意して 押されたら fetch して console.log へ出力だけ行います
[cscript.js]
const div = document.createElement("div")
div.innerHTML = `
<button id="js-get">js-GET</button>
<button id="js-post">js-POST</button>
<button id="py-get">py-GET</button>
<button id="py-post">py-POST</button>
`
document.body.append(div)
document.querySelector("#js-get").onclick = async eve => {
const res = await fetch("http://localhost:9001/")
console.log("js-get", await res.arrayBuffer())
}
document.querySelector("#js-post").onclick = async eve => {
const res = await fetch("http://localhost:9001/", {method: "POST"})
console.log("js-post", await res.arrayBuffer())
}
document.querySelector("#py-get").onclick = async eve => {
const res = await fetch("http://localhost:9000/")
console.log("py-get", await res.arrayBuffer())
}
document.querySelector("#py-post").onclick = async eve => {
const res = await fetch("http://localhost:9000/", {method: "POST"})
console.log("py-post", await res.arrayBuffer())
}
[manifest.json]
{
"manifest_version": 2,
"name": "cscript",
"version": "1",
"content_scripts": [
{
"matches": ["http://localhost:9002/*", "http://fedora-server/*"],
"run_at": "document_end",
"js": ["cscript.js"]
}
]
}
Python のサーバ (9000) と Node.js のサーバ (9001) の両方試せるようにしてます
また POST で発生したりしなかったりしたのですが GET では全然発生しないのでメソッドが関係するのかとも思って GET/POST の両方も用意しています
localhost 以外のサーバ
試してた感じでは拡張機能を動かしたページが MDN や Google などの一般的なページと localhost:9002 で違ってる気もしたので localhost 外のサーバも用意しました軽いページが良かったので VM のサーバに空の HTML を作ってそこにしてます
拡張機能の manifest.json の http://fedora-server/* がそれです
レスポンスの種類
返すファイルによって結果が違うみたいだったので 空の zip と空の xlsx を用意しましたどっちもバイナリ的には zip です
9000 と 9001 のサーバで返すファイルのファイル名の拡張子を変えます
実行結果
fedora-server から test.zip のダウンロード
js-get ArrayBuffer(22) {}
js-post ArrayBuffer(22) {}
py-get ArrayBuffer(22) {}
py-post ArrayBuffer(22) {}
全部ダウンロードできてます
fedora-server から test.xlsx のダウンロード
js-get ArrayBuffer(6553) {}
js-post ArrayBuffer(6553) {}
Cross-Origin Read Blocking (CORB) blocked cross-origin response http://localhost:9000/ with MIME type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet. See https://www.chromestatus.com/feature/5629709824032768 for more details.
document.querySelector.onclick @ cscript.js:19
py-get ArrayBuffer(0) {}
Cross-Origin Read Blocking (CORB) blocked cross-origin response http://localhost:9000/ with MIME type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet. See https://www.chromestatus.com/feature/5629709824032768 for more details.
document.querySelector.onclick @ cscript.js:23
py-post ArrayBuffer(0) {}
9000 番ポートの Python サーバのみ CORB で弾かれてます
ちなみに console から直接実行すると問題なく取得できます
const res = await fetch("http://localhost:9000/", {method: "POST"})
console.log("manual-py-post", await res.arrayBuffer())
// manual-py-post ArrayBuffer(6553) {}
また content_scripts ではなくページの JavaScript から実行した場合も CORB 制限には引っかかりませんでした
拡張機能の content_scripts として実行するとダメのようです
それなら content_scripts が script タグを追加してページ内の JavaScript 扱いで実行すれば回避できそうな気もします
localhost:9002 から test.zip と test.xlsx のダウンロード
こっちはすべて正常に行えました回避策
localhost 以外かつ Python サーバの方のみ GET/POST どっちも CORB に引っかかるようですfedora-server のところは MDN など適当なページ試しても同じで localhost だけ特別のようでした
localhost は開発用なので特別扱いしてるのでしょうか
実際に拡張機能を動かすのは localhost 以外で固定ではないどこかのサーバのウェブページになります
なので何かの回避策が必要です
content_scripts はダメでページ内の JavaScript なら問題ないところを見ると拡張機能がブロックされてるように見えます
むしろ拡張機能ならセキュリティ制約無視して何でもできてほしいのですけど
ただページ内として見せかければ動くようなのでそれで試そうと思ったのですが……なぜか Python 版のみなんですよね
サーバで対策できるならそれが一番なので Node.js 版と Python 版の違いを調べてみました
send_file が送る Content-Type などのヘッダーに問題あるのかと思ってそのあたりを色々いじってみたのですが 特に変わりませんでした
header といえば Node.js 版では Access-Control-Allow-Origin を直接指定してますが Python では CORS 許可するらしい flask_cors というパッケージに任せています
これくらいしか思いつかなかったので flask_cors をなくして直接 Access-Control-Allow-Origin を指定してみたところ 無事 Python 版でも動くようになりました
CORS 許可してくれるというからサンプルそのまま使ってましたが Access-Control-Allow-Origin に常に * を指定してくれてるわけじゃないみたいですね
基本クロスオリジンを許可したいなら全許可でいいので手動で書こうと思います
なぜ?
もう少し調べたところ flask-cors を使った場合 Access-Control-Allow-Origin は * ではなくリクエストの Origin がそのまま使われていましたRequest
Origin: chrome-extension://fgippodmgfepnjbmfkijklocaoobkgbj
Response
Access-Control-Allow-Origin: chrome-extension://fgippodmgfepnjbmfkijklocaoobkgbj
chrome-extension スキームがブロックされるなら content_scripts からの fetch はすべてブロックされてよさそうですが そうでもないのですよね
あとは localhost が例外なのはわかるとしても zip だとよくて xlsx だと発生するとか条件がよくわかりません
拡張機能で xlsx をダウンロードとか悪用されやすい組み合わせと Chrome が判断してブロックとかしてるのでしょうか?
ちなみに zip も xlsx も Windows のエクスプローラの右クリックの「新規作成」から作った空のファイルです