個別通信プロトコルと脆弱性(第4回)/HTTP前編WWW

## はじめに

* プログラミング初学者向けに
* まず、インターネット上の情報を閲覧・共有するためのWorld Wide Webという概念を学ぶ
    * HTML
    * URL
    * MIME-type

### World Wide Webとは？

WWW（World Wide Web）は、インターネット上で情報を閲覧・共有するためのシステムで、インターネット上の情報を、文書相互の関連付けされた状態をネットワークを覆う蜘蛛の巣(Web)に見立てた名称です。

Tim Berners-Leeが提唱し、HTTP（Hypertext Transfer Protocol）を使ってハイパーテキスト文書をやり取りする仕組みとして設計されました。

> Webサイトを見るという行為は、まるで「ショーウィンドウ越しに商品を見ている」ようなもの。 でもその裏では、あなたの手の動き、視線、クリックが全部記録され、しかも誰かがこっそり店内のレイアウトを変えているかもしれません。
> 
> 
> 今回は、そんな「見えているけど見えていない」HTTP通信の裏側を体験します。

## 4-1. WWWの基本構造（ Why / What / How）

| 項目 | 内容 |
| --- | --- |
| **Why** | インターネット上の情報を関連付け、直感的かつ効率的に閲覧・共有すること |
| **What** | 関連付け情報を持つテキストベースの統一データ形式HTMLと参照形式URLで、サーバとクライアントが文書や画像、スクリプトなどをやり取りする仕組みのこと |
| **How** | リクエスト・レスポンス型のステートレスプロトコル。Webブラウザという専用ソフトウェアを使う |

---
### Webブラウザ登場以前の情報共有

もともとインターネット上で情報をやり取りする手段としては、**電子メール（SMTP/POP3/IMAP）やファイル転送（FTP）** が主流でした。しかし、これらには次のような課題がありました。

- 情報共有の難しさ
    - メールやFTPでは**個々のファイルを直接送受信**する形態であり、
        - 文書が増えると管理が煩雑
        - **リンクを介して文書を関連付ける**手法が存在しなかった
    - 文書のリンクを使って、**直感的に情報を辿れる仕組みが必要**でした。
- フォーマットの統一性がない
    - メールは**テキスト主体**であり、リッチコンテンツ（画像、表、レイアウト）が使いにくい
    - 異なるコンピュータ環境で**フォーマットが崩れる**ことが多かった
- 多様な情報へのアクセスが不便
    - FTPサーバにアクセスし、**ディレクトリ構造を辿って手動でファイルを探す**必要があり、非効率
    - **情報を検索して、すぐにアクセスできる仕組み**が求められた

---
### WWWプロジェクト(1989年): HTMLとURLの発明

HTMLが情報を構造化し、URLで情報を特定し、HTTPで情報を送受信する。

1. HyperText Markup Language( HTML ); 情報構造の統一記述言語
    - 情報を構造化した文書から体裁情報を分離し、Cascading Style Sheet(CSS)が成立(1996年)
2. Uniform Resource Locator(URL); インターネット上の文書に一意性を与える参照形式
    - 識別子として: HTML文書内で他文書との関係を定義できる
    - 参照情報として: URLだけで必要な文書の取得が可能
3. HyperText Transfer Protocol(HTTP); 情報を送受信するためのプロトコル
    - リクエスト・レスポンス型の**ステートレス通信**(次回詳しくやります)

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>HTML文書のひな型</title>
    <!-- 体裁情報はスタイルシートで指定 -->
    <link rel="stylesheet" href="styles.css">    
    <!-- 前のページ -->
    <link rel="prev" href="previous.html">
    <!-- 次のページ -->
    <link rel="next" href="next.html">
    <!-- 目次ページ -->
    <link rel="contents" href="index.html">
    <!-- 著者情報 -->
    <link rel="author" href="about.html">
    
</head>
<body>
  <main>
	  <p>Hello, World!</p>
  </main>
</body>
</html>
```

```css
body > main > p {
  color: red;
}
```
---
- **URLとURN**を包含する上位概念URI
    - URLが「**場所**」
    - URNは「**名前**」
- どちらも**リソースの識別子**であり、状況に応じて使い分ける
- **URLの例**：
    - `https://www.example.com/index.html`
- **URNの例**：
    - `urn:isbn:978-3-16-148410-0`
- **URIの例**（URLとURNの両方を含む）：
    - `http://example.com`
    - `urn:ietf:rfc:2141`

- **URLの構造** 
```
URL
  href
    origin
      protocol
        scheme: http, https, mailto など
        ‘//’
    authority
      userinfo
        username
        `:`
        password
      `@` 
      host
        hostname
          subdomain
            lower
          domain
            second
            toplevel
      `:` 
      port
    resource
      pathname
        dirname
        filename
          basename
          extension
      `?`
      search
      `#`
      hash
```
```text
/*
                                                           href
                   ┌────────────────────────────────────────┴──────────────────────────────────────────────┐
                origin                                                                                     │
      ┌────────────┴──────────────┐                                                                        │
      │                       authority                                                                    │
      │           ┌───────────────┴───────────────────────────┐                                            │
      │           │                                         host                                       resource
      │           │                                ┌──────────┴─────────────────┐             ┌────────────┴───────────┬───────┐
      │           │                             hostname                        │          pathname                    │       │
      │           │                 ┌──────────────┴────────────┐               │      ┌──────┴───────┐                │       │
  protocol     userinfo         subdomain                    domain             │      │           filename            │       │
   ┌─┴──┐     ┌───┴────┐            │                  ┌────────┴───────┐       │      │          ┌───┴─────┐          │       │
scheme  │username password lowerleveldomains secondleveldomain topleveldomain port  dirname    basename extension    search   hash
┌──┴───┐│┌──┴───┐ ┌──┴───┐ ┌──┬─┬─┴─────┬───┐┌───────┴───────┐ ┌──────┴──────┐┌─┴┐┌────┴──────┐┌──┴───┐ ┌───┴───┐ ┌────┴────┐ ┌┴┐
│      │││      │ │      │ │  │ │       │   ││               │ │             ││  ││           ││      │ │       │ │         │ │ │
scheme://username:password@test.abcdedgh.www.secondleveldomain.topleveldomain:1234/hello/world/basename.extension?name=ferret#hash
*/
```
---
## 4-2. 実習1: HTTP通信の基礎

### nodejsによるWebサーバ

以下は、Node.jsで簡単なWebサーバ(HTTP通信をするサーバ)を構築するコードです。

```jsx
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;                   // HTTPステータスコード(1)
  res.setHeader('Content-Type', 'text/plain');  // MIME-type(2)
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
```

### 説明

- **httpモジュールの利用**：Node.jsに組み込まれている標準モジュールを使用
- **createServer関数**：リクエストを受け取り、レスポンスを返すサーバを生成
- **res.statusCode**：HTTPステータスコードを設定（200 OK）
- **res.setHeader**：レスポンスのヘッダーを設定（Content-Type: text/plain）
- **res.end**：クライアントにレスポンスを送信し、接続を終了
- **server.listen**：指定したホスト名とポートでサーバを待機

### 実行方法

1. VSCodeで新しいワークスペースを開く
    - ファイル>名前を付けてワークスペースを保存> httpserverで保存

2. ファイルを編集する
    - ファイル>新規ファイル
3. server.jsで保存する
4. nodejsプログラムの実行; 
    - 実行>デバッグの開始>Node.js
5. 戻ってWebサーバプログラムの停止; 右端の四角を押下
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192607/3c091911-b4c4-4bea-a989-6b4ea6650735.png)
    
6. 先ほどの新たなタブに戻ってリロードすると、アクセスを拒否される
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192607/25df9fb6-f511-423d-99a2-7fb9bab799f7.png)


### サーバサイドデバッグ

(1) ブレークポイントの設定

(2) Webブラウザで3000ポートで開いたページに移動をすると、VSCode上で設定したブレークポイントで停止

(3) ブレークポイントからステップ実行/再開

### クライアントサイドデバッグ

Webブラウザは二種類の通信をしています。Webサーバに要求した **リクエスト通信**と、応答する**レスポンス通信**です。WebブラウザChromeでは　メニューボタン > その他メニュー > デベロッパーツール > Network を選んでおきます。

![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F192607%2F523c7f76-21e5-3f7b-9f8a-66c476b296f3.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=8c8f2a61eefa916c623267cd871b83af)

(1) HTTPリクエストの実体確認; Webブラウザにhttp://localhost:3000と入力 > リクエスト通信(localhost)を選択 > Headersタブを選択 > General > Request URL に注目

![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F192607%2F7ad1e4f0-e9ef-94fa-847b-0cb20ef03603.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=efdd58d0bd245aa64cd4075c0892d2f5)

- Headers > Response Headers > view source
- Headers > Request Headers > view source

このview sourceのアンカーをクリックすると、パース前の、通信内容が確認できます。

(2) HTTPレスポンスの実体確認; Responseタブを選択

![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F192607%2F4a41b051-95fd-be8c-5c23-2e5b411ed22f.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=0ac3da005529c9fc57f7f88baa4eb738)

(3) HTTPステータスコード

- おなじみの200, 404, 500などあります。詳しくはRFCを参照のこと
- Webサーバと接続に成功して、レスポンス通信が受け取れたら200と思ってください
- 接続に成功しても、サーバ側でエラーが生じた場合は400番台や500番台のステータスコードが返ってきます
- 接続に失敗したら、そもそもHTTP通信が始められなかったということです

## 4-3. 実習2: HTTPヘッダContent-TypeとWebブラウザの解釈

server.jsを順次書き換えていくことで、HTTP通信を理解します。

### Content-Type: text/plain

(1) res.write()

server.jsを書き換えたらデバッグ実行中のメニューで回転矢印のリロードボタンを押すと、デバッグプロセスの停止、ファイル保存、再読込、デバッグの開始と、一連の動作をまとめてやってくれます。

```jsx
const http = require('http');

const hostname = '0.0.0.0';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;                         // HTTPステータスコード(1)
  res.setHeader('Content-Type', 'text/plain');  // MIME-type(2)
  res.write('Hello World');
  res.end();
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
```

(2) 繰り返し

```jsx
const http = require('http');

const hostname = '0.0.0.0';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;                         // HTTPステータスコード(1)
  res.setHeader('Content-Type', 'text/plain');  // MIME-type(2)
  for(var i = 0; i < 5; i++)
    res.write('Hello World');
  res.end();
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
```

(3) writeしたからといって直ちに通信しているわけではない(重要)

- res.write行にブレークポイントを仕掛けておく
- http://localhost:3000/でアクセス
- ブレークポイントで止まったら、ステップ実行
- res.end()で初めてレスポンス通信が開始していることがわかるでしょう

### Content-Type: text/html

(1) HTTPヘッダがtext/htmlの場合、HTTPボディの中身はHTML文書です、という意味です

```javascript
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;                         // HTTPステータスコード(1)
  res.setHeader('Content-Type', 'text/html');  // MIME-type(2)
  res.write('<html><body><strong>Hello world</strong></body></html>')
  res.end('');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
```

![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F192607%2Fdf2f2db4-3775-17bb-59aa-cc84364649bf.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=14af2d61c1db2770f088154d1440886e)

WebブラウザはHTML文書として解釈して、Hello worldが強調されていますね。

(2) HTTPヘッダでMIME-typeがtext/plainなのにHTTPボディがHTMLなら...?

```js
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;                         // HTTPステータスコード(1)
  res.setHeader('Content-Type', 'text/plain');  // MIME-type(2)
  res.write('<html><body><strong>Hello world</strong></body></html>')
  res.end('');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
```

![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F192607%2F3e340aa2-a77f-2220-bfc4-7b1e3f1a5514.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=0fd8f3eb5009846825ce6b1ee0b662c5)

HTML文書であっても、text/plainで送るとWebブラウザはHTML文書とは解釈してくれません。HTTPレスポンス通信のsetHeader()で設定されるHTTPヘッダ情報で、Webブラウザ側の挙動が変わることがこれで理解できると思います。

### Content-Typeに指定するMIME-Type

MIME(Multipurpose Internet Mail Extensions) とは、もともと電子メールで異なる形式のデータを扱うために策定されたデータ形式を表現する規格です。現在では、HTTPを含む様々なプロトコルで利用されている。

- **形式** ：`type/subtype` （例：`text/html` , `image/png` , `application/json` ）
- **目的** ：データの種類をクライアントに知らせ、正しく解釈させるため
- **実例** : HTTPレスポンスの`Content-Type`ヘッダで指定された形式に基づき、ブラウザがコンテンツを解釈
    - `text/html` → HTMLとしてレンダリング
    - `text/plain` → プレーンテキストとして表示
- **誤ったMIME-Typeの影響**：
    - HTMLファイルが`text/plain`として送信されると、**ソースコードがそのまま表示**されてしまう。
    - 画像ファイルが`text/html`として送信されると、**ブラウザが解釈できずに表示エラー**が発生する。
- よく使われるMIME-Type一覧
    - 📄 一般的な文書形式
        - `text/plain`：プレーンテキスト（.txt）
        - `text/html`：HTML文書（.html）
        - `text/css`：スタイルシート（.css）
        - `application/pdf`：PDF文書
        - `application/msword`：Microsoft Word（.doc）
        - `application/vnd.openxmlformats-officedocument.wordprocessingml.document`：Word（.docx）
    - 🖼️ 画像ファイル
        - `image/jpeg`：JPEG画像（.jpg, .jpeg）
        - `image/png`：PNG画像
        - `image/gif`：GIF画像
        - `image/svg+xml`：SVGベクター画像
        - `image/webp`：WebP画像
    - 🎥 音声・動画
        - `audio/mpeg`：MP3音声
        - `audio/ogg`：OGG音声
        - `video/mp4`：MP4動画
        - `video/webm`：WebM動画
    - 📦 アプリケーションデータ
        - `application/json`：JSONデータ
        - `application/javascript`：JavaScriptコード
        - `application/zip`：ZIPアーカイブ
        - `application/x-www-form-urlencoded`：フォーム送信のエンコード形式
        - `multipart/form-data`：ファイルアップロード時のフォーム形式
    - その他
        - `application/octet-stream`：汎用バイナリデータ
        - `application/xml`：XML文書

---
## 前編のまとめ

### HTTP通信

- テキストベースのプロトコル
- リクエスト・レスポンス型（クライアントがリクエストし、サーバが応答）
- ステートレス（個々の通信に状態を持たない）

### WWW(Web)の正体

- HTML、URL、MIME-typeの統合的理解
    
    1. **HTML** ：文書や情報を**構造化**するためのマークアップ言語
    
    2. **URL** ：インターネット上の**リソースを特定**するための住所
    
    3. **MIME-type** ：リソースが**どの形式であるか**を明示するための識別子
    
- HTTPにおける役割分担
    - **URLでリソースにアクセス**
    - **HTTPレスポンスヘッダーでMIME-typeを指定**
    - **ブラウザがHTMLや画像、CSSとして解釈**

### 次回予告

HTTP通信の続編では、**フォーム入力とパラメータ解析** 、**セッション管理のためのCookie** に注目し、HTTPの持つ脆弱性である**セッションハイジャック** について深掘りしていきます。

## おわりに

* 本稿では、HTTPの脆弱性の前段として最低限必要なキーワードを集めてWWWの話とした
* 中学生でもわかるよう、比喩の後、説明し、具体例とコンピュータ上での操作を念頭に構成した
    * 実習内容は、拙稿[JavaScriptで学ぶHTTP通信](https://qiita.com/Gatz/items/55325aaa495a1f62c177)を再利用した
* ステートレス通信やセッションの概念は、分量が多くなったため、稿を改めることにした