はじめに
- プログラミング初学者向けに
- まず、インターネット上の情報を閲覧・共有するための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では個々のファイルを直接送受信する形態であり、
- フォーマットの統一性がない
- メールはテキスト主体であり、リッチコンテンツ(画像、表、レイアウト)が使いにくい
- 異なるコンピュータ環境でフォーマットが崩れることが多かった
- 多様な情報へのアクセスが不便
- FTPサーバにアクセスし、ディレクトリ構造を辿って手動でファイルを探す必要があり、非効率
- 情報を検索して、すぐにアクセスできる仕組みが求められた
WWWプロジェクト(1989年): HTMLとURLの発明
HTMLが情報を構造化し、URLで情報を特定し、HTTPで情報を送受信する。
- HyperText Markup Language( HTML ); 情報構造の統一記述言語
- 情報を構造化した文書から体裁情報を分離し、Cascading Style Sheet(CSS)が成立(1996年)
- Uniform Resource Locator(URL); インターネット上の文書に一意性を与える参照形式
- 識別子として: HTML文書内で他文書との関係を定義できる
- 参照情報として: URLだけで必要な文書の取得が可能
- HyperText Transfer Protocol(HTTP); 情報を送受信するためのプロトコル
- リクエスト・レスポンス型のステートレス通信(次回詳しくやります)
<!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>
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.comurn: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
/*
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通信をするサーバ)を構築するコードです。
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:指定したホスト名とポートでサーバを待機
実行方法
-
VSCodeで新しいワークスペースを開く
- ファイル>名前を付けてワークスペースを保存> httpserverで保存
-
ファイルを編集する
- ファイル>新規ファイル
-
server.jsで保存する
-
nodejsプログラムの実行;
- 実行>デバッグの開始>Node.js
-
戻ってWebサーバプログラムの停止; 右端の四角を押下
-
先ほどの新たなタブに戻ってリロードすると、アクセスを拒否される
サーバサイドデバッグ
(1) ブレークポイントの設定
(2) Webブラウザで3000ポートで開いたページに移動をすると、VSCode上で設定したブレークポイントで停止
(3) ブレークポイントからステップ実行/再開
クライアントサイドデバッグ
Webブラウザは二種類の通信をしています。Webサーバに要求した リクエスト通信と、応答するレスポンス通信です。WebブラウザChromeでは メニューボタン > その他メニュー > デベロッパーツール > Network を選んでおきます。
(1) HTTPリクエストの実体確認; Webブラウザにhttp://localhost:3000と入力 > リクエスト通信(localhost)を選択 > Headersタブを選択 > General > Request URL に注目
- Headers > Response Headers > view source
- Headers > Request Headers > view source
このview sourceのアンカーをクリックすると、パース前の、通信内容が確認できます。
(2) HTTPレスポンスの実体確認; Responseタブを選択
(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を書き換えたらデバッグ実行中のメニューで回転矢印のリロードボタンを押すと、デバッグプロセスの停止、ファイル保存、再読込、デバッグの開始と、一連の動作をまとめてやってくれます。
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) 繰り返し
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文書です、という意味です
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}/`);
});
WebブラウザはHTML文書として解釈して、Hello worldが強調されていますね。
(2) HTTPヘッダでMIME-typeがtext/plainなのにHTTPボディがHTMLなら...?
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}/`);
});
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として送信されると、ブラウザが解釈できずに表示エラーが発生する。
- 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の統合的理解
-
HTML :文書や情報を構造化するためのマークアップ言語
-
URL :インターネット上のリソースを特定するための住所
-
MIME-type :リソースがどの形式であるかを明示するための識別子
-
-
HTTPにおける役割分担
- URLでリソースにアクセス
- HTTPレスポンスヘッダーでMIME-typeを指定
- ブラウザがHTMLや画像、CSSとして解釈
次回予告
HTTP通信の続編では、フォーム入力とパラメータ解析 、セッション管理のためのCookie に注目し、HTTPの持つ脆弱性であるセッションハイジャック について深掘りしていきます。
おわりに
- 本稿では、HTTPの脆弱性の前段として最低限必要なキーワードを集めてWWWの話とした
- 中学生でもわかるよう、比喩の後、説明し、具体例とコンピュータ上での操作を念頭に構成した
- 実習内容は、拙稿JavaScriptで学ぶHTTP通信を再利用した
- ステートレス通信やセッションの概念は、分量が多くなったため、稿を改めることにした






