「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-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文字セットのエリアマップです。
下 位 4 ビ ッ ト | |||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f | ||
上 位 4 ビ ッ ト | 0 | 0x00 NUL | 0x01 SOH | 0x02 STX | 0x03 ETX | 0x04 EOT | 0x05 ENQ | 0x06 ACK | 0x07 BEL | 0x08 BS | 0x09 HT | 0x0a LF | 0x0b VT | 0x0c FF | 0x0d CR | 0x0e SO | 0x0f SI |
1 | 0x10 DLE | 0x11 DC1 | 0x12 DC2 | 0x13 DC3 | 0x14 DC4 | 0x15 NAK | 0x16 SYN | 0x17 ETB | 0x18 CAN | 0x19 EM | 0x1a SUB | 0x1b ESC | 0x1c FS | 0x1d GS | 0x1e RS | 0x1f US | |
2 | 0x20 SP | 0x21 ! | 0x22 " | 0x23 # | 0x24 $ | 0x25 % | 0x26 & | 0x27 ' | 0x28 ( | 0x29 ) | 0x2a * | 0x2b + | 0x2c , | 0x2d - | 0x2e . | 0x2f / | |
3 | 0x30 0 | 0x31 1 | 0x32 2 | 0x33 3 | 0x34 4 | 0x35 5 | 0x36 6 | 0x37 7 | 0x38 8 | 0x39 9 | 0x3a : | 0x3b ; | 0x3c < | 0x3d = | 0x3e > | 0x3f ? | |
4 | 0x40 @ | 0x41 A | 0x42 B | 0x43 C | 0x44 D | 0x45 E | 0x46 F | 0x47 G | 0x48 H | 0x49 I | 0x4a J | 0x4b K | 0x4c L | 0x4d M | 0x4e N | 0x4f O | |
5 | 0x50 P | 0x51 Q | 0x52 R | 0x53 S | 0x54 T | 0x55 U | 0x56 V | 0x57 W | 0x58 X | 0x59 Y | 0x5a Z | 0x5b [ | 0x5c \ | 0x5d ] | 0x5e ^ | 0x5f _ | |
6 | 0x60 ` | 0x61 a | 0x62 b | 0x63 c | 0x64 d | 0x65 e | 0x66 f | 0x67 g | 0x68 h | 0x69 i | 0x6a j | 0x6b k | 0x6c l | 0x6d m | 0x6e n | 0x6f o | |
7 | 0x70 p | 0x71 q | 0x72 r | 0x73 s | 0x74 t | 0x75 u | 0x76 v | 0x77 w | 0x78 x | 0x79 y | 0x7a z | 0x7b { | 0x7c | | 0x7d } | 0x7e ~ | 0x7f DEL |
一方、以下はJIS X 0201第1面(JISラテン文字)文字セットのエリアマップです。背景が緑色のセルが示す文字がASCIIとは異なっていることがわかります。
下 位 4 ビ ッ ト | |||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f | ||
上 位 4 ビ ッ ト | 0 | 0x00 NUL | 0x01 SOH | 0x02 STX | 0x03 ETX | 0x04 EOT | 0x05 ENQ | 0x06 ACK | 0x07 BEL | 0x08 BS | 0x09 HT | 0x0a LF | 0x0b VT | 0x0c FF | 0x0d CR | 0x0e SO | 0x0f SI |
1 | 0x10 DLE | 0x11 DC1 | 0x12 DC2 | 0x13 DC3 | 0x14 DC4 | 0x15 NAK | 0x16 SYN | 0x17 ETB | 0x18 CAN | 0x19 EM | 0x1a SUB | 0x1b ESC | 0x1c FS | 0x1d GS | 0x1e RS | 0x1f US | |
2 | 0x20 SP | 0x21 ! | 0x22 " | 0x23 # | 0x24 $ | 0x25 % | 0x26 & | 0x27 ' | 0x28 ( | 0x29 ) | 0x2a * | 0x2b + | 0x2c , | 0x2d - | 0x2e . | 0x2f / | |
3 | 0x30 0 | 0x31 1 | 0x32 2 | 0x33 3 | 0x34 4 | 0x35 5 | 0x36 6 | 0x37 7 | 0x38 8 | 0x39 9 | 0x3a : | 0x3b ; | 0x3c < | 0x3d = | 0x3e > | 0x3f ? | |
4 | 0x40 @ | 0x41 A | 0x42 B | 0x43 C | 0x44 D | 0x45 E | 0x46 F | 0x47 G | 0x48 H | 0x49 I | 0x4a J | 0x4b K | 0x4c L | 0x4d M | 0x4e N | 0x4f O | |
5 | 0x50 P | 0x51 Q | 0x52 R | 0x53 S | 0x54 T | 0x55 U | 0x56 V | 0x57 W | 0x58 X | 0x59 Y | 0x5a Z | 0x5b [ | 0x5c ¥ | 0x5d ] | 0x5e ^ | 0x5f _ | |
6 | 0x60 ` | 0x61 a | 0x62 b | 0x63 c | 0x64 d | 0x65 e | 0x66 f | 0x67 g | 0x68 h | 0x69 i | 0x6a j | 0x6b k | 0x6c l | 0x6d m | 0x6e n | 0x6f o | |
7 | 0x70 p | 0x71 q | 0x72 r | 0x73 s | 0x74 t | 0x75 u | 0x76 v | 0x77 w | 0x78 x | 0x79 y | 0x7a z | 0x7b { | 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日(火)にリリース予定です。
・関連記事
「Google Chrome 138」安定版リリース、文章の要約や翻訳の機能を提供するAPIを公開 - GIGAZINE
「Google Chrome 137」安定版リリース、CSSにif()関数を導入し条件値を簡潔に表現可能に - GIGAZINE
Google検索のAIモードが「画像やPDFファイルの内容に応じた検索」などに対応し超絶強化 - GIGAZINE
ほぼすべてのデバイスをファイルサーバーに変える「copyparty」、転送が中断してもレジュームで続きから可能&HTTP・WebDAV・FTP・TFTP・SMB/CIFSをサポート - GIGAZINE
Google Chromeの大規模な広告ブロック対策アップデートの回避策を見つけた方法、ただし今は修正済み - GIGAZINE
・関連コンテンツ
in ソフトウェア, Posted by log1c_sh
You can read the machine translated English article 'Google Chrome 139' stable release, abol….