From 13b159c6dd499b5c67a110e02780d9e741f0ecdb Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 24 Feb 2025 06:27:07 +0900 Subject: feat(blog/nuldoc): do not insert whitespace to linebreaks between Japanese sentences --- .../phperkaigi-2023-tokens-q1/index.html | 74 +++++++++++----------- 1 file changed, 37 insertions(+), 37 deletions(-) (limited to 'vhosts/blog/public/posts/2025-01-08') diff --git a/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html b/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html index c76dd5fa..16bf6730 100644 --- a/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html +++ b/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html @@ -77,20 +77,20 @@ NOTE
- これは PHPerKaigi 2023 の記事です。今は 2025 年ですが、PHPerKaigi 2023 の記事です。 + これは PHPerKaigi 2023 の記事です。今は 2025 年ですが、PHPerKaigi 2023 の記事です。

- 2023-03-23 から 2023-03-25 にかけて開催された PHPerKaigi 2023 では、PHPer チャレンジという企画がおこなわれた。 PHPer チャレンジとは、スポンサーのパンフレットやカンファレンス会場などから「#」記号で始まる文字列を集め、景品などを得るという企画である。 この文字列は「PHPer トークン」と呼ばれている。弊社デジタルサーカス株式会社からは、トークン問題という形で、PHP に関する問題を解くと PHPer トークンが得られるようになっている問題を出題した。 + 2023-03-23 から 2023-03-25 にかけて開催された PHPerKaigi 2023 では、PHPer チャレンジという企画がおこなわれた。PHPer チャレンジとは、スポンサーのパンフレットやカンファレンス会場などから「#」記号で始まる文字列を集め、景品などを得るという企画である。この文字列は「PHPer トークン」と呼ばれている。弊社デジタルサーカス株式会社からは、トークン問題という形で、PHP に関する問題を解くと PHPer トークンが得られるようになっている問題を出題した。

- PHPerKaigi 2023 の参加レポ でも書いたとおり、この年のトークン問題は「昨年の PHPerKaigi 2022 が終わった段階から作り始め、約半年かけて制作」された。 PHPerKaigi 当日も PHPer チャレンジ解説セッション という形で解説の機会を頂いたのだが、せっかく時間をかけて作題したので記事の形でも残しておこうと思う。 + PHPerKaigi 2023 の参加レポ でも書いたとおり、この年のトークン問題は「昨年の PHPerKaigi 2022 が終わった段階から作り始め、約半年かけて制作」された。PHPerKaigi 当日も PHPer チャレンジ解説セッション という形で解説の機会を頂いたのだが、せっかく時間をかけて作題したので記事の形でも残しておこうと思う。

- この記事では、全5問ある中の第1問について解説する。他の問題については以下のリンクを参照のこと。 + この記事では、全5問ある中の第1問について解説する。他の問題については以下のリンクを参照のこと。

    @@ -116,14 +116,14 @@

- それぞれの問題はこちらの GitHub リポジトリ (nsfisis/PHPerKaigi2023-tokens) からも閲覧できる。 + それぞれの問題はこちらの GitHub リポジトリ (nsfisis/PHPerKaigi2023-tokens) からも閲覧できる。

Q1: An Art of Computer Programming

- 第1問『An Art of Computer Programming』はこちら。 + 第1問『An Art of Computer Programming』はこちら。

全体がQRコードになっており、中央には小さな文字で「Password is one of the PHPer tokens.」と書かれている @@ -133,13 +133,13 @@

解き方

- まずはトークンを得る方法を解説抜きで説明する。次のように実行する。 + まずはトークンを得る方法を解説抜きで説明する。次のように実行する。

$ echo "#iwillblog" | php Q1.png >/dev/null

- 無事に実行できていれば「#ModernPHPisStaticallyTypedLanguage」というトークンが得られる。 + 無事に実行できていれば「#ModernPHPisStaticallyTypedLanguage」というトークンが得られる。

@@ -148,50 +148,50 @@

画像として解釈する

- まずは素直に画像として見てみよう。 全体は QR コードになっている。適当な QR コードリーダで読み込むと、次のようなテキストが表示されるはずだ。 + まずは素直に画像として見てみよう。全体は QR コードになっている。適当な QR コードリーダで読み込むと、次のようなテキストが表示されるはずだ。

Guess password. $ echo "password" | php Q1.png >/dev/null

- メッセージは、この画像の実行方法とこの問題でやるべきこと (パスワードの推測) を示している。 + メッセージは、この画像の実行方法とこの問題でやるべきこと (パスワードの推測) を示している。

- 次に QR コードの中央部に目を向けると、小さな文字で「Password is one of the PHPer tokens.」と書かれているのがわかる。 他の PHPer トークンの中から適切な1つを見つけだし、「パスワード」として渡すことで答えとなる PHPer トークンが得られるというわけだ。 + 次に QR コードの中央部に目を向けると、小さな文字で「Password is one of the PHPer tokens.」と書かれているのがわかる。他の PHPer トークンの中から適切な1つを見つけだし、「パスワード」として渡すことで答えとなる PHPer トークンが得られるというわけだ。

パスワード

- 不正なパスワードを使って実行してみると、次のようなエラーメッセージが表示される。 + 不正なパスワードを使って実行してみると、次のようなエラーメッセージが表示される。

$ echo "foo" | php Q1.png >/dev/null
 401 Unauthorized

- すでに「解き方」の節で示したように、パスワードである PHPer トークンは「#iwillblog」である。これを与えて実行すると正解のトークンが得られる。 + すでに「解き方」の節で示したように、パスワードである PHPer トークンは「#iwillblog」である。これを与えて実行すると正解のトークンが得られる。

- このパスワードの選択にはとある事情がある。 今回の問題の作問は前回の開催 (PHPerKaigi 2022) 直後からスタートしており、この時点では PHPerKaigi 2023 で登録される PHPer トークンにどのようなものがあるかはまったくわからない状態であった。 作問作業を早期に終わらせるには、次回開催でも確実に使われるであろう定番のトークンを予測して選ぶ必要があったのだ。 かくして、私が知る限り毎回登場しているトークンである「#iwillblog」に白羽の矢が立てられた。 + このパスワードの選択にはとある事情がある。今回の問題の作問は前回の開催 (PHPerKaigi 2022) 直後からスタートしており、この時点では PHPerKaigi 2023 で登録される PHPer トークンにどのようなものがあるかはまったくわからない状態であった。作問作業を早期に終わらせるには、次回開催でも確実に使われるであろう定番のトークンを予測して選ぶ必要があったのだ。かくして、私が知る限り毎回登場しているトークンである「#iwillblog」に白羽の矢が立てられた。

- なお、解いてくださった方の中には、先頭の「#」を入力せずに何度も試してしまい答えが得られずじまいになった方もいらっしゃるようだった。 問題を置いていたリポジトリにヒントとしてパスワードのトークンが「i」で始まると書いていたのだが、これが意図せずミスリードになってしまった。 これは私のミスである。 + なお、解いてくださった方の中には、先頭の「#」を入力せずに何度も試してしまい答えが得られずじまいになった方もいらっしゃるようだった。問題を置いていたリポジトリにヒントとしてパスワードのトークンが「i」で始まると書いていたのだが、これが意図せずミスリードになってしまった。これは私のミスである。

PNG ステガノグラフィ

- QR コードも言っているように、このファイルは PNG 画像であるにもかかわらず PHP で実行することができる。なぜこのようなことが可能なのか。 + QR コードも言っているように、このファイルは PNG 画像であるにもかかわらず PHP で実行することができる。なぜこのようなことが可能なのか。

- PNG 画像のフォーマットは、次のようになっている。 + PNG 画像のフォーマットは、次のようになっている。

    @@ -213,15 +213,15 @@

- PNG フッタの後ろにあるデータは、画像ビューアには解釈されず、画像の表示には影響を与えない。したがって、PNG フッタの後ろには任意のデータを埋め込むことができる。 + PNG フッタの後ろにあるデータは、画像ビューアには解釈されず、画像の表示には影響を与えない。したがって、PNG フッタの後ろには任意のデータを埋め込むことができる。

- さて、PHP には、PHP プログラムの始まりを示すための PHP タグ (<?php または <?) がある。 CLI で実行する場合、PHP タグよりも前にあるデータは標準出力へそのまま出力される。 + さて、PHP には、PHP プログラムの始まりを示すための PHP タグ (<?php または <?) がある。CLI で実行する場合、PHP タグよりも前にあるデータは標準出力へそのまま出力される。

- この画像ファイルは次のような構造になっていた。 + この画像ファイルは次のような構造になっていた。

    @@ -251,11 +251,11 @@

- PNG ファイルとして読むときは PNG フッタ以降は無視され、PHP スクリプトとして読むときは PHP タグ以前が無視されるという仕掛けである。 + PNG ファイルとして読むときは PNG フッタ以降は無視され、PHP スクリプトとして読むときは PHP タグ以前が無視されるという仕掛けである。

- strings コマンドを使うと、隠されたデータを簡単に閲覧できる。 + strings コマンドを使うと、隠されたデータを簡単に閲覧できる。

IHDR
@@ -271,24 +271,24 @@ $h = $b[24]+2;
 // (以下略)

- IHDRIEND が PNG 画像の一部で、<?php からが実際のプログラムになっている。 もちろんこれを PHP プログラムとして動かすと、PHP タグより前にある PNG 画像としてのデータはそのまま標準出力へと出力されてしまう。 それを防ぐため、QR コードを読み込んだときの実行方法 + IHDRIEND が PNG 画像の一部で、<?php からが実際のプログラムになっている。もちろんこれを PHP プログラムとして動かすと、PHP タグより前にある PNG 画像としてのデータはそのまま標準出力へと出力されてしまう。それを防ぐため、QR コードを読み込んだときの実行方法

Guess password. $ echo "password" | php Q1.png >/dev/null

- には標準出力を捨てるよう >/dev/null と指定されている。 + には標準出力を捨てるよう >/dev/null と指定されている。

- なお、このように PNG 画像などに本来のデータとは異なる別のデータを隠すことを「ステガノグラフィ」(Wikipedia「ステガノグラフィー」) と呼ぶ。 + なお、このように PNG 画像などに本来のデータとは異なる別のデータを隠すことを「ステガノグラフィ」(Wikipedia「ステガノグラフィー」) と呼ぶ。

実行される PHP プログラム

- 画像の正体がわかったところで、画像に隠されていた PHP プログラムについて見ていこう。 先ほどは一部しか記載しなかったので、全体を載せる。 なお、ある程度ゴルフしながら書いたので、空白こそ残しているものの可読性は非常に低いことと思う。 + 画像の正体がわかったところで、画像に隠されていた PHP プログラムについて見ていこう。先ほどは一部しか記載しなかったので、全体を載せる。なお、ある程度ゴルフしながら書いたので、空白こそ残しているものの可読性は非常に低いことと思う。

<?php
@@ -394,7 +394,7 @@ $h = $b[24]+2;
 fwrite(STDERR, str_replace('403 Forbidden', '401 Unauthorized', $o));

- これは一体なんなのか。ずばり、難解プログラミング言語の一つ Piet のインタプリタである。 Piet はピエト・モンドリアン (『赤・青・黄のコンポジション』などで知られる抽象画家) の作品にインスピレーションを受けて作られた、画像をソースコードとするプログラミング言語である。 インタプリタは画像の各ピクセルの上を進みながら、色等に応じて特定の処理をおこなっていく。 ここでは詳しい言語仕様については解説しないので、気になる方は Wikipedia の記事「Piet」 などを参照してほしい。 + これは一体なんなのか。ずばり、難解プログラミング言語の一つ Piet のインタプリタである。Piet はピエト・モンドリアン (『赤・青・黄のコンポジション』などで知られる抽象画家) の作品にインスピレーションを受けて作られた、画像をソースコードとするプログラミング言語である。インタプリタは画像の各ピクセルの上を進みながら、色等に応じて特定の処理をおこなっていく。ここでは詳しい言語仕様については解説しないので、気になる方は Wikipedia の記事「Piet」 などを参照してほしい。

@@ -404,18 +404,18 @@ $h = $b[24]+2;

$b = unpack('C*', file_get_contents(__FILE__));

- で __FILE__ つまりこの画像ファイルを読み込んでいる。 先ほど Piet は画像をソースコードにしていると説明した。 そう、今回の問題の画像ファイル Q1.png は、PHP 製 Piet インタプリタであると同時に、Piet のソースコード画像でもあるのだ。 QR コード中央のカラフルな部分が Piet の命令になっている。 + で __FILE__ つまりこの画像ファイルを読み込んでいる。先ほど Piet は画像をソースコードにしていると説明した。そう、今回の問題の画像ファイル Q1.png は、PHP 製 Piet インタプリタであると同時に、Piet のソースコード画像でもあるのだ。QR コード中央のカラフルな部分が Piet の命令になっている。

Piet のソースコード

- さて、Piet でどのようなコードが書かれて (いや、描かれて) いるのかを解説したいところだが、今の私にはできそうにない。 というのも、すでに述べたように Piet は「難解プログラミング言語」である。 およそ人が描いたり読んだりするようには作られていない。性質としては、パズルに近い代物である。 + さて、Piet でどのようなコードが書かれて (いや、描かれて) いるのかを解説したいところだが、今の私にはできそうにない。というのも、すでに述べたように Piet は「難解プログラミング言語」である。およそ人が描いたり読んだりするようには作られていない。性質としては、パズルに近い代物である。

- というわけで、ここではあらましを説明するだけでご容赦いただきたい。 それぞれの部分はおおよそ次のようなことをやっている (再検証・再読解はしていないので大嘘かもしれない)。 + というわけで、ここではあらましを説明するだけでご容赦いただきたい。それぞれの部分はおおよそ次のようなことをやっている (再検証・再読解はしていないので大嘘かもしれない)。

    @@ -457,7 +457,7 @@ $h = $b[24]+2;

- ところで、先ほど掲載した Piet のインタプリタのソースコード末尾には次のような箇所がある。 + ところで、先ほど掲載した Piet のインタプリタのソースコード末尾には次のような箇所がある。

// The original Piet image is wrong: it outputs 403 error for invalid passwords.
@@ -467,26 +467,26 @@ $h = $b[24]+2;
 fwrite(STDERR, str_replace('403 Forbidden', '401 Unauthorized', $o));

- コメントにも書かれているが、この Piet のソースコード画像には誤りがあった。 本来 HTTP のステータスコードを真似るのなら、認証の失敗には 401 を返さなければならない。 しかし、Piet のソースは 403 を返すように書いてしまっていた。 そのことに私が気付いたのは PHPerKaigi 2023 が開催されるひと月前で、その時点で私はこの Piet のソースコードを (ちょうどこの記事でそうなっているのと同じように) 読解できなくなっていた。 さらに悪いことに、正しいメッセージ「401 Unauthorized」は元の「403 Forbidden」よりも3文字長い。 3文字出力が長くなるということは、それだけ Piet で塗るべきピクセルが増えることを意味する。 もはや3文字追加で出力するだけの余白はこの画像に残されていなかった (と思う。腕ききの Piet プログラマならできるかもしれないので挑戦してみてほしい)。 + コメントにも書かれているが、この Piet のソースコード画像には誤りがあった。本来 HTTP のステータスコードを真似るのなら、認証の失敗には 401 を返さなければならない。しかし、Piet のソースは 403 を返すように書いてしまっていた。そのことに私が気付いたのは PHPerKaigi 2023 が開催されるひと月前で、その時点で私はこの Piet のソースコードを (ちょうどこの記事でそうなっているのと同じように) 読解できなくなっていた。さらに悪いことに、正しいメッセージ「401 Unauthorized」は元の「403 Forbidden」よりも3文字長い。3文字出力が長くなるということは、それだけ Piet で塗るべきピクセルが増えることを意味する。もはや3文字追加で出力するだけの余白はこの画像に残されていなかった (と思う。腕ききの Piet プログラマならできるかもしれないので挑戦してみてほしい)。

- これを解決するために私が選んだのは、インタプリタを改造し、本来のメッセージとは異なるメッセージを無理やり出力させて帳尻を合わせることだった。 そういうわけでこの Piet インタプリタは完全な Piet インタプリタではなく、「403 Forbidden」というテキストを絶対に出力できない。 + これを解決するために私が選んだのは、インタプリタを改造し、本来のメッセージとは異なるメッセージを無理やり出力させて帳尻を合わせることだった。そういうわけでこの Piet インタプリタは完全な Piet インタプリタではなく、「403 Forbidden」というテキストを絶対に出力できない。

その他小ネタ

- ここまでで問題の核心部分は説明し終えたので、ここからは残った小ネタを紹介しておく。 + ここまでで問題の核心部分は説明し終えたので、ここからは残った小ネタを紹介しておく。

- この問題のタイトル『An Art of Computer Programming』は、ドナルド・クヌースの『The Art of Computer Programming』をパロディしたものである。 + この問題のタイトル『An Art of Computer Programming』は、ドナルド・クヌースの『The Art of Computer Programming』をパロディしたものである。

- この問題で得られるトークン「#ModernPHPisStaticallyTypedLanguage」は特に元ネタがあるわけではない。当然のような顔で嘘を主張したかったのでこうなった。 + この問題で得られるトークン「#ModernPHPisStaticallyTypedLanguage」は特に元ネタがあるわけではない。当然のような顔で嘘を主張したかったのでこうなった。

@@ -494,7 +494,7 @@ $h = $b[24]+2;

おわりに

- この問題の自己評価はこちら。 問題の出題順はおおよそ作成した順になっているのだが、そのせいで難易度高めの問題が1問目に配置されてしまった。 これは反省点の一つである。 + この問題の自己評価はこちら。問題の出題順はおおよそ作成した順になっているのだが、そのせいで難易度高めの問題が1問目に配置されてしまった。これは反省点の一つである。