近年HTTPもバイナリフレームを採用したHTTP/2やその次のHTTP/3など新しい仕様が発表され進化を遂げています。
また、GraphQLやRPCなどHTTPの上で動くプロトコルも出てきている中、ベースとなっているHTTPの仕様はもはや低レイヤーと呼べるようになってきつつあると私は思います。
しかしエンジニアたるもの基本の仕様を理解せずして何をか言わんやです。ということで今回は内定先のCARTA HOLDINGSの成長支援制度のお世話になってReal World HTTP第2版をゲットしたので、それを読んだ内容について色々つついていこうと思います。
HTTP1.0の世界
-
代表的なヘッダ
-
User-Agent: 自分のアプリケーション名が入る。(curlならcurl/7.48.0みたいに)クライアントのOSが入ってくることもよくある気がする。
-
Referrer: クライアントがリクエストを送るときに見ていたページのURL.
-
-
ステータスコード100番台
- 処理中の情報の伝達に使う。(特殊な用途らしいので使われてるのを見たことがなかった)
-
ステータスコード300番台
- 恒久的なリダイレクトか一時的なリダイレクトかでステータスコードが変わる。今後も移転前のリソースが存在すれば一時的、そうでなければ恒久的なリダイレクトになる。
- 301(Moved Parmanently)は恒久的リダイレクト。ドメイン移転やHTTPS化するときのリダイレクトなど、移転前のリソースをもはや使わない場合に301を使う。Googleも検索エンジンに移転を伝えるための手段として推奨してる。リダイレクトされたことをキャッシュする。
- 302(Found)一時的リダイレクト。一時的なメンテナンスやモバイルサイトへの転送で使う。リダイレクトされたことをキャッシュしない。
- 301でも302でも移転後のリソースへの再リクエストではメソッド変更が許可されている。(例えば初回のリクエストがPOSTでもリダイレクトのリクエストをクライアントの好きにGETなどに変えて良い)
-
curlコマンド
-
curl -d @test.json
のように@をつけるとファイルから読み込んだ内容を送れる。
-
キャッシュ
- そもそもキャッシュにはブラウザのキャッシュと、CDNなどのサーバとブラウザの間に入るタイプのキャッシュの2パターンがある。
- ブラウザのキャッシュにはLast-ModifiedヘッダとExpiresヘッダを使った実装がある
- Last-Modifiedは前回のレスポンスの日時をリクエストヘッダに引き回して、サーバから304(Not Modified)が返ってきたらブラウザキャッシュを利用する。200が返ってきたときはコンテンツをキャッシュしておく。キャッシュを使う場合でもサーバへのリクエストが発生するのが特徴。
- Expiresヘッダを使う場合は、クライアントが前回のレスポンスのExpiresヘッダを見て期限切れでなければそもそもサーバへのリクエストを行わずキャッシュを使う。期限切れの場合にサーバアクセスを行う。
- なお、期限切れになったからといって即座にrefetchが走るわけではないので頻繁に内容が変わるSNSのトップページなどに使ってはいけない。スタイルシートなどのあまり内容の変わらないリソースに使うこと。
- 最長でも一年にすることがRFCで推奨されている。
認証認可
- SSO. アカウント管理を一括して行い、一度ログインしたら全てのシステムでログインしたことにする。
- SAML. SPがログインしていないと判定したらIdPにリダイレクトさせてログインさせる。SPとIdPの間で直接通信しないことが特徴。
- OAuth. 認可のための仕組みである。
- OIDC. OAuth2.0をベースに認可だけでなく認証に使っても良いよう拡張した仕様。
- JWT. 3つのbase64エンコードした文字列をピリオドで連結した形を取っている。トークンを受け取ったクライアントは署名を検証の上、あとはトークンに含まれる認証済み属性をサーバアクセスなしで利用できる。(これがJWTの最大の利点)なお、使う際は署名や有効期限、発行元など無効判定を必ず行うこと。また、ログアウト処理には「トークンの有効期限を短くしてリフレッシュさせる」「ログアウトしたトークンのIDをDBに入れて判定する」などのバリエーションがある。
双方向通信
- WebSocket(去年のTreasureで使ったなぁ懐かしい), Server-Sent Eventsが紹介されてた。どちらもサーバ側から何度もレスポンスを返すことができる。
Webを強くする様々な技術
- DNS事前問い合わせ。CSSに含まれるWebフォントなどのDNS問い合わせをCSSの問い合わせと並列に行うことで、高速化を図る。最初のHTTPレスポンスでLinkヘッダやlinkタグをHTMLに含めて返すか、HTTP/2のpreconnenctを用いる。
- 実はブラウザは短期間だけDNSの結果をキャッシュすることで高速化を図っていた。少し遅れてサーバにAjaxで通信したりするときに効果が出る。
- DNSのAレコードを複数設定して、1つのドメインに複数のIPを対応させておくと、レスポンスとなるIPが順繰りに変化する。これを利用したロードバランスをDNSラウンドロビンという。
- AkamaiではCNAMEレコードを使ってDNSリクエストを自身の権威サーバへ移譲してもらい、ユーザーのアクセス元の地域に応じたキャッシュサーバのIPを返す制御を行っている。
- SRVレコードを使ったサービスディスカバリ。サービスの種別から名前解決を行い、レコードに定義された優先順位に従ってクライアントはアクセスする。
- リバースプロキシ。実際に何を行うかはプロキシにより大きく異なる。エッジそばでコンテンツをキャッシュしていればCDNだし、ロードバランスしていればロードバランサだし認証したりログを出したり、静的コンテンツを返しつつAPIサーバへのリクエストを投げたり、などなど。
- ただし、各ユーザのみの情報を扱うブラウザキャッシュとは違い、CDNはプロキシ経由で後ろのAPIサーバへリクエストを投げることもあるという特性上様々なユーザの情報をキャッシュしうるためインシデント時の影響が大きくなりやすいという注意点もある。メルカリではCDN切り替えの際最初にアクセスしたユーザの情報が、同じURLにアクセスした後続のユーザにも見えてしまうというインシデントが発生した
- Liveness Prove vs Rediness Prove. 前者はそのサービスが生きてるかどうかだけ確認する。後者はそのサービスが依存するサービスが生きてるかも確認する。
- JWTはマイクロサービス構成において証明書があれば各サービスでトークンの検証ができる、つまり認証サービスへのアクセス負荷を下げられるのでよく使われる。
セキュリティ
- CSRF。HTTPのステートレス性を利用した攻撃。ブラウザが誘導されたページにもCookieを発行してしまうことを利用して、偽サイトなどから本物のAPIサーバにユーザが意図しない操作を投げる(投げさせる)攻撃である。フォームのhiddenな要素にサーバからランダムなトークンを発行しておくことで、攻撃者にリクエストの再現を困難にさせることで防げる。
- 中間者攻撃に対するHSTS(HTTP Strict Transport Security). 悪意のあるWi-Fiアクセスポイントなどで通信の盗聴などを行おうとする攻撃を中間者攻撃と呼ぶ。これに対抗する仕組みの1つがHSTSで、レスポンスヘッダに「今後はHTTPSで接続してほしい」という返すというもの。ただし、初回の通信はHTTPなので、初回の戻りの通信で悪意のあるプロキシに偽サイトへのリダイレクトをしてしまうとブラウザが検知する方法がない。(HTTPSであれば署名を検証すれば良いのだが)一応、ChromeではHSTSを行っているサーバとして申請すれば自身のサーバをChromeのDBに登録することができる。登録されるとSafariなどの他のブラウザでもこの情報が引き回され、初回からHTTPSで通信するようになる。
- WebAuthn. スマホのロック解除時の指紋認証などのハード、OSの機能をブラウザから使えるように標準規格としたものがWebAuthn. W3Cの標準規格で、主要なブラウザが対応済み。BluetoothやNFC,指紋などでブラウザから認証できるようになる。