Web開発者のためのIPv6
Web開発者がIPv6について知る必要があること:括弧付きURL、データベースストレージ、ソケットプログラミング、避けるべき一般的なバグ。
Web開発者のためのIPv6#
「後で問題になる」と思ってIPv6を無視してきた場合、後は今です。モバイルネットワークはIPv6ファーストで実行されています。NAT64の背後にいる一部のユーザーは、IPv6経由でのみ到達できます。開発マシンのlocalhostでさえ、おそらく127.0.0.1の前に::1に解決されます。
このガイドは、実際に知る必要があることをカバーしています:URLでのIPv6の処理、データベースへのアドレスの保存、ソケットプログラミング、注意しないとかまれるバグ。
TL;DR - 要点まとめ
重要ポイント:
- URLではIPv6アドレスを角括弧で囲む:
http://[2001:db8::1]:8080 - PostgreSQLは
INET型を使用、MySQLはVARBINARY(16)またはVARCHAR(45)を使用 - 比較前にアドレスを正規化 - IPv6には複数の有効な表現があります
- デュアルスタックソケットには
::にバインド(IPv4とIPv6の両方を受け入れる) - 名前解決には
getaddrinfo()を使用 - IPv4アドレスをハードコードしないでください - レート制限はIPv6サブネット(/64)を使用 - プライバシー拡張によりアドレスが変更されます
- 正規表現でIPv6を検証しないでください - 適切な解析ライブラリを使用してください
URLでのIPv6#
IPv6アドレスにはコロンが含まれており、URLのポート区切り文字と競合します。解決策は角括弧表記です:
http://[2001:db8::1]:8080/api/users
https://[2606:2800:220:1:248:1893:25c8:1946]/括弧がないと、パーサーはアドレスが終わりポートが始まる場所を判断できません。これは以下に適用されます:
- HTTP/HTTPS URL
- WebSocket接続(
ws://[::1]:3000) - データベース接続文字列
- 任意のURIスキーム
プログラムでURLを生成する場合、IPv6アドレスを括弧で囲みます。解析する場合、検証または保存の前にそれらを削除します。
データベースストレージ#
クラシックな間違いは、IPアドレスにVARCHAR(15)を使用することです。これはIPv4(最大15文字:255.255.255.255)に適合しますが、IPv6には適合しません。
PostgreSQLにはIPv4とIPv6の両方を処理するネイティブINET型があります:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
ip_address INET NOT NULL
);
-- 自動検証と正規化
INSERT INTO users (ip_address) VALUES ('2001:db8::1');
INSERT INTO users (ip_address) VALUES ('192.0.2.1');INET型は、アドレスを効率的に保存し、挿入時に検証し、<<および>>演算子を使用したサブネットマッチングなどのネットワーク操作をサポートします。
MySQLにはネイティブ型がないため、次のいずれかを使用します:
VARBINARY(16)- バイナリ表現を保存(IPv6の場合は16バイト、IPv4の場合は4)VARCHAR(45)- 文字列表現を保存(展開されたIPv6の最大長)
バイナリストレージはより効率的ですが、変換関数が必要です:
-- 保存
INSERT INTO users (ip_address) VALUES (INET6_ATON('2001:db8::1'));
-- 取得
SELECT INET6_NTOA(ip_address) FROM users;**正規化が重要です。**IPv6アドレスには複数の有効な表現があります:
2001:0db8:0000:0000:0000:0000:0000:00012001:db8::1(圧縮)2001:db8:0:0:0:0:0:1(部分的に圧縮)
比較またはインデックス作成の前に常に正規化します。ほとんどのライブラリには正規形式関数があります。
ソケットプログラミング#
ソケットAPIはIPv4とIPv6を別々のアドレスファミリとして扱います。最新のアプリケーションは両方をサポートする必要があります。
Node.jsはデフォルトでデュアルスタックソケットを使用します:
const http = require('http');
// ::(すべてのIPv6アドレス)にバインドし、マッピング経由でIPv4を受け入れる
const server = http.createServer((req, res) => {
res.end(`Your IP: ${req.socket.remoteAddress}`);
});
server.listen(3000, '::');::アドレスは0.0.0.0のIPv6版であり、すべてのインターフェイスで接続を受け入れます。ほとんどのシステムは、IPv4マップIPv6アドレス(::ffff:192.0.2.1)をサポートしており、単一のIPv6ソケットで両方のプロトコルを処理できます。
Pythonは明示的なデュアルスタック処理が必要です:
import socket
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
sock.bind(('::', 8080))
sock.listen(5)IPV6_V6ONLYフラグは、ソケットがIPv6のみを受け入れるか、IPv4とIPv6の両方を受け入れるかを制御します。
Goはデフォルトでデュアルスタックを作成します:
listener, err := net.Listen("tcp", ":8080")
// 自動的にIPv4とIPv6の両方でリッスンクライアント接続の場合、手動でアドレスを解決する代わりに、常にgetaddrinfo()(または言語の同等物)を使用します。IPv4、IPv6、デュアルスタックシナリオを正しく処理します:
import socket
# これをしないでください
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('example.com', 80))
# これを行ってください - IPv4とIPv6の両方で機能します
addr_info = socket.getaddrinfo('example.com', 80, socket.AF_UNSPEC, socket.SOCK_STREAM)
sock = socket.socket(addr_info[0][0], addr_info[0][1])
sock.connect(addr_info[0][4])ローカルテスト#
ほとんどのシステムはlocalhostを::1(IPv6)と127.0.0.1(IPv4)の両方に解決します。/etc/hostsを確認:
127.0.0.1 localhost
::1 localhostアプリケーションが127.0.0.1のみにバインドしている場合、IPv6接続を受け入れません。デュアルスタックの場合は::にバインドするか、両方のアドレスに明示的にバインドします。
IPv6のみの動作をテストするには、ローカルマシンでIPv4を無効にするか、HTTPクライアントで[::1]を明示的に使用します:
curl http://[::1]:3000/現実的なテストには、IPv6のみのテストネットワークを使用します。多くのクラウドプロバイダーは、NAT64/DNS64などのエッジケースを処理することを強制するIPv6のみのインスタンスを提供します。
CDNとオリジンサーバー#
ほとんどのCDN(Cloudflare、Fastly、AWS CloudFront)はデフォルトでIPv6をサポートします。クライアントにIPv6経由でコンテンツを提供し、必要に応じてオリジンのIPv4に変換します。
キャッチ:API を直接公開する場合(CDNをバイパス)、オリジンサーバーはIPv6をサポートする必要があります。DNSにAAAAレコードがあり、ファイアウォールがIPv6トラフィックを許可することを確認してください。
一般的なバグ#
**正規表現検証。**IPv6正規表現パターンは非常に複雑で、通常は間違っています:
// これをしないでください
const ipv6Regex = /^([0-9a-f]{1,4}:){7}[0-9a-f]{1,4}$/i;
// ::圧縮、IPv4マップアドレス、ゾーンIDで失敗...代わりに適切な解析ライブラリを使用してください。すべての主要言語には、IPv6を正しく処理するものがあります。検証には、IPv6バリデーターツールを使用してください。
**ハードコーディングされたIPv4アドレス。**コードベースで次のようなパターンを検索:
const API_SERVER = 'http://192.168.1.100:3000'; // IPv6では機能しません代わりにホスト名を使用するか、両方のアドレスファミリをサポートします。
**IPベースのレート制限。**プライバシー拡張により、IPv6アドレスは頻繁に変更されます。正確なアドレスでレート制限すると失敗します。代わりに/64サブネットで制限します:
// 悪い
const key = `rate:${ipAddress}`;
// IPv6により良い
const key = `rate:${ipv6ToSubnet64(ipAddress)}`;**IPv6サポートのないライブラリ。**古いHTTPクライアント、データベースドライバー、ネットワークライブラリはIPv6を処理しない場合があります。実際のIPv6アドレスでテストしてください。localhostだけではありません。ライブラリが失敗した場合は、更新または代替を確認してください。
**URL解析のエッジケース。**ルーター/フレームワークが括弧付きIPv6アドレスを処理することを確認:
GET http://[2001:db8::1]:8080/api/users
Host: [2001:db8::1]:8080一部のパーサーは、Hostヘッダーに括弧を誤って含めたり、アドレスを正しく抽出できなかったりします。
チェックリスト#
アプリケーションがIPv6対応の場合:
- データベース列が45文字の文字列を保存できるか、ネイティブIPタイプを使用
- サーバーが
::にバインドするか、IPv4とIPv6の両方で明示的にリッスン - クライアントコードが名前解決に
getaddrinfo()または同等物を使用 - IP検証が正規表現に依存しない
- 設定にハードコーディングされたIPv4アドレスがない
- レート制限とジオロケーションがIPv6サブネットを処理
- URL解析が括弧表記を処理
IPv6はもはやオプションではありません。最初から組み込むと、後で痛みを伴う移行を避けることができます。
関連記事#
- IPv6の基礎:その概要と重要性 - 開発者が習得する必要があるIPv6の核心概念を学びます。
- IPv6アドレスタイプ:グローバル、リンクローカル、マルチキャストの解説 - アプリケーションが処理する必要があるさまざまなアドレスタイプを深く理解します。
実際に試してみましょう
IPv6バリデーターおよびPingツールを使用して、アプリケーションのIPv6サポートをテストします。