# 03. Web ________________________________________ Real World HTTP ミニ版 2019年3月版 https://www.oreilly.co.jp/books/9784873118789/ Webを支える技術 ── HTTP,URI,HTML,そしてREST(WEB+DB PRESS plusシリーズ) https://gihyo.jp/magazine/wdpress/plus/978-4-7741-4204-3 html5test.com https://html5test.com/compare/browser/index.html XMLHttpRequest - Wikipedia https://ja.wikipedia.org/wiki/XMLHttpRequest Web API: The Good Parts 2014年11月 https://www.oreilly.co.jp/books/9784873116860/ ________________________________________ ## 1. JS対応状況 ________________________________________ ECMAScript 6 compatibility table https://kangax.github.io/compat-table/es6/ browser |version |expire(jp) -------------------------------------|------------|----------- iOS latest Safari |es2021※1 |- Android latest Chrome |es2021※1 |- Windows10 latest Edge |es2021※1 |- Windows10 latest Chrome |es2021※1 |- OS X latest Safari |es2021※1 |- iOS 9.3.6 Safari(iPad mini) |es2011 |- iOS 9.3.6 Safari(iPhone4S, iPad2) |es2011 |2019/04/30 iOS 10.3.4 Safari(iPhone5, 5c) |es2011 |2018/10/30 iOS 12.5.4 Safari(iPhone6, touch6) |es2015※1※2|- iOS 15.5 Safari(iPhone6s, iPad5) |es2021※1 |- Android 12.0(Pixel4a) Chrome |es2021※1 |- ※1 以下の機能は一部対応してないブラウザあり。使用は控えること - es2015 duplicate identifier(Appleは全滅) - es2015 Symbol.isConcatSpreadable, spreadable object with poisoned getter(大半対応していない) - es2015 変数名に特定の環境依存文字を含めること - es2017 shared memory and atomics(モバイル系は何らかの制限に引っ掛かるためか、実装されない) - es2018 RegExp Lookbehind Assertions(Appleは全滅) - es2019 Function.prototype.toString revision(Appleの挙動が一部刷新されていない) ※2 - iOS12のTemplateStrings permanent cachingにはバグがある ※3 実際のところ、適当にトランスパイルすれば問題ないため重要ではない ________________________________________ ## 2. Uri ________________________________________ URIの形式 ```text サンプルフォーマット https://example.jp:443/path?q1=a&q2=b#id-foo 1. 絶対パス、相対パス指定が可能 2. 使用推奨される文字は、アルファベット、数字、ハイフン、ドット 3. マルチバイト部分はエンコードされる 4. クエリ文字列(?から先の文字列部分)も同様。別名getパラメータ、クエリパラメータ 5. フラグメント(#。ページ内リンク)も同様 6. URIの長さは2038Byte(Edgeの制限) 7. URI命名の作法1、公開期限を想定する 8. URI命名の作法2、作者名、件名、状態、アクセス権などを含めない 9. URI命名の作法3、実装依存を避ける(cgi-bin/, .php etc.) 10. URI命名の作法4、SessionIDをuriに含めない ``` ________________________________________ ## 3. form要素、input要素、パラメータ、配列 ________________________________________ HTML フォームで配列を使用するにはどうすればよいですか? https://www.php.net/manual/ja/faq.html.php#faq.html.arrays 1. ブラウザはname重複を気にせずクエリ文字列やpostパラメータにする 2. ただしphp、railsのサーバ側は、キーに[]がついていないと対応しない 3. select multipleでも同様 例:下記チェックボックスがすべてチェックされていた場合 ```text ↓ ?hobby=sports&hobby=reading&hobby=music ↓ phpやrailsでは認識しない(hobby[]でないと、サーバ側が配列ならない) ``` ファイル送信とenctype ```text
ファイル送信を行う場合、form要素はenctype="multipart/form-data"が必要 なお、enctypeは厳密には3種類存在するが、事実上以下の2種類のみ application/x-www-form-urlencoded(デフォルト) multipart/form-data ``` ________________________________________ ## 4. Http ________________________________________ Forbidden header name (禁止ヘッダー名) https://developer.mozilla.org/ja/docs/Glossary/Forbidden_header_name ________________________________________ ### 4.1. Httpメッセージ Httpメッセージの形式 ```abnf = *( CRLF) CRLF [] = / = SP SP CRLF = SP SP CRLF ※ 要約すると、ヘッダとボディは空行で見分けている この単純な仕様を悪用するのがHttpヘッダインジェクション ``` Httpリクエストの例 ```http GET / HTTP/1.0 Host: localhost:18888 Connection: close Accept: */* User-Agent: curl/7.48.0 X-Test: Hello ``` リクエストヘッダー例 - forbidden(ブラウザ自動制御) - Accept-Charset - Accept-Encoding - Accept-Control-Request-Headers - Accept-Control-Request-Method - Connection - Content-Length - Cookie - Cookie2 - Date - DNT - Expect - Host - Keep-Alive - Origin - Proxy-xxx - Sec-xxx - Referer - TE - Trailer - Transfer-Encoding - Upgrade - Via - その他頻出 - Authorization - User-Agent - X-xxx - キャッシュ類 - 後述参照のこと Httpレスポンスの例 ```http HTTP/1.1 200 OK Date: Tue, 05 Apr 2016 13:58:43 GMT Content-Length: 32 Content-Type: text/html; charset=utf-8 hello, world ``` 代表的なレスポンスヘッダ― term |mean ----------------|---------------------------------------------- Content-Type |MIME Content-Length |ボディのサイズ(圧縮後)。ストリームなら不定値 Content-Encoding|圧縮がされていた場合の圧縮形式 Date |ドキュメントの日時 - GET と POST - GETは以下すべてを満たす場合にのみ使ってよい - 副作用がないAPI(サーバ側のログ以外のデータに変更がないAPI) - 秘密情報を送信していない(URLに秘密情報がそのまま載ってしまう) ________________________________________ ### 4.2. Httpステータスコード code|mean |例 ----|----------------------|------------ 200 |OK |成功 301 |Moved Permanently |サイト運営でドメイン変更に伴うリダイレクト 400 |Bad Request |エンドポイントが存在しないなど 401 |Unauthorized |HTTP認証に失敗(Basic/Digest/Windows/SECOMなど) 403 |Forbidden |適切なリクエストだが、拒否した場合。 404 |Not Found |エンドポイントが存在しないなど 500 |Internal Server Error |サーバが故障・DBがタイムアウトなど、サーバ側が原因のエラーが発生している場合 503 |Service Unavailable |サーバが故障・DBがタイムアウトなど、サーバ側が原因のエラーが発生している場合 - ステータスコード補足 ```text メンテ画面は、本来は503が適切(ただし200で返すことが多い) 業務エラーは、本来は403が適切(たたし200で返すことが多い) リダイレクトは301+Locationヘッダーで実現するべき URIルーティングはフレームワーク機能のURIルーティングで実現するべき ``` ________________________________________ ### 4.3. HTTP認証 1. Basic認証(利用禁止) 2. Digest認証(Basic認証の代わりに使う) 3. Bearer認証(定番) 4. クライアント認証(別名:証明書認証) 5. Windows認証(IISに存在するAD認証) ________________________________________ ### 4.4. HTTPキャッシュ 前提 - GET, HEADのみが対象 - キャッシュキーは、method + url + vary(selecting-header) - Http認証がある場合、publicキャッシュ(プロキシキャッシュ)されない - Varyのselecting-headerは意味的に同じであれば良い。「*」は無効 - Freshness(Expires, Cache-Control: max-age=n)が新鮮なら、問い合わせない - Validation(Last-Modified, ETag)は新規や新鮮でない場合に、更新確認を行う - 次の3種の書き方は、いずれも同じ効果 - Pragma: no-cache - Cache-Control: no-cache - Cache-Control: max-age=0 Last-Modified ```text // response Last-Modified: Wed, 08 Jun 2016 15:23:45 GMT // request If-Modified-Since: Wed, 08 Jun 2016 15:23:45 GMT ``` Expires ```text // response Expires: Fri, 05 Aug 2016 00:52:00 GMT ``` Pragma: no-cache ```text // response Pragma: no-cache // request Pragma: no-cache ``` ETag ```text // response ETag: "1dba6-131b-3fd31e4a" // request If-None-Match: "1dba6-131b-3fd31e4a" ``` Cache-Control ```text // response Cache-Control: no-store Cache-Control: public, max-age=86400 Cache-Control: private, max-age=86400 // request Cache-Control: no-store ``` Vary ```text // response Vary: Accept-Language ``` ________________________________________ ### 4.5. Cookieとセッション Cookie要約 1. Cookieはドメインごと。プロトコルやポートは見分けない 2. 各デフォルトは次の表を参照の事 3. ドメイン内で共有するには;path=/ 4. サブドメイン間の共有は、デフォルトでは行われない。明示的に指定すると共有できる。;domain=... 5. セキュリティに関わる挙動の詳細は別ページの「04. Web Application Security」も参照の事 Cookieのオプション属性とデフォルト |項目 |デフォルト |指定方法 | |-------------------|------------------------------|--------------------------| |有効期限 |現在のブラウザセッションのみ |expires, max-age | |送信プロトコル制限 |制限なし |secure | |jsアクセス制限 |制限なし |httponly(サーバ側用機能)| |送信先・ロード制限1|現在のドメインのみ(サブ含まず)|domain | |送信先・ロード制限2|現在のパス |path | |送信先・ロード制限3|過渡期(旧仕様None、新仕様Lax) |samesite | - セッションという用語の意味 1. ブラウザを開いたままにしているかどうか、の意味(ブラウザセッション) 2. SessionIDとサーバ側Session値の事(アプリケーションセッション) 3. 概念トランザクションに対する誤用(ECサイトにおけるカート~購入完了まで、など) 4. Tcp/Httpリクエストの開始から終了までの事(Tcpセッション、Httpセッション) 5. Tcp/Httpリクエストにおいて、KeepAliveが効いている期間に対する誤用 Cookieとセッション 1. アプリケーションセッションは、CookieでSessionIDを保持することで実現される 2. バランサ構成にする場合は、サーバ側Session値は共有Redisなどにする ________________________________________ ### 4.6. リバースプロキシ/LB越しの送信元IP https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Forwarded-For https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Forwarded-Proto ________________________________________ ## 5. ブラウザ固有仕様 ________________________________________ Same Origin Policy https://www.w3.org/Security/wiki/Same_Origin_Policy オリジン間リソース共有 (CORS) https://developer.mozilla.org/ja/docs/Web/HTTP/CORS Browser Security Handbook, part 2 https://code.google.com/archive/p/browsersec/wikis/Part2.wiki#Same-origin_policy ________________________________________ ### 5.1. 同一生成元ポリシー制限 概要 ```text 1. httpの仕様ではなくブラウザで実装されているセキュリティ制限である 2. jsから異なるオリジンのリソースへは、操作要求できるがその結果を受け取れない xhrに対するこれらの制限緩和は、「### 5.2. XHR、プリフライト、CORS」を参照のこと ``` ドメイン、Cookie共有、オリジンの違い ```text ドメイン : urlのドメイン部分の事 Cookie共有: secure属性がないとドメイン(domain属性未指定時、サブドメインは除外される)のみ見る。プロトコルやポートを見ずに送るか送らないかが決まる オリジン : プロトコル + ホスト + ポート ※ Cookieに関しては、「4.5. Cookieとセッション」も参照の事 ``` 具体例 ```text 1. 異なるオリジンへxhr送信できるが、その結果をブラウザが拒否する 2. 異なるオリジンから取得したリソースに対して、js経由で詳細を見れない - img/video/audioタグの内容の詳細 - iframe内に読み込まれたhtml - scriptで発生したエラーの詳細 ``` 付則 ```text - オリジンがnullなウィンドウは、それらを表示したオリジンを継承する - 空のタブ(about uri scheme のblank) - javascriptによるポップアップ(javascript uri scheme) - data:によるjavascript動的生成(data uri scheme) - document.domainを書き換えて上位ドメインにすると、上位オリジン扱いになる(nullポート) ``` ________________________________________ ### 5.2. XHR、プリフライト、CORS シンプルなxhrリクエスト条件(詳細はMDNサイト参照のこと) ```text 1. メソッドがget, post, headいずれか 2. jsで明示的に付加したヘッダーが、以下のみ - Accept - Accept-Language - Content - Content-Type 3. jsで明示的に付加したContent-Typeが、以下のいずれか - application/x-www-form-encoded - multipart/form-data - text/plain 4. XMLHttpRequest.uploadイベントハンドラが登録されていない 5. ReadableStreamを使用していない * 当たり前だが、ブラウザが自動で付加するヘッダーは特に制限がない * 上記を満たさない場合、プリフライトが発生する * Cookie、Authorization、TLSクライアント証明書はデフォルトでは無視されるため、許可が必要 ``` CORSを許可するレスポンスヘッダ ```http Access-Control-Allow-Origin: http://example.jp Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Credentials: true Access-Control-Max-Age: 60 ``` CORSでCookie、Authorization、TLSクライアント証明書を使用する方法 ```text 1. ブラウザ側は、js上のxhr.withCredentialsをtrueにする 2. サーバ側は、Access-Control-Allow-Credentials: trueにする ``` OriginとCORSの注意点 ```text 2022年現在、XHRでOriginによるエラーが発生した際、javascriptでそれを知る手段がない XHRのステータスコードが0を返し詳細を報告しないため、何が原因で起きたのか識別できない なお、現在のブラウザの大半は、開発モードを開いてConsoleを見ると、Originエラーが発生した旨を知らせる 単にブラウザがjavascriptにOriginエラーであることを通知しないだけで、ブラウザとしては何のエラーが起きたかは把握している ``` 歴史的経緯 ```text 1996 : IE3にて、ActiveXコントロール機能が追加され、ブラウザからocx系DLLが読み込める様になった 1999 : IE5にて、ActiveXファミリーの一環としてjsからリクエストする機能であるActiveXObjectクラス(=XMLHttpRequestと同等)が追加された 2001 : GoogleがFireFoxの開発に参加し、XMLHttpRequestという名前で移植した 2005 : Googleマップが登場 2006 : IE7にて、XMLHttpRequestを単独で実装しなおし、ブラウザ標準となった 最初期である1999年のActiveXObjectから、XHRに対する同一生成元ポリシー自体はあった(脆弱性発覚はたびたび発生していたが) ``` Fetch APIとの関係 ```text Fetch APIは、非同期通信標準APIを新しく作り直したもの ・IE11とiOS9.x以外は、対応済み(iOSはreferrerPolicyだけ未対応) ・Promiseベース ・jQuery.ajaxのような直観的な関数で構成 ・将来的には、stream対応もすべてのブラウザで行われる予定 ``` ________________________________________ ## 6. REST ________________________________________ REST 1. サーバ側はステートレスとし、リクエスト内容にステータスを含む 2. キャッシュ可能である 3. プロキシ可能である 4. JSなどをダウンロードさせ、クライアントサイドスクリプトできる 5. URIはリソースであり名詞である 6. リソースに対するuiが取得できる場合、そのuiからそのリソースへの操作が行える 7. レスポンスは、適切なmimeを持つ 8. リソースや関連リソースのアクセスや操作に必要なtocがある 9. HTTPメソッドのget(read), post(new/other), put(create/update), delete(delete)で操作する 10. Get, put, deleteは冪等である 補足 1. HTML formはgetとpostしか発行できない 2. XHRならputとdeleteが使える ________________________________________ ## 7. Nginx vs Apache ________________________________________ Nginx vs Apache – Which is the Best Web Server? https://www.plesk.com/blog/various/nginx-vs-apache-which-is-the-best-web-server/ PHP: PHP を手に入れるには - Manual https://www.php.net/manual/ja/faq.obtaining.php なぜ、 IIS は PHP アプリケーションの実行に不向きとされてきたのか? – monoe's blog https://blogs.msdn.microsoft.com/osamum/2011/04/05/iis-php-1236/ 要約 ```text ・静的コンテンツならnginx ・Phpが関わるならどれでも誤差の範囲 ・Fast-cgi(fcgi)は、processが都度生成破棄されず使いまわされるcgi ・Php_fpm(fcgi形式php)は、php部分のみをpreforkのように動作させる ・Phpのスレッドセーフは、実質不可能(拡張ライブラリが対応していない事が多い) ・Fcgi形式(php-fpm)なら、phpはノンスレッドセーフでok ・言いかえると、mod_phpはpreforkが必須、php-fpmはeventが強く推奨 ・IIS+php_isapi = Apache+mod_php+event = 正常に動作しない ・mod_phpとphp-fpmのphp側の性能差は、fast-cgiによるオーバーヘッド分のみである ・eventの方が遅いはずなのにpreforkの方が遅いのは、高負荷時のApache側の差である ISAPIとphpの歴史はこちらが詳しい PHP on Windows ガイドライン - Microsoft Download Center PHPonWindowsGuideline_chapter0_draft.pdf ``` 詳細 項目 |内容 ---------------------|------------------------------------------------------ 静的コンテンツ |Nginx > Apache event(70%) > Apache prefork(ダメ) 動的コンテンツ |Nginx = Apache event > Apache prefork(80%) mod_php vs php-fpm |php-fpmは、mod_phpより1msのoverheadがある ________________________________________ ## 8. ドメイン ________________________________________ 意味が保証されている主なドメイン ドメイン |取得団体や個人の保証 ---------|-------------------------------------------- .gov |アメリカ合衆国連邦政府 .edu |アメリカの教育機関 .int |国の間の条約に関わる組織 .go.jp |政府機関 .ac.jp |大学等の学校法人など .ed.jp |幼稚園・保育所~高等義務教育機関など .ne.jp |NSPやISPが提供するサービス等(※) .co.jp |株式会社など(※) .or.jp |一般社団法人(営利可)、非営利法人の大半(※) .jpなど |その国籍を持つ。cnなら中国、krなら韓国 (※) メールアドレスはその顧客のユーザが利用しているので、信用できない 意味が保証されていないドメイン(gTLD) gTLD |あるべき用途 --------|----------------------- .org |非営利団体です .site |汎用。特にページを公開する用途向け .website|汎用。特にページを公開する用途向け .online |汎用 .com |汎用 .net |汎用 .biz |ビジネスです .space |開かれた芸術・創作・創造です .cloud |クラウドサービスです .tech |IT系やテクノロジー系です