Secure Contextsに関する localhost と、IETFでの新提案

Secure Contexts

Service Workers、Web BluetoothといったAPIは、安全に使用するためにセキュリティ上の条件があります。


その条件がSecure Contextと呼ばれるコンテキストであり、W3CのSecure Contexts(URL)というドキュメントで定義されています。


このSecure Contextsの仕様上で localhost. の扱いがどうなっているかというと次のようになっている。RFC6761の定義では「ローカルのリゾルバはlocalhost. 及び .localhost. の内側のドメインを特殊扱いしてもよい、すべき(MAY/SHOULD)」と書かれており、その不確実性のためlocalhostではなく127.0.0.1の場合に特別扱いするとしている。

つまり、localhostドメインをローカルネットワークのDNSに問い合わせても良いため、ループバックアドレスという保証が無いことになる。

新提案

そういった状況に切り込んだのが、W3CのWebAppSecでは大変良く見かけるMike West氏である。


IETFに「Let 'localhost' be localhost.」(URL)という、I-Dを提出している。


この提案では、localhost. はループバックアドレスになるように、localhostの名前解決でSHOULDとなっていた部分をMUSTへ変更する提案です。


必ずlocalhost.を特別扱いし(MUST)、DNSにクエリを送信することも許可されません(MUST NOT)。あわせて、DNSサーバも問い合わせに対してNSレコードを検索しようとしてはいけません(MUST NOT)。既存のlocalhost.の扱いを幾つか変更することで、必ずループバックアドレス扱いになるようにし、Secure Contextsでの問題を解消しようとしています。


提案仕様の最後に、Implementation Considerationsとして、サーバの名前にserver1.localhostといった名前をつけている場合は予期せぬ挙動になるという懸念を挙げています。


引き続き、Secure Contextsとlocalhostの議論は続くと思いますが、個人的には興味深いなと思っています。

wicgのFace detection API

wicgで議論になっている「Face detection API」の仕様が「Shape Detection in Images」としてwicgのリポジトリで公開されている。


https://wicg.github.io/shape-detection-api/

Shape Detection in Images

HTMLImageElement、HTMLCanvasElement、ImageDataといった画像のデータを与えることで、顔領域の矩形が得られるようである。


現状は、顔を検出する「Face Detection API」と、バーコードを検出する「Barcode Detection API」が定義されている。


具体的にはexampleを見るとわかりやすい

let faceDetector = new FaceDetector({fastMode: true, maxDetectedFaces: 1});
// Assuming |theImage| is e.g. a <img> content, or a Blob.

faceDetector.detect(theImage)
.then(detectedFaces => {
  for (const face of detectedFaces) {
    console.log(' Face @ (${face.boundingBox.x}, ${face.boundingBox.y}),' +
        ' size ${face.boundingBox.width}x${face.boundingBox.height}');
  }
}).catch(() => {
  console.error("Face Detection failed, boo.");
})

HTTP/2のデバック用情報エンドポイントの仕様

HTTP/2 Implementation Debug State

「HTTP/2 Implementation Debug State」というHTTP/2用のデバッグ情報を表示するエンドポイントの仕様が、IETFに提出されています。

https://tools.ietf.org/html/draft-benfield-http2-debug-state-01


サーバの「.well-known/h2/state」にアクセスすることでそのコネクションの状態を表示するサーバ側のエンドポイント、及びその内容を定義しています。

mod-h2 http2-status

mod-h2のversion 1.6.0より http2-status が、この仕様に準拠したので簡単に動作確認する。


ubuntu16.04(openssl 1.0.2)で、今回は svn のtrunkからビルドする

#nghttp2をインストールしておく
sudo apt-get install -y  libtool libtool-bin libpcre3-dev autoconf libssl-dev libxml2-dev libev-dev build-essential 

svn checkout http://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x httpd-2.4.x
cd ../httpd-2.4.x/
svn co http://svn.apache.org/repos/asf/apr/apr/trunk srclib/apr
./buildconf

./configure
make

証明書の設定及びSSL(TLS)とHTTP/2を有効にし、confにhttp-2statusの設定を加える

   Protocols h2c http/1.1
   <Location "/.well-known/h2/state">
        SetHandler http2-status
   </Location>

結果

/.well-known/h2/state

{
  "version": "draft-01",
  "settings": {
    "SETTINGS_MAX_CONCURRENT_STREAMS": 100,
    "SETTINGS_MAX_FRAME_SIZE": 16384,
    "SETTINGS_INITIAL_WINDOW_SIZE": 65535,
    "SETTINGS_ENABLE_PUSH": 1
  },
  "peerSettings": {
    "SETTINGS_MAX_CONCURRENT_STREAMS": 1000,
    "SETTINGS_MAX_FRAME_SIZE": 16384,
    "SETTINGS_INITIAL_WINDOW_SIZE": 6291456,
    "SETTINGS_ENABLE_PUSH": 1,
    "SETTINGS_HEADER_TABLE_SIZE": 4096,
    "SETTINGS_MAX_HEADER_LIST_SIZE": -1
  },
  "connFlowIn": 2147483647,
  "connFlowOut": 15707901,
  "sentGoAway": 0,
  "streams": {
    "41": {
    "state": "HALF_CLOSED_REMOTE",
    "created": 1473179424.329667,
    "flowIn": 65535,
    "flowOut": 6291456,
    "dataIn": 0,
    "dataOut": 0
    }
  },
  "stats": {
    "in": {
      "requests": 21,
      "resets": 0, 
      "frames": 24,
      "octets": 1250
    },
    "out": {
      "responses": 20,
      "frames": 43,
      "octets": 21712
    },
    "push": {
      "cacheDigest": "AQg",
      "promises": 0,
      "submits": 0,
      "resets": 0
    }
  }
}


幾つかの仕様上でオプショナルな物は表示されない。例えばHPACKのダイナミックテーブルの情報なども仕様上は定義されているが、セキュリティ上の理由により検討して仕様すべきとされている。

Site-Wide HTTP Headers とは

Mark Nottingham氏から、HTTPレスポンスヘッダをサイト全体で使いまわせるようにする 「Site-Wide HTTP Headers」 という仕様が提案されています。

https://tools.ietf.org/html/draft-nottingham-site-wide-headers-00


例えば、Public-Key-PinsやStrict-Transport-Securityというものはオリジン全体に適応されるヘッダですし、Content-Security-Policyも殆どの同じように使用されます。HTTP/2になりHPACKでヘッダは圧縮されるものの、デフォルトで4KでありContent-Security-Policyがそのうちの殆どを使用してしまってるケースも観測されています(3KのCSPヘッダなどもあるようです)。


そこで、HPACKのサイズも抑えるために、より少ないヘッダでサイト全体に適応されるヘッダを表現できるようにするのが、Site-Wide HTTP Headersです。

  • 1. ユーザエージェントはまず、site-headersを取得します
  • 2. 次回からユーザエージェントはHTTPリクエストを送信する際、site-headersを持ってることを通知します
  • 3. サーバはsite-headersを指し示して、site-headersのヘッダが付加されていることを示します
1. site-headersリソースの取得

サーバはRFC5785で定義される、URIでsite-headersリソースを提供します。このリソースはユーザエージェントによってGETで取得するか、サーバからServerPushで送信されます。
このHTTPレスポンスでは、Content-Typeに"text/site-headers"がセットされます。さらに、ETagが付加されて無ければなりません。
# a以下がサイト全体で使用するヘッダセットです。

HTTP/1.1 200 OK
Content-Type: text/site-headers
Cache-Control: max-age=3600
ETag: "abc123"
Content-Length: 1234

# a
Strict-Transport-Security: max-age=15768000 ; includeSubDomains
Server: Apache/2.4.7 (Ubuntu)
Public-Key-Pins: max-age=604800; 
  pin-sha256="ZitlqPmA9wodcxkwOW/c7ehlNFk8qJ9FsocodG6GzdjNM=";
  pin-sha256="XRXP987nz4rd1/gS2fJSNVfyrZbqa00T7PeRXUPd15w="; 
  report-uri="/lib/key-pin.cgi"
2. HTTPリクエスト

ユーザエージェントが改めてfoo.jpg等の別のリソースを取得するためにHTTPリクエストを送信する場合、最新のsite-headersリソースを持ってる事を示すために、SMヘッダに取得した時のETatの値を付加します。

GET /images/foo.jpg HTTP/1.1
Host: www.example.com
SM: "abc123"
3. HTTPレスポンス

サーバはHSヘッダで、site-headersの#aがこのレスポンスヘッダには付け加えて解釈するようにユーザエージェントに指示します。

HTTP/1.1 200 OK
Content-Type: image/jpeg
Vary: SM, Accept-Encoding
Cache-Control: max-age=3600
HS: "a"
Transfer-Encoding: chunked


つまり、上記のHTTPレスポンスヘッダは下記のように解釈されます

HTTP/1.1 200 OK
Content-Type: image/jpeg
Vary: SM, Accept-Encoding
Cache-Control: max-age=3600
Connection: close
Strict-Transport-Security: max-age=15768000 ; includeSubDomains
Server: Apache/2.4.7 (Ubuntu)
Public-Key-Pins: max-age=604800; 
  pin-sha256="ZitlqPmA9wodcxkwOW/c7ehlNFk8qJ9FsocodG6GzdjNM=";
  pin-sha256="XRXP987nz4rd1/gS2fJSNVfyrZbqa00T7PeRXUPd15w="; 
  report-uri="/lib/key-pin.cgi"

ポリシーをオリジン全体に適応する Origin Policy (sec-origin-policy)

2020126 追記
仕様も大きく変わっているので、記事を書き直しました
asnokaze.hatenablog.com


20190218 追記
sec-origin-policyヘッダを使うように変更になった模様


WICGで議論されている「Set origin-wide policies via a manifest」の仕様がGoogleのMike West氏から提案されています。この「Origin Policy」と言う仕様は、氏のGithubリポジトリから確認できます。


これは、Content-Security-PolicyやReferrer-Policyといったレスポンスヘッダでリソースごとに毎回指定していたものを、Origin Policy Manifestとしてオリジンに対して指定できるようにするものです。


クライアントは、このマニフェストに書かれているヘッダがレスポンスヘッダに付いていると仮定して処理を行います。

サーバはまず、レスポンスヘッダのOrigin-Policyヘッダでマニフェストファイルの場所を指示します。

HTTP/1.1 200 OK
Content-Encoding: gzip
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html
...
Origin-Policy: "policy-1"


Origin-Policyを受信したクライアントは、そのマニフェストファイルを取りに行きます。Origin Policyのマニフェストファイルは.well-known/origin-policy/に置くことになっており、今回は「https://example.com/.well-known/origin-policy/policy-1」から取得します。
このマニフェストファイルもHTTP/2のPushを使用することで遅延を抑えられる旨仕様に書かれています。マニフェストファイルは以下の様になっています

{
  "headers": {
    "fallback": [
      {
        "name": "Content-Security-Policy",
        "value": "script-src 'self' https://cdn.example.com"
      },
      {
        "name": "Referrer-Policy",
        "value": "origin-when-cross-origin"
      }
    ],
    "baseline": [
      {
        "name": "Content-Security-Policy",
        "value": "object-src 'none'; frame-ancestors 'none'"
      },
      {
        "name": "Strict-Transport-Security",
        "value": "max-age=10886400; includeSubDomains; preload"
      },
      {
        "name": "X-Content-Type-Options",
        "value": "nosniff"
      }
    ]
  },
  "cors-preflight": { /* TODO(mkwst): Syntax? */ },
}

baselineは今後の全てのレスポンスヘッダに追加して解釈されるもので、fallbackは今後のレスポンスヘッダに該当のヘッダがなかった場合に追加して解釈されるヘッダが指定されます。

cors-preflightは、サーバがCORSの通信を解釈できることを示しています。クライアントは幾つかのCORS-preflightリクエストが成功することを前提に出来るようです。


クライアントは以降より、リクエストヘッダに適応しているマニフェストファイル名をOrigin-Policyヘッダに付加します。(適応しているOrigin Policyが無ければ、「Origin-Policy: 0」)

Origin-Policy: "policy-1"
|<<

QUICの仕様を翻訳していく (追記 2018/10/26

2018/10/26

現在最新版の draft-16 について翻訳を開始しました
GitHub - flano-yuki/my-quic-spec-translation: QUICの仕様の翻訳

2017年追記

2017年時点で、仕様は更新され、拡張仕様も出てきています。
asnokaze.hatenablog.com

QUIC in ietf96

Google の試験的トランスポート、QUIC のアップデート」などでも紹介されている、Googleが提案・実装してるQUIC。


すでに関連するドキュメントはChromium Projects配下のページで公開されていますが、先日IETFにQUICの仕様が提出されています。


さらに先週ドイツで行なわれたIETF96の中でQUICに関する議論が行なわれIETFとしてQUICの標準化を進める事が決まりました。セッションの発表ではQUICのプロトコルのオーバービュー、Googleでのデプロイ状況及び統計情報、Akamaiからの発表、TLS1.3の使用に関する発表が行われましたその時の資料は、既にアップロードされています。

仕様

さて、既にIETFにはQUICに関する仕様が4つ提出されています。

Coreになる仕様を中心に、3つの仕様が付随しQUICというプロトコルを作り上げている。仕様としてはversion 00でありTODOや誤字などがある。また、TLSの仕様に関してはTLS1.3側の変更を取り込んだものが著者のgithubに上がっているものの、TLS1.3もまだ変更があるため今後も変更されるだろう。

翻訳

翻訳という大それたものではないが、読んだ副産物として翻訳を公開してみようと思う。自分でもわかりにくい部分があると思ってるので、時間を見つけて直していきます。完璧な翻訳を与えるものではありませんが、より良いものになるように努めます。


今のところ、4つのうち2つだけ
https://github.com/flano-yuki/my-quic-spec-translation


残りの仕様も読んでますが、やはり難しい...

iframeのアプリケーションにPermissionを委譲する仕様

追記 20180918
Chromeではこういう形で実装されました (W3Cで提案されているものとは別物)
asnokaze.hatenablog.com

Permission Delegation To Embedded Web Applications

iframeで埋め込まれたクロスオリジンのWebアプリケーションがPermissionを要求するとこともあります、しかしユーザにとっても分かりづらく、扱いづらい点があります。Googleの調査でも、ユーザはiframe内のアプリケーションによるPermission要求を正しく理解していないという結果が出ているようです。


W3Cでは、公式のドキュメントにはなってませんが、「Permission Delegation To Embedded Web Applications」という、 iframeなどで埋め込まれたWebアプリケーションへのPermission委譲の仕様が議論されています。Chromeでも実装への議論がメーリングリスト上で行なわれています。


基本的に、埋め込む側がPermissionを保持し、そのPermissionを埋め込まれた側に委譲する仕組みです。こうすることで、ユーザとしてもどのオリジンにPermissionを許可しているか分かりやすくなります。ブラウザがPermissionを記憶したり解除する際もiframeを埋め込んでいるオリジンの管理だけで良くなりますし、管理画面も分かりやすくなるでしょう。


さらに、今までは埋め込まれる側がPermissionを要求することを規制できませんでしたが、委譲方式をとることで埋め込む側がコントロールできるようになります。


ただし、互換性の問題や、ユーザは今まで通り自身でPermission管理を行いたいという要望もあるかもしれません。

委譲方法

委譲方法は、iframeの属性に指定する方法と、JavaScriptから実行する方法があります。


以下の通り、permissionsに要求するpermissionを指定することで委譲することが出来ます。

<iframe id="embedee" src="https://maps.example.com/" permissions="geolocation"></iframe>


また、Javascriptからnavigator.permissions.delegateと実行することで指定されたiframeにPermissionを委譲出来ます。

var iframe = document.getElementById('embedee');
navigator.permissions.delegate({embedee: iframe, name: 'geolocation'}).then(
  function() {
    // Delegated geolocation.
  }).catch(function() {
    // Delegation failed.
  });


JavaScriptからは委譲を辞めることも出来ます。

navigator.permissions.undelegate({embedee: iframe, name: 'geolocation'});