DNS ALTSVC recordの提案仕様

Alternative ServicesをDNSで通知できるように、ALTSVCレコードを定義する「Finding HTTP Alternative Services via the Domain Name Service」という仕様が出ています。

DNSレコードタイプの議論ですが、HTTPbis WGから議論が始まっています。

HTTP Alternative Services

Alternative Servicesは、オリジンを別のエンドポイント、別のプロトコルで提供できることをクライアントに通知する仕組みです。

例えば、現在もQUICの通信を始める際はHTTPレスポンスヘッダで自身のサービスはQUICでも提供できることを通知しています。

googleにアクセスすると、下記のようにalt-svcヘッダを付けてきます

alt-svc:hq=":443"; ma=2592000; quic=51303431; quic=51303339; quic=51303338; quic=51303337; quic=51303335,quic=":443"; ma=2592000; v="41,39,38,37,35"

現在alt-svcを通知する方法は 「RFC7838 HTTP Alternative Services」で定義されているとおり

  • alt-svcレスポンスヘッダを用いる
  • HTTP/2でALTSVC frameを使用する

これらは、HTTPの通信が始まってから切り替えを行う流れになります。

より早いタイミングで、Alternative Servicesを通知するためにDNSレコードを使う方法が出てきたというわけです。

DNS ALTSVCレコード

Alternative Servicesを通知するALTSVCレコーとタイプを新しく定義します。

例えば、このようなalt-svcレスポンスヘッダは

Alt-Svc: h2=":8000"; ma=60

このようなDNSレコーとなります

_https._443.www.example.com. 60S IN ALTSVC "h2=\":8000\""

クライアントは、A/AAAAレコードを取得する際にALTSVCレコードも問い合わせ、Alternative Servicesを確認することでより早く代替エンドポイント・プロトコルで接続することが出来ます。

何故SRVレコードを使わないのか

似たようなレコードとして、SRVレコードが有ります。SRVレコードはそのドメインが対応しているか確認できる。

例えば、「_http2._tcp.example.com.」のように問い合わせる。

このSRVレコードを使わない理由としては以下などがあげられている

  • プロトコルのアップグレードすることを指示できない
  • パラメータの拡張性がない
  • 後の接続でAlt-Svc情報の処理をスキップ出来ない

HTTP/2 クロスオリジン サーバプッシュを可能にする提案仕様

現状HTTP/2では、クロスオリジンでのサーバプッシュは出来い。

a.comからb.comのコンテンツを、b.comのコンテンツだとしてプッシュされてもクライアントは信頼することが出来ない。


f:id:ASnoKaze:20180114234751j:plain

それを可能にする「Origin-signed HTTP Responses」という仕様がGoogleのJeffrey Yasskin氏から提案されている。

Origin-signed HTTP Responses」では、コンテンツを署名する仕組みを定義している。b.comの署名があれば、b.comのコンテンツを別の所から配布しても信頼できるという感じである。

自分も正しく理解できてるかわからないが、簡単に読んでみた。

Origin-signed HTTP Responses

Origin-signed HTTP Responses」は、以前書いたWebPackagingの議論からでてきた仕様である。
asnokaze.hatenablog.com

Webページを1つにまとめる仕組みとそれに署名する仕組みは分離され、後者が「Origin-signed HTTP Responses」である。

コンテンツをCBOR形式で表現する

再配布するコンテンツは、HTTPリクエストヘッダ・HTTPレスポンスヘッダ含めてCBOR形式で表現され、このファイルに対して署名が行われる

例えば、以下のようなやりとりは

GET https://example.com/ HTTP/1.1
Accept: */*
HTTP/1.1 200
Content-Type: text/html
Signed-Headers: "content-type"

<!doctype html>
<html>
...

CBOR形式で以下のように固められる(分かりやすいように書き下されている)

[
  "request",
  [
    ':method', 'GET',
    ':url', 'https://example.com/'
  ],
  "response",
  [
    ':status', '200',
    'content-type', 'text/html'
  ],
  "payload",
  '<!doctype html>\n<html>...'
]

この時、Signed-Headersレスポンスヘッダで指定された領域が署名の対象に含まれる

Signatureヘッダ

この仕様では、Signatureレスポンスヘッダを新しく定義している。

Signature: sig1;
  sig=*MEUCIQDXlI2gN3RNBlgFiuRNFpZXcDIaUpX6HIEwcZEc0cZYLAIga9DsVOMM+g5YpwEBdGW3sS+bvnmAJJiSMwhuBdqp5UY;
  validityUrl="https://example.com/resource.validity";
  certUrl="https://example.com/certs";
  certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;
  date=1511128380; expires=1511560380
  • sig: 署名 (リクエストメソッド, リクエスURI, Signed-Headersで指定されたレスポンスヘッダ, ペイロードの署名)
  • validityUrl: 署名が更新(expire)された場合、validityUrlで指定された場所に新しい署名が格納されている
  • certUrl: 証明書のURL。TLS1.3のCertificate message形式のデータが得られる
  • certSha256: 証明書のハッシュ
  • date: 現在のUnix Time
  • expires: 失効 Unix Time (現在時間より7日以内である必要がある)

元コンテンツがレスポンスする際にSignatureヘッダを付けてくるので、再配布者はこれをそのまま再配布すれば良い

クライアントはそのコンテンツの署名を確認し、有効であれば受け入れる。

HTTP/2 extension for cross-origin Server Push

HTTP/2でクロスオリジン サーバプッシュを可能にするため、幾つかの拡張を定義している。

まだ、IETFに提出されている提案仕様では含まれていないが、Github上ではmasterにコミットされている(コミットURL)

  • ENABLE_CROSS_ORIGIN_PUSH SETTINGSパラメータ: クロスオリジン サーバプッシュに使うことをネゴシエーションするためのSETTINGSパラメータ
  • NO_TRUSTED_EXCHANGE_SIGNATURE error code: プッシュされたコンテンツが署名検証できなかったときなど、信頼できなかったときのエラー

雑感

なんでサーバ証明書そのものを、ヘッダに入れないんだろうか、キャッシュできるようにかな

preloadを使うのがシンプルで強力だが、クロスオリジン サーバプッシュも面白いなと思いました。

そろそろ標準化されるHTTP/2 ORIGIN フレームについて

HTTP/2の拡張仕様で、ORIGINフレームという提案仕様が大詰めを迎えています。
https://tools.ietf.org/html/draft-ietf-httpbis-origin-frame-05


IESGに送られていますが、現在のところでは反対意見は出ていません。

このORIGINフレームについては、数年前に一度記事を書きましたが幾つかの更新があるので補足する。
asnokaze.hatenablog.com

背景

HTTP/2では、上限があえば異なるオリジンとの通信でも1つのコネクションを使いまわすことが出来ます。

しかし、クライアントがコネクションを使いまわそうとしたものの、サーバ側がそのオリジンのコンテンツを提供できないという場合があります。その時は、サーバは421 (Misdirected Request)を返すことになっています。

421レスポンスを貰ってから再度目的のオリジンにコネクションをはるというのは、待ち時間を発生させます。

ORIGIN フレームは、サーバ側が対応しているオリジンのリストをクライアントに提供することで、クライアントはコネクションを再利用可能か判断出来るようにします。

HTTP/2 ORIGINフレーム

初期の提案仕様と異なり、複数オリジンをOrigin-Entryとして1つのフレームで送信できるようになっています

ORIGINフレームの中身はOrigin-Entryと呼ばれる領域が一つだけあります。

   +-------------------------------+-------------------------------+
   |         Origin-Entry (*)                                    ...
   +-------------------------------+-------------------------------+

Origin-Entryの中身は、通知するオリジンが長さと値を繰り返したものです。複数のオリジンを通知できます。

   +-------------------------------+-------------------------------+
   |         Origin-Len (16)       | ASCII-Origin?               ...
   +-------------------------------+-------------------------------+

ASCII-Originは、Scheme, Host, Portからなり、https://example.com:443 のように記述されます

クライアントはORIGINフレームで指定されたオリジンの場合のみそのコネクションを再利用できます。書かれてないものに関してコネクションを再利用しては行けません。

セキュリティ上の対応

セキュリティ上の対応で、初期提案と比べ変更点が有ります

  • クライアントはhttpsで接続している場合のみ、ORIGINフレームを解釈する。h2cの場合は無視する
  • *.example.comのようなアスタリスクは無効
  • alt-svcの広告の影響をうけない
  • コネクションを再利用するオリジン間でTLSハンドシェイク中に取得した証明書が有効なこと(subjectAltName or ワイルドカード証明書)

特に大きなトピックだったのは、クライントはORIGINフレームで提供されたオリジンの名前解決をすべきかどうかでした。

HTTP/2のコネクション再利用条件に、同じIPアドレスであることが条件の一つになっていますが、悪意あるサーバに悪用される可能性があるため、ORIGINフレームでもIPアドレスが同一かどうか確認するべきなのかが議論になりました。

今のところ、名前解決をしなくても良いことになっていますが、その場合はCTやOCSPをつかって証明書の確度をあげるべきだとしています。

Amazonの人が提案するDistributed OAuthという提案仕様

AnazonのDick Hardt氏が「Distributed OAuth」という仕様を提案している。

昨年行われたIETF100でも議論があり、来週行われるOAuth WG Virtual Meetingでも話が進められるようだ。

遅らせばながら簡単に読んで見る。

Distributed OAuthとは

通常のOAuth2では、一つのリソースサーバと一つの認可サーバがあり、その関係は固定的です。この提案仕様では、もっと複雑で大きなケースを想定しています。
同様の機能を持つ複数のリソースサーバと、リソースサーバごとに別の認可サーバがあるようなケースです。

IETF100の発表資料を見るとイメージは湧きやすいでしょう(あくまで例だと思います)
f:id:ASnoKaze:20180108010721p:plain
https://datatracker.ietf.org/meeting/100/materials/slides-100-oauth-sessa-distributed-oauth/

Distributed OAuthでは、クライアントがリソースの認可を行う認可サーバをリソースサーバに問い合わせることで、そのリソースサーバと関連する認可サーバを知れるようにする提案仕様です。

Distributed OAuthフロー

f:id:ASnoKaze:20180108011422p:plain

  • (A) クライアントは認可サーバを見つけるために、Authorizationヘッダをつけずリソースサーバにリクエストを送信する (この時TLS通信で得られた証明書から、hostを取得しておく)
  • (B) リソースサーバは401レスポンスを返す。WWW-Authenticateヘッダのissトークンを発行する認可サーバのエンドポイントを指定する
       HTTP/1.1 401 Unauthorized
       WWW-Authenticate: Bearer realm="example_realm",
                                iss="http://issuer.example.com/token",
                                scope="example_scope",
                                error="invalid_token"
  • (C) クライアントはissで指定されたURLにアクセストークンリクエストを送信する
       POST /token HTTP/1.1
       Host: issuer.example.com
       Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
       Content-Type: application/x-www-form-urlencoded

       grant_type=client_credentials
       &scope="example scope"
       &host=resource.example.com
       &realm="example_realm"
  • (D) 認可サーバはhost属性を持つアクセストークンを払い出す際に、
  • (E) クライアントは保護されたリソースにアクセストークンを持ってアクセスする
  • (F) リソースサーバはそのアクセストークンのhost属性がリソースサーバのhostであることを検証を行い、保護されたリソースへの処理をおこないます。

攻撃モデル

このDistributed OAuthでは、幾つかの攻撃モデルについて検討しています。

  • 悪意あるリソースサーバがクライアントから得たアクセストークンを再利用し、別のリソースサーバにアクセスする
  • 悪意あるリソースサーバは、異なるリソースサーバーで使用できるアクセストークンを取得するようにクライアントに指示しアクセストークンを取得する
  • 悪意あるリソースサーバは、悪意ある認可サーバにクライアントをリダイレクトし、クライアントからの情報を取得する

host属性を検証する、mutual TLS・proof of possion認証で上記の問題は緩和できるらしい...

RFC 8307 WebSocketにおけるWell-Known URIの標準化

RFC 8307 Well-Known URIs for the WebSocket Protocol」でWebSocketにおいても Well-Known URIが使用できるようになりました。

この界隈では珍しく、個人ドラフトの-00から一気にRFCになっています
https://datatracker.ietf.org/doc/rfc8307/

Well-Known URI

httpやhttpsでは、Well-Known URIというものがあり特定用途に使用するURIが予約できるようになっています (RFC 5785)

例えば、https://example.com/.well-known/○○○○○ のように /.well-known/ 以下に特定用途のURIが予約されます。

現在登録されているWell-Known URIの一覧は、IANAのページから確認できます (URL)

例えば以下のような物があります

  • /.well-known/acme-challenge/ : 証明書自動発行のためのプロトコルであるACMEで、チャレンジに使用される
  • /.well-known/http-opportunistic : HTTPで日和見暗号を使用する際の情報が記載される
  • /.well-known/dns-query : DNS over HTTPのリクエスト先URIとして使用される(策定中の仕様)

WebSocketの場合

Well-Known URI自体を定義するRFC5785ではHTTPとHTTPS スキームの場合についてのみ言及しており、ws://やwss://での使用は定義されておりませんでした。

そこでRFC 8307では、ws://やwss://でもHTTPの場合と同様に Well-Known URIに対応する事が決められました。

用途

なぜこのような仕様が出てきたのか一見わからなかったのですがMLを遡ると、「CoAP (Constrained Application Protocol) over TCP, TLS, and WebSockets」という提案仕様においてWebSocketでもWell-Known URIを使うようだ。

CoAPは、IoTデバイスなどのリソース・ネットワークが制限された環境下での使用を目的とした軽量アプリケーションプロトコルで、この提案仕様はそのCoAPをWebSocket上で通信させる仕様のようだ。

以下のように、ws://でもWell-Known URIを使用している

ws://example.org/.well-known/coap

具体例を見るに、WebSocketのアップグレード手順内で使用されるエンドポイントのようだ。

            GET /.well-known/coap HTTP/1.1
            Host: example.org
            Upgrade: websocket
            Connection: Upgrade
            Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
            Sec-WebSocket-Protocol: coap
            Sec-WebSocket-Version: 13

なるほど。

2017年 振り返り

2017年を振り返る

今年はブログ記事をたくさん書いた。
60記事、そのうち internet-draftタグの付いたものは33にもなる、W3Cは10であった。

ブログに加え、登壇や執筆などを行えた。さらに、Mozaic.fmにも出させてもらえた。
mozaic.fm

某省庁に呼ばれたり、IETFにもつづがなくオフライン参加出来た。

反面、うまく進められなかった案件もあるし、手を動かしたりすることも少なかったし、コードも全然書けていない。

来年は、標準化にコミット出来るように頑張りたい。

ただ、どんなに忙しくても、華はなくても、バズらなくてもInternet-Draftを読んでブログ書く事は続けたい

今年も皆様ありがとうございました。来年もよろしくお願いします。

今年の記事一覧 (降順)

DTLSにコネクションIDを導入する提案仕様

DTLSにコネクションIDを導入する「The Datagram Transport Layer Security (DTLS) Connection Identifier」という提案が出ています。

DTLS1.3の議論を進める中で出てきたトピックであり、既にWG Draft になっています。

DTLSとセッション

DTLSはUDPのようなコネクションのないトランスポート上で暗号化された通信を行うためのプロトコルです。階層にコネクションのないトランスポートを使っている一方で、現在のDTLSは送信元IP・送信先IP・送信元ポート番号・送信先ポート番号でDTLSのセッションを識別しています。

そのため、それらのうちどれかが変わってしまうと、鍵交換を含むハンドシェイクを初めからやり直す必要があります。特にIoTデバイスなどが普及していく中で、移動する端末や通信頻度が低いもの(NATリバインディングが起こる)は容易に起こるでしょう。さらにそういった端末はリソースが潤沢でない可能性があります。

DTLS Connection Identifier では、DTLSにコネクションIDを導入しIPやポート番号が変わっても通信を維持できるようにします。これにより、ハンドシェイクを1からやり直す手間が省けます。

DTLS Connection Identifier

The Datagram Transport Layer Security (DTLS) Connection Identifier」では、DTLS1.2とDTLS1.3両方について対応します。

この提案仕様では、DTLSに以下を追加します

  • connection_id拡張:ClientHelloとServerHelloで使われる、コネクションIDの使用をネゴシエーションするための拡張
  • RequestConnectionId:DTLS1.3で相手にコネクションIDを要求するメッセージ
  • NewConnectionId:DTLS1.3で相手にコネクションIDを通知するメッセージ (コネクションIDをすぐに使うか、予備か指定可)
  • Record Layer Extensions:レコードに現在のコネクションIDを指定できるように変更される。

例えば、DTLS1.2では、以下のようにcidが追加されます。

     struct {
        ContentType type;
        ProtocolVersion version;
        uint16 epoch;
        uint48 sequence_number;
        opaque cid[cid_length];               // New field
        uint16 length;
        select (CipherSpec.cipher_type) {
           case block:  GenericBlockCipher;
           case aead:   GenericAEADCipher;
        } fragment;
     } DTLSCiphertext;

コネクションIDの使用

DTLS1.3及びDTLS1.2で微妙に手順は異なるが、ハンドシェイク中にconnection_id拡張でコネクションIDの使用が合意されると、以降コネクションIDが含まれたメッセージを交換するようになる。

以下は、DTLS1.2の例である
f:id:ASnoKaze:20171230233015p:plain