「Google Chrome 139」安定版リリース、ISO-2022-JPの自動検出を廃止

ウェブブラウザ「Google Chrome」の最新安定版であるバージョン139がリリースされました。HTMLの文字コード自動検出の対象からISO-2022-JP(JISコード)が除外されることになりました。
Chrome 139 | Release notes | Chrome for Developers
https://developer.chrome.com/release-notes/139?hl=ja
ISO-2022-JPは国際的な文字コード規格ISO/IEC 2022に準拠する日本語文字コードであり、Unicode系の文字セットが普及する以前から、Shift JISコードや日本語EUCコードと並び広く用いられてきました。その特徴の一つに、複数の文字セットを「エスケープシーケンス」によって切り替えて使用できる点があります。

この特徴がHTMLの文字コードを自動検出する機能に影響を及ぼし、セキュリティリスクとなる可能性が知られていました。今回の更新では、HTMLの文字コード自動検出を行うにあたり、ISO-2022-JPを対象から除外することで、このリスクを回避することが目的となります。
ブラウザがHTML文字コードを判断するには、以下の3つの方法があります。
・バイトオーダーマークの検出
・Content-Typeヘッダーのcharset属性
・HTMLドキュメントの<meta>タグ
ただし、これらのいずれもが存在しないケースもあり、その場合ブラウザはコンテンツそのものを確認することにより自動検出を行います。そしてブラウザは、ISO-2022-JPのエスケープシーケンスを一箇所でも含むHTMLドキュメントについてはISO-2022-JP文字コードであると自動検出します。ここで、検出された文字コードと実際の文字コードが異なると、適切にサニタイズすることができず、XSS脆弱性を引き起こす可能性があります。ISO-2022-JPにはエスケープシーケンスによる文字セットの切り替え機能があるので、その仕組みが攻撃者に悪用されると、他の文字コードと比較して容易にXSS攻撃を仕掛けることができます。
ここで想定される具体的な攻撃手法は2つあります。
・手法1:JavaScript文字列に含まれるバックスラッシュエスケープの無効化
この手法を説明するために、URLパラメータを参照し、JavaScriptを含むHTMLドキュメントに反映させるページを想定します。この例では、HTMLドキュメントはASCII文字のみで構成され、文字コードは指定されておらず自動検出任せであるものとします。

このページのURLパラメータに攻撃者が悪意あるコードを仕込もうとしても、普通のやり方ではサニタイジングによりコードが実行されない形式に変換されるため、影響を受けることはありません。以下の例では攻撃者はURLパラメータに引用符を仕込むことによりJavaScriptの文字列をそこで終了させ、それ以降を実行可能なコードとして読み込ませることを企んでいますが、サニタイジングにより引用符の前に「バックスラッシュ」を追加されており、これがJavaScript文字列のエスケープシーケンス(ややこしいですが、ISO-2022-JPのエスケープシーケンスとは別物です)として機能するため、この引用符は文字列の終端ではなく文字列中の文字として認識されます。これにより、その後ろにJavaScriptのコードとして認識される文字列があったとしても、コードとしてではなく文字列として認識されます。

ここで、<script>タグより前にもURLパラメータを埋め込んでいる場所があり、このパラメータにISO-2022-JPのエスケープシーケンス「ESC ( J」を指定されたとします。すると、このHTMLドキュメントは自動検出によりISO-2022-JPと判定され、かつこのエスケープシーケンスは「JIS X 0201第1面(JISラテンコード)」への切り替えを指定するものなので、エスケープシーケンス以降はASCII文字セットではなくJIS X 0201第1面文字セットとして解釈されるようになります。

ASCII文字セットもJIS X 0201第1面(JISラテン文字)文字セットもラテン文字であるため、ここで発生した文字セットの切り替えは一見何の影響もないように見受けられます。しかしながら、両者にはわずかながら決定的な違いがあります。以下の表はASCII文字セットのエリアマップです。
.sheet td {text-align: center;} .sheet .diff {background-color: #3f0;} 下 位 4 ビ ッ ト0123456789abcdef上
位
4
ビ
ッ
ト00x00
NUL0x01
SOH0x02
STX0x03
ETX0x04
EOT0x05
ENQ0x06
ACK0x07
BEL0x08
BS0x09
HT0x0a
LF0x0b
VT0x0c
FF0x0d
CR0x0e
SO0x0f
SI10x10
DLE0x11
DC10x12
DC20x13
DC30x14
DC40x15
NAK0x16
SYN0x17
ETB0x18
CAN0x19
EM0x1a
SUB0x1b
ESC0x1c
FS0x1d
GS0x1e
RS0x1f
US20x20
SP0x21
!0x22
"0x23
#0x24
$0x25
%0x26
&0x27
'0x28
(0x29
)0x2a
*0x2b
+0x2c
,0x2d
-0x2e
.0x2f
/30x30
00x31
10x32
20x33
30x34
40x35
50x36
60x37
70x38
80x39
90x3a
:0x3b
;0x3c
<0x3d
=0x3e
>0x3f
?40x40
@0x41
A0x42
B0x43
C0x44
D0x45
E0x46
F0x47
G0x48
H0x49
I0x4a
J0x4b
K0x4c
L0x4d
M0x4e
N0x4f
O50x50
P0x51
Q0x52
R0x53
S0x54
T0x55
U0x56
V0x57
W0x58
X0x59
Y0x5a
Z0x5b
[0x5c
\0x5d
]0x5e
^0x5f
_60x60
`0x61
a0x62
b0x63
c0x64
d0x65
e0x66
f0x67
g0x68
h0x69
i0x6a
j0x6b
k0x6c
l0x6d
m0x6e
n0x6f
o70x70
p0x71
q0x72
r0x73
s0x74
t0x75
u0x76
v0x77
w0x78
x0x79
y0x7a
z0x7b
{0x7c
|0x7d
}0x7e
~0x7f
DEL
一方、以下はJIS X 0201第1面(JISラテン文字)文字セットのエリアマップです。背景が緑色のセルが示す文字がASCIIとは異なっていることがわかります。
下 位 4 ビ ッ ト0123456789abcdef上
位
4
ビ
ッ
ト00x00
NUL0x01
SOH0x02
STX0x03
ETX0x04
EOT0x05
ENQ0x06
ACK0x07
BEL0x08
BS0x09
HT0x0a
LF0x0b
VT0x0c
FF0x0d
CR0x0e
SO0x0f
SI10x10
DLE0x11
DC10x12
DC20x13
DC30x14
DC40x15
NAK0x16
SYN0x17
ETB0x18
CAN0x19
EM0x1a
SUB0x1b
ESC0x1c
FS0x1d
GS0x1e
RS0x1f
US20x20
SP0x21
!0x22
"0x23
#0x24
$0x25
%0x26
&0x27
'0x28
(0x29
)0x2a
*0x2b
+0x2c
,0x2d
-0x2e
.0x2f
/30x30
00x31
10x32
20x33
30x34
40x35
50x36
60x37
70x38
80x39
90x3a
:0x3b
;0x3c
<0x3d
=0x3e
>0x3f
?40x40
@0x41
A0x42
B0x43
C0x44
D0x45
E0x46
F0x47
G0x48
H0x49
I0x4a
J0x4b
K0x4c
L0x4d
M0x4e
N0x4f
O50x50
P0x51
Q0x52
R0x53
S0x54
T0x55
U0x56
V0x57
W0x58
X0x59
Y0x5a
Z0x5b
[0x5c
\0x5d
]0x5e
^0x5f
_60x60
`0x61
a0x62
b0x63
c0x64
d0x65
e0x66
f0x67
g0x68
h0x69
i0x6a
j0x6b
k0x6c
l0x6d
m0x6e
n0x6f
o70x70
p0x71
q0x72
r0x73
s0x74
t0x75
u0x76
v0x77
w0x78
x0x79
y0x7a
z0x7b
{0x7c
|0x7d
}0x7e
~0x7f
DEL
このうち問題となるのはコード「0x5c」の文字の違いであり、ASCIIでは「バックスラッシュ」として定義されているのに対し、JIS X 0201第1面(JISラテン文字)では「円記号」と定義されています。使用しているフォントなど環境によってはバックスラッシュが円記号として表示される場合もあり、あまり影響がなさそうに思われそうですが、重要なのはJavaScriptのエスケープシーケンスはあくまでバックスラッシュであり、円記号はそうとはみなされない、ということです。そのため、サニタイジングが期待通りには機能せず、引用符がそのまま引用符として認識されてしまうので、攻撃者の企てどおりその後ろにある悪意あるコードが実行可能な状態となります。

・手法2:HTMLコンテキストの破壊
こちらについては、MarkdownからHTMLへの変換をサポートするサイトを例として、画像を2つ表示するMarkdownから<img>タグを含むHTMLを出力する、ごくありふれたパターンについて見てみます。手法1の例と同様に、生成されるHTMLドキュメントはASCII文字のみで構成され、文字コードは指定されておらず自動検出任せであるものとします。

まずは、1つ目の画像のソースを、エスケープシーケンス「ESC $ @」に置き換えます。これで何が起こるかというと、エスケープシーケンス以降の文字列がJIS X 0208 1987(旧JIS漢字 JIS C 6226-1978)文字セットとして解釈されるようになります。本来はASCII文字セットを用いるべきところ、2バイトコードとして無理やり解釈しようとするため、エスケープシーケンス以降はいわゆる「文字化け」の状態となってしまい、HTMLドキュメントとしては破綻してしまっています。

ここで、2つの画像の間にあるプレーンテキスト部分にもう一つエスケープシーケンス「ESC ( B」を仕込みます。こうするとそこからの文字列はASCII文字セットで解釈されるようになるため、一部の文字化けは残っているもののHTMLドキュメントとしての破綻は解消されます。ここで注目すべき点は、残された文字化けにより1つ目の<img>タグのalt属性値の終端だった二重引用符が消失していることです。これにより、2つ目の<img>タグのsrc属性値の始点となる二重引用符までがalt属性値として扱われることになります。

これにより、2つ目の<img>タグのsrc属性値だったものが属性値ではなくなってしまいました。ここが攻撃者の付け入る箇所となり、JavaScriptコードを含むエラーハンドラを差し挟むことが可能となってしまいました。

こういったセキュリティリスクを根本的に排除するには、HTMLドキュメント中にISO-2022-JPのエスケープシーケンスが含まれているだけでISO-2022-JP文字コードであると自動検出する仕組み自体を見直す必要があり、今回の変更に至ったというわけです。記事作成時点でISO-2022-JPで記述されたサイトを目にすることはほぼなく、この変更により影響を受けるケースは少ないと考えられますが、そういったサイトを管理・運営している場合は、Content-Typeヘッダーのcharset属性を追加するなどし、文字コードの自動検出に頼らないよう変更する必要があります。
◆CSS関数var()・attr()のショートカット
CSS関数のvar()とattr()は、フォールバックが発生しない場合、代替値を探すことなく評価されます。ここで言うフォールバックとは、例えばvar()関数が以下のように使用される場合に、一番目の引数が無効値であっても二番目の引数を代替値として利用できることを指します。
--blue: blue;
--a: var(--green, --blue);
これを踏まえた上で以下のコードについて考えてみます。
--green: green;
--blue: blue;
--a: var(--green, var(--b));
--b: var(--blue, var(--a));
このコードは、一番目の引数に指定されているカスタムプロパティが--green・--blueとも存在しており、フォールバックは発生しません。もしこの場合に代替値に設定された二番目の引数が評価されるとすると、問題が発生します。というのも、--aの代替値が--bであり、その--bの代替値が--aであるという状態であることから、循環参照となっているからです。今回の更新はこういったケースを解消するもので、フォールバックが発生しない限り代替値となる二番目の引数が評価されることはないので、上述のコードは(あくまで例でありこういった記述はそもそも避けるべきですが)問題にはなりません。
◆CSS caret-animationプロパティ
CSSで入力キャレット(テキスト入力カーソル)の色を設定するのがcaret-colorプロパティであり、この色設定はアニメーションに対応しています。ただし、入力キャレットは元々点滅というアニメーション表示を行っており、そのためcaret-colorによるアニメーション表示と干渉して見にくくなる可能性があります。これに対応するために用意されたのがcaret-animationプロパティです。このプロパティは以下の2つの値を設定することができます。
・auto:デフォルトの状態(点滅)
・manual:ウェブ開発者によってキャレットアニメーションが制御されている状態(点滅無効)
以下のcaret-colorのサンプルサイトにて、CSSのinputセレクターにcaret-animationプロパティを追加して値を切り換えてみると、このプロパティの挙動が理解できます。
caret-color animation
https://codepen.io/daviddiaz/pen/vYXdXbR
◆CSS corner-shapeプロパティ
DOM要素の角の形状を定義する既存のCSSプロパティとしてborder-radiusがありますが、その上でさらに自由度の高い設定を可能にするものとして「corner-shape」プロパティが追加されました。これを使用することにより、これまでは曲線半径を指定した単純な丸みを持たせるだけだった角の形状に、様々な表現を追加することが可能となります。以下はその表現力を示すサンプルアニメーションです。
corner-shape
https://codepen.io/amit_sheen/pen/pvJKGov/5d14d83481ba74eea561f08d81493614
corner-shapeプロパティには以下の値を設定することができます。
・round(規定値):従来と同じ円弧または楕円弧の形状
・squircle:直角と円弧の中間的な形状
・scoop:凹型となる円弧または楕円弧の形状
・bevel:角を直線的に切り取った形状
・notch:角を直角に切り取った形状
・square:角に一切手を加えない形状(border-radiusを無効化する)

◆CSSカスタム関数
カスタム関数はカスタムプロパティに似ていますが、固定値を返すのではなく、他のカスタムプロパティ・パラメータ・条件に応じて異なる値を返します。以下の例では、カスタムプロパティ「--gap」の値を負数に変換するカスタム変数「--negate」を定義しています。
@function --negate(--value) {
result: calc(var(--value) * -1);
}
div {
--gap: 1em;
margin-top: --negate(var(--gap));
}
◆その他の更新
・CSS:遷移関連のプロパティを初期値に切り替えるときに遷移の実行を継続するよう変更(webkitやgeckoと同等の挙動に)
・CSS:font-widthプロパティと記述子を追加、font-stretchはレガシーエイリアスに
・ウェブアプリ:マニフェスト仕様で更新の対象となるアルゴリズムを指定できるように
・WebXR:深度センシングのパフォーマンス改善
・API:JavaScript DOM APIでより多くの文字数を使用可能に(HTML パーサーに合わせて制限を緩和)
・WebGPU:BC・ASTC圧縮形式の3Dテクスチャをサポート
・WebGPU:core-features-and-limits機能の追加(WebGPUアダプタとデバイスが仕様のコア機能と上限をサポートしていることを示す)
・Secure Payment Confirmation(SPC):暗号署名の追加、およびsecurePaymentConfirmationAvailability APIの提供
・SVGの<script>要素がasync属性をサポート
・オンデバイスWeb Speech API:サードパーティのサービスにデータ送信することなしに、ウェブサイト上で音声を処理可能に
・RTCエンコードフレームの音声レベル:WebRTC Encoded Transformで公開したエンコード済みフレームの音声レベルをウェブに公開
・クロスサイトナビゲーション:コンテキストグループを切り替えるナビゲーションを行う際はwindow.nameプロパティの値をクリアし、情報の漏洩を防止する
・ネットワーク:Accept-Languageヘッダー情報のフィンガープリントを削減(ユーザーの優先言語の完全なリストではなく、最も優先度の高い言語のみを送信)
・ネットワーク:WindowsでTCPポート割り当てをランダム化
・Androidでバックグラウンドページ(および関連するWorker)がフリーズするまでの時間を5分から1分に短縮
・ウェブアプリ:スコープを他のオリジンに拡張可能にするscope_extensionsマニフェストフィールドを追加し、複数のサブドメインとトップレベルドメインを制御するサイトが単一ウェブアプリ化可能に
・コンテンツ・セキュリティ・ポリシー(CSP)によりブロックされたWorkerに関して、例外をスローするのではなく、エラーイベントを非同期的に発生させるよう変更
・JSONのMIMEタイプ検出が仕様に準拠:サブタイプが「+json」で終わるMIMEタイプが追加
◆オリジントライアル
・Prompt API:AI言語モデルへのテキスト・画像・音声の入力をサポート
・SharedWorkerのライフタイム延長を指示可能に(コンストラクタのオプション「extendedLifetime: true」を追加)
・SoftNavigationのパフォーマンスエントリ追加(soft-navigation・interaction-contentful-paint)
・新規レンダリングブロック属性full-frame-rate:レンダラを低いフレームレートで動作させ、読み込み処理のためのリソースを確保する
・ウェブ認証の即時仲介:ブラウザがすぐに認識できるサイトのパスキーまたはパスワードがある場合、ブラウザのログインUIがユーザーに表示されるように
・WebGPU互換モード:OpenGLやDirect3D11といった古いグラフィックAPIを実行可能にするWebGPU APIの制限付きサブセット
◆非推奨化される機能
・macOS 11のサポートを終了
・プリフェッチとプリレンダリングからPurpose: prefetchヘッダーの送信を停止
・SwiftShaderによるWebGLへの自動フォールバックを廃止:セキュリティリスクの軽減(段階的廃止期間完了)
また、Google Chrome 139には12件のセキュリティバグフィックスが含まれています。
なお、次期安定版の「Google Chrome 140」は現地時間の2025年9月2日(火)にリリース予定です。
