【PHP】正しい連絡フォームの作り方(クライアント側をぜったいに信用するな!!)

このコミットが含まれているのは:
守矢諏訪子 2023-08-04 14:49:38 +09:00
コミット a3583c9dd7
25個のファイルの変更655行の追加514行の削除

バイナリ
ass/anzenform1.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 60 KiB

バイナリ
ass/anzenform2.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 25 KiB

バイナリ
ass/anzenform3.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 16 KiB

バイナリ
ass/anzenform4.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 24 KiB

バイナリ
ass/anzenform5.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 122 KiB

バイナリ
ass/anzenform6.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 88 KiB

バイナリ
ass/fuanform1.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 220 KiB

バイナリ
ass/fuanform2.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 233 KiB

バイナリ
ass/fuanform3.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 258 KiB

バイナリ
ass/fuanform4.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 105 KiB

ファイルの表示

@ -3,13 +3,329 @@
<id>gemini://technicalsuwako.moe</id>
<title>テクニカル諏訪子</title>
<updated>2023-07-17T02:40:00Z</updated>
<updated>2023-08-04T00:00:00Z</updated>
<author><name>テクニカル諏訪子</name></author>
<link href="gemini://technicalsuwako.moe" rel="alternate"></link>
<entry>
<id>blog/fix-broken-contact-form.gmi</id>
<author><name>凛</name></author>
<title type="html">【ウェブ開発】正しい連絡フォームの作り方(クライアント側をぜったいに信用するな!!)</title>
<published>2023-08-04T00:00:00Z</published>
<category term="blog"></category>
<category term="jp"></category>
<category term="プログラミング"></category>
<category term="php"></category>
<category term="ウエブ開発"></category>
<category term="html"></category>
<category term="javascript"></category>
<link href="gemini://technicalsuwako.moe/blog/fix-broken-contact-form.gmi" rel="alternate"></link>
<content type="html">
&lt;h2 id=&#34;heading&#34;&gt;問題&lt;/h2&gt;
&lt;p&gt;現在の「モダン」ウェブ開発で、連絡フォームはJavascriptで制御されていますが、これは大きなリスクがあります。&lt;br /&gt;
その理由について、直ぐに説明します。&lt;/p&gt;
&lt;p&gt;以下のスクショをご覧いただいたら、何が問題は何だと思いますか?&lt;br /&gt;
&lt;a href=&#34;https://ass.technicalsuwako.moe/fuanform1.png&#34;&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/fuanform1.png&#34; alt=&#34;&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;正解は:送信ボタンは&lt;code lang=&#34;&#34;&gt;&amp;lt;form&amp;gt;&lt;/code&gt;タグの外にある事です。&lt;br /&gt;
これでは、Javascriptを無効にした場合、送信ボタンをクリックする事が出来ません。&lt;/p&gt;
&lt;p&gt;このフォームを送信する為には、この送信ボタンをフォーム内に移動し、&lt;code lang=&#34;&#34;&gt;type=&amp;quot;button&amp;quot;&lt;/code&gt;を&lt;code lang=&#34;&#34;&gt;type=&amp;quot;submit&amp;quot;&lt;/code&gt;に変更する事で、Javascriptなしでもフォームを送信する事が可能になります。&lt;br /&gt;
そんな感じ:&lt;br /&gt;
&lt;a href=&#34;https://ass.technicalsuwako.moe/fuanform2.png&#34;&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/fuanform2.png&#34; alt=&#34;&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;そうして、入力画面で「required=&amp;quot;&amp;quot;」というパラメータがあり、これによりJavascriptが無効であってもフィールドが入力されているかどうかを確認できます。&lt;br /&gt;
例:&lt;br /&gt;
&lt;a href=&#34;https://ass.technicalsuwako.moe/fuanform3.png&#34;&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/fuanform3.png&#34; alt=&#34;&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;しかし、このパラメータを削除すると、どのような事態が起こると思いますか?&lt;br /&gt;
正解はこちら:&lt;br /&gt;
&lt;a href=&#34;https://ass.technicalsuwako.moe/fuanform4.png&#34;&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/fuanform4.png&#34; alt=&#34;&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;また、確認画面ではフォームが&lt;code lang=&#34;&#34;&gt;&amp;lt;input type=&amp;quot;hidden&amp;quot; /&amp;gt;&lt;/code&gt;タグを沢山含んでいます。&lt;br /&gt;
その中の「value=&amp;quot;&amp;quot;」部分を変更する事が可能です。&lt;br /&gt;
これにより、MySQLインジェクションも可能となります。&lt;/p&gt;
&lt;h2 id=&#34;heading-1&#34;&gt;解決策&lt;/h2&gt;
&lt;p&gt;上述の問題を解決する為には、サーバー側でのチェックが必要です。&lt;br /&gt;
勿論、クライアント側とサーバー側の両方でチェックを行う事も可能です。&lt;/p&gt;
&lt;p&gt;例として、PHPの場合を紹介します(PHPを使用するフォームが多い為)&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#666&#34;&gt;&amp;lt;?&lt;/span&gt;php
session_name(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;formvals&amp;#34;&lt;/span&gt;);
session_start([
&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;cookie_httponly&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;true&lt;/span&gt;,
]);
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;empty&lt;/span&gt;(&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;csrf_token&amp;#34;&lt;/span&gt;])) &lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;csrf_token&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; bin2hex(random_bytes(&lt;span style=&#34;color:#666&#34;&gt;32&lt;/span&gt;));
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#666&#34;&gt;!&lt;/span&gt;isset(&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;step&amp;#34;&lt;/span&gt;])) &lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;step&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;1&lt;/span&gt;;
&lt;span style=&#34;color:#b8860b&#34;&gt;$errmes&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; [];
&lt;span style=&#34;color:#b8860b&#34;&gt;$reqvals&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; [
&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;??&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;kana&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;kana&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;??&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
];
&lt;span style=&#34;color:#b8860b&#34;&gt;$optvals&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; [
&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;??&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
];
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#b8860b&#34;&gt;$_SERVER&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;REQUEST_METHOD&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;) {
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#666&#34;&gt;!&lt;/span&gt;hash_equals(&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;csrf_token&amp;#34;&lt;/span&gt;], &lt;span style=&#34;color:#b8860b&#34;&gt;$_POST&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;csrf_token&amp;#34;&lt;/span&gt;])) {
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;die&lt;/span&gt;(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;不正なCSRFトークン&amp;#34;&lt;/span&gt;);
}
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;step&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;1&lt;/span&gt;) {
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#b8860b&#34;&gt;$reqvals&lt;/span&gt; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$v&lt;/span&gt;) {
&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; filter_input(INPUT_POST, &lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt;, FILTER_SANITIZE_STRING);
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt;]) &lt;span style=&#34;color:#b8860b&#34;&gt;$reqvals&lt;/span&gt;[&lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt;];
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$errmes&lt;/span&gt;[] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;をご入力下さい。&amp;#34;&lt;/span&gt;;
}
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#b8860b&#34;&gt;$optvals&lt;/span&gt; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$v&lt;/span&gt;) {
&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; filter_input(INPUT_POST, &lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt;, FILTER_SANITIZE_STRING);
&lt;span style=&#34;color:#b8860b&#34;&gt;$optvals&lt;/span&gt;[&lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b8860b&#34;&gt;$k&lt;/span&gt;];
}
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;empty&lt;/span&gt;(&lt;span style=&#34;color:#b8860b&#34;&gt;$errmes&lt;/span&gt;)) &lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;step&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;2&lt;/span&gt;;
}
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;step&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;2&lt;/span&gt;) {
&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;step&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;1&lt;/span&gt;;
session_destroy();
header(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;Location: /success.html&amp;#34;&lt;/span&gt;);
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;die&lt;/span&gt;();
}
}
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;else&lt;/span&gt; {
&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;csrf_token&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; bin2hex(random_bytes(&lt;span style=&#34;color:#666&#34;&gt;32&lt;/span&gt;));
&lt;span style=&#34;color:#b8860b&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;step&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;1&lt;/span&gt;;
}
&lt;span style=&#34;color:#080&#34;&gt;?&amp;gt;&lt;/span&gt;&lt;span style=&#34;&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;&amp;lt;html&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;head&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;meta charset=&amp;#34;utf-8&amp;#34; /&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;title&amp;gt;連絡フォーム&amp;lt;/title&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;/head&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;body&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;&amp;lt;?php
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; if ($_SESSION[&amp;#34;step&amp;#34;] == 1) {
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; if (count($errmes) != 0) {
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;?&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;ul style=&amp;#34;font-width: bolder; color: #f00; list-style: none;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;&amp;lt;?php
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; foreach ($errmes as $e) {
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; echo &amp;#34;&amp;lt;li&amp;gt;&amp;#34;.$e.&amp;#34;&amp;lt;/li&amp;gt;&amp;#34;;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; }
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;?&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;/ul&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;&amp;lt;?php
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; }
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;?&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;form method=&amp;#34;POST&amp;#34; action=&amp;#34;/contact.php&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;input type=&amp;#34;hidden&amp;#34; name=&amp;#34;csrf_token&amp;#34; value=&amp;#34;&amp;lt;?= $_SESSION[&amp;#39;csrf_token&amp;#39;] ?&amp;gt;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;table&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;tbody&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;td&amp;gt;お名前 (必須):&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;td&amp;gt;&amp;lt;input placeholder=&amp;#34;山田 太郎&amp;#34; required=&amp;#34;&amp;#34; name=&amp;#34;name&amp;#34; type=&amp;#34;text&amp;#34; value=&amp;#34;&amp;lt;?= $reqvals[&amp;#34;name&amp;#34;] ?&amp;gt;&amp;#34; /&amp;gt;&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;td&amp;gt;お名前 (かな) (必須):&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;td&amp;gt;&amp;lt;input placeholder=&amp;#34;やまだ たろう&amp;#34; required=&amp;#34;&amp;#34; name=&amp;#34;kana&amp;#34; type=&amp;#34;text&amp;#34; value=&amp;#34;&amp;lt;?= $reqvals[&amp;#34;kana&amp;#34;] ?&amp;gt;&amp;#34; /&amp;gt;&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;tr&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;td&amp;gt;御社又は関連サイトのURL:&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;td&amp;gt;&amp;lt;input placeholder=&amp;#34;https://076.moe/&amp;#34; name=&amp;#34;url&amp;#34; type=&amp;#34;text&amp;#34; value=&amp;#34;&amp;lt;?= $optvals[&amp;#34;url&amp;#34;] ?&amp;gt;&amp;#34; /&amp;gt;&amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;/tbody&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;/table&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;button&amp;gt;確認画面へ&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;/form&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;&amp;lt;?php
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; } else if ($_SESSION[&amp;#34;step&amp;#34;] == 2) {
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;?&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;form method=&amp;#34;POST&amp;#34; action=&amp;#34;/contact.php&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;input type=&amp;#34;hidden&amp;#34; name=&amp;#34;csrf_token&amp;#34; value=&amp;#34;&amp;lt;?= $_SESSION[&amp;#39;csrf_token&amp;#39;] ?&amp;gt;&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; お名前 (必須): &amp;lt;?= $reqvals[&amp;#34;name&amp;#34;] ?&amp;gt;&amp;lt;br /&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; お名前 (かな) (必須): &amp;lt;?= $reqvals[&amp;#34;kana&amp;#34;] ?&amp;gt;&amp;lt;br /&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; 御社又は関連サイトのURL: &amp;lt;?= $optvals[&amp;#34;url&amp;#34;] ?&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;button&amp;gt;送信する&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;/form&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;&amp;lt;?php
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; } else {
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;?&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;p&amp;gt;不明なエラー。&amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;&amp;lt;?php
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; }
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;?&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt; &amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;span style=&#34;&#34;&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;結果:&lt;br /&gt;
&lt;a href=&#34;https://ass.technicalsuwako.moe/anzenform1.png&#34;&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/anzenform1.png&#34; alt=&#34;&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ass.technicalsuwako.moe/anzenform2.png&#34;&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/anzenform2.png&#34; alt=&#34;&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ass.technicalsuwako.moe/anzenform3.png&#34;&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/anzenform3.png&#34; alt=&#34;&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ass.technicalsuwako.moe/anzenform4.png&#34;&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/anzenform4.png&#34; alt=&#34;&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ass.technicalsuwako.moe/anzenform5.png&#34;&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/anzenform5.png&#34; alt=&#34;&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ass.technicalsuwako.moe/anzenform6.png&#34;&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/anzenform6.png&#34; alt=&#34;&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ねぇねぇー!&lt;br /&gt;
簡単でしょー!&lt;/p&gt;
&lt;p&gt;以上&lt;/p&gt;
</content>
</entry>
<entry>
<id>blog/zig-gengo-1.gmi</id>
<author><name>凛</name></author>
<title type="html">【Zig言語】第部~基本的な紹介・セットアップ・「こんにちは、世界」</title>
<published>2023-08-01T00:00:00Z</published>
<category term="blog"></category>
<category term="jp"></category>
<category term="プログラミング"></category>
<category term="zig"></category>
<category term="システム開発"></category>
<link href="gemini://technicalsuwako.moe/blog/zig-gengo-1.gmi" rel="alternate"></link>
<content type="html">
&lt;h2 id=&#34;zig&#34;&gt;Zig言語シリーズ&lt;/h2&gt;
&lt;p&gt;このブログでは様々なプログラミング言語の使い方を紹介します。&lt;br /&gt;
主にZig、PHP、Go言語、C言語について解説し、更にはFLTK、Raylib、OpenGL等も取り上げます。&lt;/p&gt;
&lt;h2 id=&#34;zig-1&#34;&gt;Zig言語とは?&lt;/h2&gt;
&lt;p&gt;Zigは非常に新しい言語です。&lt;br /&gt;
Goが新しい形のPHP、Carbonが新しいC++、Kotlinが新しいJava、TypeScriptが新しいJavascript、Swiftが新しいObjective-Cのように、Zigは新しい形のC言語と考える事が出来ます。&lt;br /&gt;
初めて見ると、Zig言語は難しそうに見えますが、約1週間使ってみれば、そう難しくは感じなくなります。&lt;br /&gt;
現在、最新バージョンは0.10.1ですが、今週中には0.11.0のリリースが予定されています。&lt;br /&gt;
このシリーズではそのバージョンを使用します。&lt;/p&gt;
&lt;h2 id=&#34;czig&#34;&gt;C言語とZig言語の違いは?&lt;/h2&gt;
&lt;p&gt;C言語は16ビットの世代で作られましたが、Zigは64ビットの世代で作られたため、Zigの方がモダンな言語といえます。&lt;br /&gt;
Zig言語は、Rustのような安全性とGo言語のようなシンプルさを持っています。&lt;br /&gt;
コードの違いを以下で示します:&lt;/p&gt;
&lt;h3 id=&#34;c&#34;&gt;C&lt;/h3&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#080&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#080&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#080&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#080&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#00a000&#34;&gt;tuika&lt;/span&gt; (&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;int&lt;/span&gt; b) {
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#666&#34;&gt;+&lt;/span&gt; b;
}
&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#00a000&#34;&gt;main&lt;/span&gt; () {
&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;1&lt;/span&gt;;
&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;int&lt;/span&gt; b &lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;2&lt;/span&gt;;
printf(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;%d&lt;/span&gt;&lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;, tuika(a, b));
&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;0&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;zig-2&#34;&gt;Zig&lt;/h3&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;std&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f&#34;&gt;@import&lt;/span&gt;(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;std&amp;#34;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;fn&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;tuika(a&lt;span style=&#34;color:#666&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;u8&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b&lt;span style=&#34;color:#666&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;u8&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;u8&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;a&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;pub&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;fn&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;main()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;a&lt;span style=&#34;color:#666&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;u8&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;1&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b&lt;span style=&#34;color:#666&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;u8&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;2&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;std.debug.print(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;{d}&lt;/span&gt;&lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.{tuika(a,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b)});&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;よく見ると、スタイルは殆ど同じです。&lt;br /&gt;
ただし、main関数の戻り値はintではなくvoidになります。&lt;br /&gt;
そして、Zigではintが一つの型ではなく、様々なサイズ(u8~u128、i8~i128)があります。&lt;br /&gt;
また、C言語で「型名 変数名 = 値」や「戻り値の型名 関数名 (パラメータ)」と表現するところを、Zig言語では「変更可否 変数名: 型名 = 値」や「公開・非公開 fn 関数名(パラメータ) 戻り値の型名」と表現します。&lt;/p&gt;
&lt;h2 id=&#34;heading&#34;&gt;セットアップ&lt;/h2&gt;
&lt;p&gt;バージョン0.11.0がリリースされれば、パッケージマネージャからインストール出来る様になると思います。&lt;br /&gt;
そうでない場合は、以下のコマンドを実行してください。&lt;br /&gt;
まず、LLVM16以上が必要です。&lt;br /&gt;
それをインストールしたら、Zigをコンパイルする方法は&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; zig-*
mkdir build
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; build
cmake .. -DZIG_STATIC_LLVM&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;ON -DCMAKE_PREFIX_PATH&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;/usr
make install -DPREFIX&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;/usr
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注意あたしはCRUXでしかコンパイル出来ませんでした。&lt;br /&gt;
Artix、OpenBSD、FreeBSDでは失敗しました。&lt;br /&gt;
Devuanは確認していません。&lt;/p&gt;
&lt;p&gt;インストール後、新しいフォルダを作り、新しいプロジェクトを作成しましょう:&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;mkdir hello
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; hello
zig init-exe
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;現在のファイルは以下の様になります:&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;.
├── build.zig
└── src
└── main.zig
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;そのまま&lt;code lang=&#34;&#34;&gt;zig build run&lt;/code&gt;を実行すると:&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;# zig build run
All your codebase are belong to us.
Run `zig build test` to run the tests.
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;heading-1&#34;&gt;「こんにちは、世界!」&lt;/h2&gt;
&lt;p&gt;build.zigについては次の記事で紹介します。&lt;br /&gt;
まず、src/main.zigを開き、全て削除し、以下のコードを書いて下さい。&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;std&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f&#34;&gt;@import&lt;/span&gt;(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;std&amp;#34;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;std.io&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;io;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;pub&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;fn&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;main()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;stdout_file&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;io.getStdOut().writer();&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;var&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;bw&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;io.bufferedWriter(stdout_file);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;stdout&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;bw.writer();&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;try&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;stdout.print(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;こんにちは、世界!&lt;/span&gt;&lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.{});&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;try&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;bw.flush();&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;保存すると、以下のエラーが表示されます:&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt; 1 main.zig|2 col 10| : error: expected &amp;#39;;&amp;#39; after declaration
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;はい、エラーがあると、それを修正するまでテキストエディターを閉じる事が出来ません。&lt;br /&gt;
エラーを直しましょう!&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;io&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;std.io;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-style:italic&#34;&gt;// ioとstd.ioを交換しましょう。
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;heading-2&#34;&gt;ビルドと実行すると&lt;/h3&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;# zig build run
こんにちは、世界!
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;heading-3&#34;&gt;コードの解説&lt;/h3&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;std&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f&#34;&gt;@import&lt;/span&gt;(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;std&amp;#34;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;これにより、Zigの公式標準ライブラリを使用出来る様になります。&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;io&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;std.io;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;これにより、ioコマンドをより簡単に実行出来る様になります。&lt;br /&gt;
例えば、「std.io.getStdOut().writer();」を「io.getStdOut().writer();」に短縮出来ます。&lt;br /&gt;
勿論、「const writer = std.io.getStdOut().writer();」と書く事も可能ですが、一度しか実行しないならばそれはもったいないです。&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;pub&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;fn&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;main()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;pubは公開を意味し、fnは関数を意味します。&lt;br /&gt;
JavaやC#を使った経験があれば、「public function」の様な物です。&lt;br /&gt;
興味深い部分は「!void」です。&lt;br /&gt;
この「!」は「anyerror」と同じ意味を持ちます。&lt;br /&gt;
「void」だけであれば、戻り値の型はいつでもvoidですが、「!void」の場合は「エラーがあれば、そのエラーを返し、なければvoidになる」という意味になります。&lt;br /&gt;
とても便利だわー!!&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;try&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;stdout.print(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;こんにちは、世界!&lt;/span&gt;&lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.{});&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;最後に、この「try」は「このコマンドがメモリ上で安全であれば、実行してください」という意味を持ちます。&lt;br /&gt;
また、この「.{}」は常に必要です。&lt;br /&gt;
値を使う場合は、それを「.{}」の中に入れましょう。&lt;br /&gt;
例えば:&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;age&lt;span style=&#34;color:#666&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;u8&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;20&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;name&lt;span style=&#34;color:#666&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;[]&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0b0;font-weight:bold&#34;&gt;u8&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;田中&amp;#34;&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;try&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;stdout.print(&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;{s}さん、{d}歳になったら、大人ですよ。&lt;/span&gt;&lt;span style=&#34;color:#b62;font-weight:bold&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.{&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;name,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;age&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;});&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;田中さん、20歳になったら、大人ですよ。
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;生成されるバイナリはzig-out/binフォルダに格納されます。&lt;/p&gt;
&lt;p&gt;続く&lt;/p&gt;
</content>
</entry>
<entry>
<id>blog/urloli-220.gmi</id>
<author><name>凛</name></author>
@ -165,516 +481,5 @@
</content>
</entry>
<entry>
<id>blog/digital-autonomy-with-wireguard-home-host.gmi</id>
<author><name>凛</name></author>
<title type="html">【デジタル自主】自宅サーバーからWireGuardを使ったホスティング方法</title>
<published>2023-07-03T00:00:00Z</published>
<category term="jp"></category>
<category term="blog"></category>
<category term="デジタル自主"></category>
<category term="wireguard"></category>
<category term="linux"></category>
<category term="bsd"></category>
<category term="devuan"></category>
<category term="openbsd"></category>
<category term="freebsd"></category>
<link href="gemini://technicalsuwako.moe/blog/digital-autonomy-with-wireguard-home-host.gmi" rel="alternate"></link>
<content type="html">
&lt;p&gt;WireGuardは非常に便利ですね。&lt;br /&gt;
自宅からウェブサイトをホスティングしたり、どこからでも自宅のサーバーやパソコンにアクセスしたりといった事が可能です。&lt;br /&gt;
今回はウェブホスティングの方法についてお伝えします。&lt;/p&gt;
&lt;h2 id=&#34;heading&#34;&gt;材料&lt;/h2&gt;
&lt;p&gt;食べ物じゃないけど(ジョーダンダヨー)、必要な物は下記で御座います。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;好みのVPSホスティング会社から最も安いVPS(日本のOpenBSDサーバーはオススメ、ConoHaまたはVultrは良い)&lt;/li&gt;
&lt;li&gt;自宅にあるサーバー(1台でも複数台でも可)&lt;/li&gt;
&lt;li&gt;高速光回線インターネット接続&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;静的IPは不要です。&lt;br /&gt;
動的IPでも問題がありません。&lt;/p&gt;
&lt;p&gt;OS等は何でも良いですが、この記事ではVPSはConoHaのOpenBSDで、自宅サーバーは2台のThinkCentreとOpenBSD、及びNECとFreeBSDを使用しています。&lt;br /&gt;
また、どこへでも持ち運べるThinkPad(Artix)というノートパソコンも用いています。&lt;br /&gt;
FreeBSDでPeerTubeをインストールし、URLロリはOpenBSDで行い、さまざまな静的なホームページはOpenBSDで実行しています。&lt;br /&gt;
インターネット接続サービスは何でも良いですが、高速光回線インターネット接続が推奨されます。&lt;/p&gt;
&lt;h2 id=&#34;wireguard&#34;&gt;WireGuardのインストール&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OpenBSD&lt;/th&gt;
&lt;th&gt;FreeBSD&lt;/th&gt;
&lt;th&gt;Artix (runitの場合)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;doas pkg_add wireguard-tools&lt;/td&gt;
&lt;td&gt;sudo pkg install wireguard-tools&lt;/td&gt;
&lt;td&gt;sudo pacman -S wireguard-tools wireguard-runit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Artixの場合、インストール後に再起動が必要な場合があります。&lt;br /&gt;
インストールしたlinux-kernelのバージョンが現在実行中のバージョンと異なると、WireGuardが起動出来なくなります。&lt;/p&gt;
&lt;h2 id=&#34;wireguard-1&#34;&gt;WireGuardの設定&lt;/h2&gt;
&lt;h3 id=&#34;vps&#34;&gt;VPS側&lt;/h3&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;doas su
mkdir /etc/wireguard
chmod &lt;span style=&#34;color:#666&#34;&gt;600&lt;/span&gt; /etc/wireguard
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; /etc/wireguard
wg genkey | tee private.key | wg pubkey &amp;gt; public.key
nvim wg0.conf
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;[Interface]
Address = 192.168.10.1/24
PrivateKey = (VPSのprivate.keyの内容)
ListenPort = 51820
[Peer]
PublicKey = (ThinkPadのpublic.keyの内容)
PreSharedKey = (ThinkPadのpreshared.keyの内容)
AllowedIPs = 192.168.10.100/32
PersistentKeepalive = 25
[Peer]
PublicKey = (ThinkCentreのpublic.keyの内容)
PreSharedKey = (ThinkCentreのpreshared.keyの内容)
AllowedIPs = 192.168.10.101/32
PersistentKeepalive = 25
[Peer]
PublicKey = (NECのpublic.keyの内容)
PreSharedKey = (NECのpreshared.keyの内容)
AllowedIPs = 192.168.10.102/32
PersistentKeepalive = 25
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;nvim /etc/pf.conf
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;set skip on lo
exsrv1 = (VPSのIPアドレス)
insrv1 = 192.168.10.101
insrv2 = 192.168.10.102
block return
pass
pass in on wg0
pass in inet proto udp from any to any port 51820
# PeerTube
pass in inet proto udp from any to $insrv2 port 9000
pass in on egress proto tcp from any to $insrv2 port {1935, 1936} rdr-to $insrv2
# URLロリ
pass in inet proto udp from any to $insrv1 port 9910
# Gemini
pass in on egress proto tcp from any to $insrv1 port { 1965 } rdr-to $insrv1
# HTML
pass out on egress inet from (wg0:network) nat-to (vio0:0)
# SSHはWireGuardネットワーク内のみ許可する
pass in on wg0 proto tcp from 192.168.10.0/24 to any port 22
block in on egress proto tcp from any to any port 22
...
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;pfctl -f /etc/pf.conf
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;最高レベルのセキュリティを保つ為に:&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;nvim /etc/ssh/sshd_config
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;...
AllowUsers (貴方のユーザー名)@192.168.10.0/24
PermitRootLogin no
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
...
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#a2f&#34;&gt;exit&lt;/span&gt;
ssh-keygen -t ed25519
&lt;span style=&#34;color:#666&#34;&gt;(&lt;/span&gt;最後までそのままEnterキーを押して&lt;span style=&#34;color:#666&#34;&gt;)&lt;/span&gt;
cat ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;出力内容をコピーし、各サーバーとノートパソコンの「~/.ssh/authorized_keys」ファイルに貼り付けて下さい。&lt;br /&gt;
次に、上記のステップを各サーバー及びノートパソコンで実行し、その結果をVPSの「/.ssh/authorized_keys」ファイルに貼り付けて下さい。&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;doas rcctl restart sshd
wg-quick up wg0
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;heading-1&#34;&gt;自宅サーバーとノートパソコンの設定&lt;/h3&gt;
&lt;p&gt;各デバイスの設定は基本的に同じです。&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;doas su
mkdir /etc/wireguard
chmod &lt;span style=&#34;color:#666&#34;&gt;600&lt;/span&gt; /etc/wireguard
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; /etc/wireguard
wg genkey | tee private.key | wg pubkey &amp;gt; public.key
wg genpsk &amp;gt; preshared.key
nvim wg0.conf
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;[Interface]
PrivateKey = (現在のデバイスのprivate.keyの内容)
Address = 192.168.10.10x/24
[Peer]
PublicKey = (VPSのpublic.keyの内容)
PreSharedKey = (現在のデバイスのpreshared.keyの内容)
Endpoint = (VPSの公開IPアドレス):51820
AllowedIPs = 192.168.10.0/24
PersistentKeepalive = 25
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Address部分の「x」は、各サーバーで設定した通りです。&lt;br /&gt;
この記事では、Artix0、OpenBSD1、FreeBSD2となります。&lt;/p&gt;
&lt;h4 id=&#34;openbsd&#34;&gt;OpenBSDサーバー&lt;/h4&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt;
wget https://lab.abiscuola.org/gmnxd/tarball/v1.2.0/gmnxd-v1.2.0.tar.gz
tar zxfv gmnxd-v1.2.0.tar.gz
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; gmnxd-v1.2.0/src
make
make install
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt;
useradd -g &lt;span style=&#34;color:#b44&#34;&gt;&amp;#39;=uid&amp;#39;&lt;/span&gt; -L daemon -s /sbin/nologin -c &lt;span style=&#34;color:#b44&#34;&gt;&amp;#39;Gmnxd user&amp;#39;&lt;/span&gt; -d /var/gemini _gmnxd
chown -R _gmnxd:_gmnxd /var/gemini
nvim /etc/inetd.conf
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;0.0.0.0:11965 stream tcp nowait _gmnxd /usr/local/libexec/gmnxd gmnxd
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;rcctl &lt;span style=&#34;color:#a2f&#34;&gt;enable&lt;/span&gt; inetd
rcctl start inetd
nvim /etc/pf.conf
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;set skip on lo
block return
pass
# HTTP
pass in inet proto tcp from any to (self) port {80, 443}
# URLロリ
pass in inet proto tcp from any to (self) port 9910
# Gemini
pass in inet proto tcp from any to (self) port 11965
...
anchor &amp;#34;relayd/*&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;pfctl -f /etc/pf.conf
wg-quick up wg0
mkdir -p /var/www/htdocs/&lt;span style=&#34;color:#666&#34;&gt;{&lt;/span&gt;076,minmi&lt;span style=&#34;color:#666&#34;&gt;}&lt;/span&gt;.moe/www
mkdir -p /var/gemini/076.moe
&lt;span style=&#34;color:#a2f&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;わーい&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /var/www/htdocs/&lt;span style=&#34;color:#666&#34;&gt;{&lt;/span&gt;076,minmi&lt;span style=&#34;color:#666&#34;&gt;}&lt;/span&gt;.moe/www/index.html
&lt;span style=&#34;color:#a2f&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;わーい&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /var/gemini/076.moe/index.gmi
nvim /etc/httpd.conf
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;eth_addr=*
wg0_addr=192.168.10.103
## 076.moe
server &amp;#34;076.moe&amp;#34; {
listen on $wg0_addr port 8080
root &amp;#34;/htdocs/076.moe/www&amp;#34;
directory index &amp;#34;index.html&amp;#34;
location &amp;#34;/repo/*&amp;#34; {
directory auto index
}
}
server &amp;#34;l3nbzyxgrkmd46nacmzf2sy6tpjrwh4iv3pgacbrbk72wcgxq5a.b32.i2p&amp;#34; {
listen on $eth_addr port 8450
root &amp;#34;/htdocs/076.moe/www&amp;#34;
directory index &amp;#34;index.html&amp;#34;
location &amp;#34;/repo/*&amp;#34; {
directory auto index
}
}
server &amp;#34;7dt6irsmfvbrtgn4nuah56kky6mvr472fbwwaltuxpf26qdqkdhfvnqd.onion&amp;#34; {
listen on $eth_addr port 8500
root &amp;#34;/htdocs/076.moe/www&amp;#34;
directory index &amp;#34;index.html&amp;#34;
location &amp;#34;/repo/*&amp;#34; {
directory auto index
}
}
## minmi.moe
server &amp;#34;minmi.moe&amp;#34; {
listen on $wg0_addr port 8087
root &amp;#34;/htdocs/minmi.moe/www&amp;#34;
directory index &amp;#34;index.html&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;mkdir -p /var/www/htdocs/urlo.li
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; /var/www/htdocs/urlo.li
git clone https://gitler.moe/suwako/urloli.git .
nvim Makefile
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;一部編集は必要となります。&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;# Linux、Cruxの場合は必須。他のディストリビューションはどうでも良い
#PREFIX=/usr
# FreeBSDとOpenBSD
PREFIX=/usr/local
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;rcctl &lt;span style=&#34;color:#a2f&#34;&gt;enable&lt;/span&gt; httpd
rcctl start httpd
make
make install
nvim /etc/urloli/config.json
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;{
&amp;#34;domain&amp;#34;: &amp;#34;https://(ドメイン名)&amp;#34;,
&amp;#34;webpath&amp;#34;: &amp;#34;/var/www/htdocs/urlo.li&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;freebsd&#34;&gt;FreeBSDサーバー&lt;/h4&gt;
&lt;p&gt;公式ガイドに従って、nginxとcertbotをインストールが必要とされていますが、今回の場合は不要です。&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;wg-quick up wg0
pkg install -y sudo bash wget git python pkgconf postgresql13-server postgresql13-contrib redis openssl node npm yarn ffmpeg unzip
visudo
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;%wheel ALL=(ALL) ALL
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;sysrc &lt;span style=&#34;color:#b8860b&#34;&gt;postgresql_enable&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;YES&amp;#34;&lt;/span&gt;
sysrc &lt;span style=&#34;color:#b8860b&#34;&gt;redis_enable&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;YES&amp;#34;&lt;/span&gt;
sysrc &lt;span style=&#34;color:#b8860b&#34;&gt;nginx_enable&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;YES&amp;#34;&lt;/span&gt;
sudo pw useradd -n peertube -d /var/www/peertube -s /usr/local/bin/bash -m
sudo passwd peertube
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; /var/www/peertube
sudo -u postgres createuser -P peertube
sudo -u postgres createdb -O peertube -E UTF8 -T template0 peertube_prod
sudo -u postgres psql -c &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;CREATE EXTENSION pg_trgm;&amp;#34;&lt;/span&gt; peertube_prod
sudo -u postgres psql -c &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;CREATE EXTENSION unaccent;&amp;#34;&lt;/span&gt; peertube_prod
&lt;span style=&#34;color:#b8860b&#34;&gt;VERSION&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;$(&lt;/span&gt;curl -s https://api.github.com/repos/chocobozzz/peertube/releases/latest | grep tag_name | cut -d &lt;span style=&#34;color:#b44&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; -f 4&lt;span style=&#34;color:#a2f;font-weight:bold&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#666&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a2f&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;Latest Peertube version is &lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;$VERSION&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; /var/www/peertube
sudo -u peertube mkdir config storage versions
sudo -u peertube chmod &lt;span style=&#34;color:#666&#34;&gt;750&lt;/span&gt; config/
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; /var/www/peertube/versions
sudo -u peertube wget -q &lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;https://github.com/Chocobozzz/PeerTube/releases/download/&lt;/span&gt;&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;VERSION&lt;/span&gt;&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;/peertube-&lt;/span&gt;&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;VERSION&lt;/span&gt;&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;.zip&amp;#34;&lt;/span&gt;
sudo -u peertube unzip -q peertube-&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;VERSION&lt;/span&gt;&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;}&lt;/span&gt;.zip &lt;span style=&#34;color:#666&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo -u peertube rm peertube-&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;VERSION&lt;/span&gt;&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;}&lt;/span&gt;.zip
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; /var/www/peertube
sudo -u peertube ln -s versions/peertube-&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#b8860b&#34;&gt;VERSION&lt;/span&gt;&lt;span style=&#34;color:#b68;font-weight:bold&#34;&gt;}&lt;/span&gt; ./peertube-latest
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; ./peertube-latest &lt;span style=&#34;color:#666&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo -H -u peertube yarn install --production --pure-lockfile
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; /var/www/peertube
sudo -u peertube cp peertube-latest/config/default.yaml config/default.yaml
&lt;span style=&#34;color:#a2f&#34;&gt;cd&lt;/span&gt; /var/www/peertube
sudo -u peertube cp peertube-latest/config/production.yaml.example config/production.yaml
nvim config/production.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;listen:
hostname: &amp;#39;0.0.0.0&amp;#39;
port: 9000
webserver:
https: true
hostname: &amp;#39;(ドメイン名)&amp;#39;
port: 443
...
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;sudo install -m &lt;span style=&#34;color:#666&#34;&gt;0555&lt;/span&gt; /var/www/peertube/peertube-latest/support/freebsd/peertube /usr/local/etc/rc.d/
sudo sysrc &lt;span style=&#34;color:#b8860b&#34;&gt;peertube_enable&lt;/span&gt;&lt;span style=&#34;color:#666&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#b44&#34;&gt;&amp;#34;YES&amp;#34;&lt;/span&gt;
sudo service peertube start
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;artix&#34;&gt;Artixノートパソコン&lt;/h4&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;wg-quick up wg0
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;vps-1&#34;&gt;再度、VPSの設定&lt;/h3&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;nvim /etc/acme-client.conf
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;authority letsencrypt {
api url &amp;#34;https://acme-v02.api.letsencrypt.org/directory&amp;#34;
account key &amp;#34;/etc/acme/letsencrypt-privkey.pem&amp;#34;
}
domain 076.moe {
alternative names { www.076.moe }
domain key &amp;#34;/etc/ssl/private/076.moe.key&amp;#34;
domain full chain certificate &amp;#34;/etc/ssl/076.moe.crt&amp;#34;
sign with letsencrypt
}
domain video.076.moe {
domain key &amp;#34;/etc/ssl/private/video.076.moe.key&amp;#34;
domain full chain certificate &amp;#34;/etc/ssl/video.076.moe.crt&amp;#34;
sign with letsencrypt
}
domain urlo.li {
domain key &amp;#34;/etc/ssl/private/urlo.li.key&amp;#34;
domain full chain certificate &amp;#34;/etc/ssl/urlo.li.crt&amp;#34;
sign with letsencrypt
}
domain minmi.moe {
domain key &amp;#34;/etc/ssl/private/minmi.moe.key&amp;#34;
domain full chain certificate &amp;#34;/etc/ssl/minmi.moe.crt&amp;#34;
sign with letsencrypt
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;nvim /etc/httpd.conf
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;server &amp;#34;default&amp;#34; {
listen on * port 80
root &amp;#34;/htdocs&amp;#34;
location &amp;#34;/.well-known/acme-challenge/*&amp;#34; {
root &amp;#34;/acme&amp;#34;
request strip 2
}
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;nvim /etc/relayd.conf
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;relayd_addr=&amp;#34;0.0.0.0&amp;#34;
insrv1_addr=&amp;#34;192.168.10.101&amp;#34;
insrv2_addr=&amp;#34;192.168.10.102&amp;#34;
table &amp;lt;home&amp;gt; { $insrv1_addr }
table &amp;lt;urloli&amp;gt; { $insrv1_addr }
table &amp;lt;minmi&amp;gt; { $insrv1_addr }
table &amp;lt;video&amp;gt; { $insrv2_addr }
http protocol reverse {
tcp { nodelay, sack }
tls ciphers &amp;#34;TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256&amp;#34;
tls keypair &amp;#34;076.moe&amp;#34;
tls keypair &amp;#34;video.076.moe&amp;#34;
tls keypair &amp;#34;urlo.li&amp;#34;
tls keypair &amp;#34;minmi.moe&amp;#34;
match request header append &amp;#34;X-Forwarded-For&amp;#34; value &amp;#34;$REMOTE_ADDR&amp;#34;
match request header append &amp;#34;X-Forwarded-Port&amp;#34; value &amp;#34;$REMOTE_PORT&amp;#34;
match response header set &amp;#34;Referrer-Policy&amp;#34; value &amp;#34;same-origin&amp;#34;
match response header set &amp;#34;X-Frame-Options&amp;#34; value &amp;#34;deny&amp;#34;
match response header set &amp;#34;X-XSS-Protection&amp;#34; value &amp;#34;1; mode=block&amp;#34;
match response header set &amp;#34;X-Content-Type-Options&amp;#34; value &amp;#34;nosniff&amp;#34;
match response header set &amp;#34;Strict-Transport-Security&amp;#34; value &amp;#34;max-age=31536000; includeSubDomains; preload&amp;#34;
match response header set &amp;#34;Cache-Control&amp;#34; value &amp;#34;max-age=86400&amp;#34;
pass request quick header &amp;#34;Host&amp;#34; value &amp;#34;076.moe&amp;#34; forward to &amp;lt;home&amp;gt;
pass request quick header &amp;#34;Host&amp;#34; value &amp;#34;www.076.moe&amp;#34; forward to &amp;lt;home&amp;gt;
pass request quick header &amp;#34;Host&amp;#34; value &amp;#34;video.076.moe&amp;#34; forward to &amp;lt;video&amp;gt;
pass request quick header &amp;#34;Host&amp;#34; value &amp;#34;urlo.li&amp;#34; forward to &amp;lt;urloli&amp;gt;
pass request quick header &amp;#34;Host&amp;#34; value &amp;#34;minmi.moe&amp;#34; forward to &amp;lt;minmi&amp;gt;
return error
pass
}
http protocol reverse80 {
match request header append &amp;#34;X-Forwarded-For&amp;#34; value &amp;#34;$REMOTE_ADDR&amp;#34;
match request header append &amp;#34;X-Forwarded-Port&amp;#34; value &amp;#34;$REMOTE_PORT&amp;#34;
match response header set &amp;#34;Referrer-Policy&amp;#34; value &amp;#34;same-origin&amp;#34;
match response header set &amp;#34;X-Frame-Options&amp;#34; value &amp;#34;deny&amp;#34;
match response header set &amp;#34;X-XSS-Protection&amp;#34; value &amp;#34;1; mode=block&amp;#34;
match response header set &amp;#34;X-Content-Type-Options&amp;#34; value &amp;#34;nosniff&amp;#34;
match response header set &amp;#34;Strict-Transport-Security&amp;#34; value &amp;#34;max-age=31536000; includeSubDomains; preload&amp;#34;
match response header set &amp;#34;Cache-Control&amp;#34; value &amp;#34;max-age=86400&amp;#34;
pass request quick header &amp;#34;Host&amp;#34; value &amp;#34;076.moe&amp;#34; forward to &amp;lt;home&amp;gt;
pass request quick header &amp;#34;Host&amp;#34; value &amp;#34;www.076.moe&amp;#34; forward to &amp;lt;home&amp;gt;
pass request quick header &amp;#34;Host&amp;#34; value &amp;#34;video.076.moe&amp;#34; forward to &amp;lt;video&amp;gt;
pass request quick header &amp;#34;Host&amp;#34; value &amp;#34;urlo.li&amp;#34; forward to &amp;lt;urloli&amp;gt;
pass request quick header &amp;#34;Host&amp;#34; value &amp;#34;minmi.moe&amp;#34; forward to &amp;lt;minmi&amp;gt;
return error
pass
}
protocol gemini {
tcp { nodelay, sack }
tls ciphers &amp;#34;TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256&amp;#34;
tls keypair &amp;#34;076.moe&amp;#34;
}
relay www_tls {
listen on $relayd_addr port 443 tls
protocol reverse
forward to &amp;lt;video&amp;gt; port 9000 check tcp
forward to &amp;lt;home&amp;gt; port 8080 check tcp
forward to &amp;lt;urloli&amp;gt; port 9910 check tcp
forward to &amp;lt;minmi&amp;gt; port 8087 check tcp
}
relay www_http {
listen on $relayd_addr port 80
protocol reverse80
forward to &amp;lt;video&amp;gt; port 9000 check tcp
forward to &amp;lt;home&amp;gt; port 8080 check tcp
forward to &amp;lt;urloli&amp;gt; port 9910 check tcp
forward to &amp;lt;minmi&amp;gt; port 8087 check tcp
}
relay gemini {
listen on $relayd_addr port 1965 tls
protocol gemini
forward to &amp;lt;home&amp;gt; check tcp port 11965
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;rcctl &lt;span style=&#34;color:#a2f&#34;&gt;enable&lt;/span&gt; httpd
rcctl start httpd
acme-client -v 076.moe
acme-client -v video.076.moe
acme-client -v urlo.li
acme-client -v minmi.moe
rcctl &lt;span style=&#34;color:#a2f&#34;&gt;enable&lt;/span&gt; relayd
rcctl start relayd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;SSL証明書を更新するには、下記のコマンドをVPS上で実行して下さい&lt;/p&gt;
&lt;pre&gt;&lt;code lang=&#34;&#34;&gt;rcctl stop relayd
acme-client -v 076.moe
acme-client -v video.076.moe
acme-client -v urlo.li
acme-client -v minmi.moe
rcctl start relayd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;https://ass.technicalsuwako.moe/kanrinya.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;ねぇー!簡単でしょー!&lt;/p&gt;
&lt;p&gt;以上&lt;/p&gt;
</content>
</entry>
<entry>
<id>blog/hozonsite-100.gmi</id>
<author><name>凛</name></author>
<title type="html">【076】保存サイト 1.0.0登場</title>
<published>2023-06-23T19:20:00Z</published>
<category term="jp"></category>
<category term="blog"></category>
<category term="ウエブ開発"></category>
<category term=""></category>
<category term="hozonsite"></category>
<category term="保存サイト"></category>
<link href="gemini://technicalsuwako.moe/blog/hozonsite-100.gmi" rel="alternate"></link>
<content type="html">
&lt;p&gt;保存サイト は1.0.0にバージョンアップしました!!&lt;/p&gt;
&lt;h2 id=&#34;heading&#34;&gt;保存サイトって何?&lt;/h2&gt;
&lt;p&gt;世界初FOSS系ウエブアーカイバーです。&lt;/p&gt;
&lt;h2 id=&#34;heading-1&#34;&gt;変更&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;PHPからGoに交換しました&lt;/li&gt;
&lt;li&gt;今度からバージョンを付きます&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;heading-2&#34;&gt;ソースコード&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://gitler.moe/suwako/hozonsite&#34;&gt;Gitler&lt;/a&gt;&lt;br /&gt;
&lt;a href=&#34;https://codeberg.org/TechnicalSuwako/hozonsite&#34;&gt;Codeberg&lt;/a&gt;&lt;br /&gt;
&lt;a href=&#34;https://notabug.org/TechnicalSuwako/hozonsite&#34;&gt;Notabug&lt;/a&gt;&lt;br /&gt;
&lt;a href=&#34;https://git.disroot.org/TechnicalSuwako/hozonsite&#34;&gt;Disroot&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;heading-3&#34;&gt;公式インスタンス&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://hozon.site/&#34;&gt;https://hozon.site/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;heading-4&#34;&gt;ダウンロード&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://gitler.moe/suwako/hozonsite/releases&#34;&gt;リリースページ&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;heading-5&#34;&gt;会話&lt;/h2&gt;
&lt;h3 id=&#34;xmpp&#34;&gt;XMPP&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;xmpp:hozonsite@chat.xmpp.076.ne.jp?join&#34;&gt;xmpp:hozonsite@chat.xmpp.076.ne.jp?join&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;irc&#34;&gt;IRC&lt;/h3&gt;
&lt;p&gt;irc.076.ne.jp/6697&lt;br /&gt;
#hozonsite&lt;/p&gt;
&lt;p&gt;以上&lt;/p&gt;
</content>
</entry>
</feed>

ファイルの表示

@ -2,7 +2,8 @@
# 記事一覧
=> /blog/gemini/blog/zig-gengo-1.gmi 2023年08月01日 【Zig言語】第部~基本的な紹介・セットアップ・「こんにちは、世界」
=> /blog/fix-broken-contact-form.gmi 2023年08月04日 【PHP】正しい連絡フォームの作り方(クライアント側をぜったいに信用するな
=> /blog/zig-gengo-1.gmi 2023年08月01日 【Zig言語】第部~基本的な紹介・セットアップ・「こんにちは、世界」
=> /blog/urloli-220.gmi 2023年07月17日 【】URLロリ 2.2.0登場
=> /blog/hozonsite-110.gmi 2023年07月17日 【076】保存サイト 1.1.0登場
=> /blog/urloli-211.gmi 2023年07月04日 【】URLロリ 2.1.1登場

167
gemini/blog/fix-broken-contact-form.gmi ノーマルファイル
ファイルの表示

@ -0,0 +1,167 @@
=> /blog.gmi ブログ一覧へ
# 【PHP】正しい連絡フォームの作り方(クライアント側をぜったいに信用するな
公開日2023-08-04
## 問題
現在の「モダン」ウェブ開発で、連絡フォームはJavascriptで制御されていますが、これは大きなリスクがあります。
その理由について、直ぐに説明します。
以下のスクショをご覧いただいたら、何が問題は何だと思いますか?
=> /static/fuanform1.png
正解は:送信ボタンは`<form>`タグの外にある事です。
これでは、Javascriptを無効にした場合、送信ボタンをクリックする事が出来ません。
このフォームを送信する為には、この送信ボタンをフォーム内に移動し、`type="button"`を`type="submit"`に変更する事で、Javascriptなしでもフォームを送信する事が可能になります。
そんな感じ:
=> /static/fuanform2.png
そうして、入力画面で「required=""」というパラメータがあり、これによりJavascriptが無効であってもフィールドが入力されているかどうかを確認できます。
例:
=> /static/fuanform3.png
しかし、このパラメータを削除すると、どのような事態が起こると思いますか?
正解はこちら:
=> /static/fuanform4.png
また、確認画面ではフォームが`<input type="hidden" />`タグを沢山含んでいます。
その中の「value=""」部分を変更する事が可能です。
これにより、MySQLインジェクションも可能となります。
## 解決策
上述の問題を解決する為には、サーバー側でのチェックが必要です。
勿論、クライアント側とサーバー側の両方でチェックを行う事も可能です。
例として、PHPの場合を紹介します(PHPを使用するフォームが多い為)
```php
<?php
session_name("formvals");
session_start([
"cookie_httponly" => true,
]);
if (empty($_SESSION["csrf_token"])) $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
if (!isset($_SESSION["step"])) $_SESSION["step"] = 1;
$errmes = [];
$reqvals = [
"name" => $_SESSION["name"] ?? "",
"kana" => $_SESSION["kana"] ?? "",
];
$optvals = [
"url" => $_SESSION["url"] ?? "",
];
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (!hash_equals($_SESSION["csrf_token"], $_POST["csrf_token"])) {
die("不正なCSRFトークン");
}
if ($_SESSION["step"] == 1) {
foreach ($reqvals as $k => $v) {
$_SESSION[$k] = filter_input(INPUT_POST, $k, FILTER_SANITIZE_STRING);
if ($_SESSION[$k]) $reqvals[$k] = $_SESSION[$k];
else $errmes[] = $k."をご入力下さい。";
}
foreach ($optvals as $k => $v) {
$_SESSION[$k] = filter_input(INPUT_POST, $k, FILTER_SANITIZE_STRING);
$optvals[$k] = $_SESSION[$k];
}
if (empty($errmes)) $_SESSION["step"] = 2;
}
else if ($_SESSION["step"] == 2) {
$_SESSION["step"] = 1;
session_destroy();
header("Location: /success.html");
die();
}
}
else {
$_SESSION["csrf_token"] = bin2hex(random_bytes(32));
$_SESSION["step"] = 1;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>連絡フォーム</title>
</head>
<body>
<?php
if ($_SESSION["step"] == 1) {
if (count($errmes) != 0) {
?>
<ul style="font-width: bolder; color: #f00; list-style: none;">
<?php
foreach ($errmes as $e) {
echo "<li>".$e."</li>";
}
?>
</ul>
<?php
}
?>
<form method="POST" action="/contact.php">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<table>
<tbody>
<tr>
<td>お名前 (必須):</td>
<td><input placeholder="山田 太郎" required="" name="name" type="text" value="<?= $reqvals["name"] ?>" /></td>
</tr>
<tr>
<td>お名前 (かな) (必須):</td>
<td><input placeholder="やまだ たろう" required="" name="kana" type="text" value="<?= $reqvals["kana"] ?>" /></td>
</tr>
<tr>
<td>御社又は関連サイトのURL:</td>
<td><input placeholder="https://076.moe/" name="url" type="text" value="<?= $optvals["url"] ?>" /></td>
</tr>
</tbody>
</table>
<button>確認画面へ</button>
</form>
<?php
} else if ($_SESSION["step"] == 2) {
?>
<form method="POST" action="/contact.php">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
お名前 (必須): <?= $reqvals["name"] ?><br />
お名前 (かな) (必須): <?= $reqvals["kana"] ?><br />
御社又は関連サイトのURL: <?= $optvals["url"] ?><br /><br />
<button>送信する</button>
</form>
<?php
} else {
?>
<p>不明なエラー。</p>
<?php
}
?>
</body>
</html>
```
結果:
=> /static/anzenform1.png
=> /static/anzenform2.png
=> /static/anzenform3.png
=> /static/anzenform4.png
=> /static/anzenform5.png
=> /static/anzenform6.png
ねぇねぇー!
簡単でしょー!
以上

バイナリ
gemini/static/anzenform1.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 60 KiB

バイナリ
gemini/static/anzenform2.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 25 KiB

バイナリ
gemini/static/anzenform3.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 16 KiB

バイナリ
gemini/static/anzenform4.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 24 KiB

バイナリ
gemini/static/anzenform5.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 122 KiB

バイナリ
gemini/static/anzenform6.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 88 KiB

バイナリ
gemini/static/fuanform1.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 220 KiB

バイナリ
gemini/static/fuanform2.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 233 KiB

バイナリ
gemini/static/fuanform3.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 258 KiB

バイナリ
gemini/static/fuanform4.png ノーマルファイル

バイナリファイルは表示されません。

変更後

幅:  |  高さ:  |  サイズ: 105 KiB

ファイルの表示

@ -3,6 +3,8 @@
# avatar = gemini://076.moe/static/suwako.jpg
# description = テクニカル諏訪子様のtwtxtフィード
# link = ホームページ gemini://technicalsuwako.moe
2023-08-04T00:00:00Z09:00 【ウェブ開発】正しい連絡フォームの作り方(クライアント側をぜったいに信用するな!!) - gemini://technicalsuwako.moe/blog/blog/fix-broken-contact-form.gmi
2023-08-01T00:00:00Z09:00 【Zig言語】第部~基本的な紹介・セットアップ・「こんにちは、世界」 - gemini://technicalsuwako.moe/blog/blog/zig-gengo-1.gmi
2023-07-17T02:40:00Z09:00 【】URLロリ 2.2.0登場 - gemini://technicalsuwako.moe/blog/blog/urloli-220.gmi
2023-07-17T02:30:00Z09:00 【076】保存サイト 1.1.0登場 - gemini://technicalsuwako.moe/blog/blog/hozonsite-110.gmi
2023-07-04T00:00:00Z09:00 【】URLロリ 2.1.1登場 - gemini://technicalsuwako.moe/blog/blog/urloli-211.gmi
@ -101,4 +103,3 @@
2019-12-05T00:00:00Z09:00 日本APIを開発中部分公開開始 - gemini://technicalsuwako.moe/blog/blog/nihon-api-wa-chotto-dake-kokaichu.gmi
2019-07-19T00:00:00Z09:00 Debian 9(stretch)→10(buster)バージョンアップする方法 - gemini://technicalsuwako.moe/blog/blog/debian-stretch-buster-upgrade.gmi
2018-12-14T00:00:00Z09:00 はじめまして - gemini://technicalsuwako.moe/blog/blog/hajimemashite.gmi
0001-01-01T00:00:00Z09:00 - gemini://technicalsuwako.moe/blog/blog/zig-gengo-1.gmi

167
src/blog/fix-broken-contact-form/index.md ノーマルファイル
ファイルの表示

@ -0,0 +1,167 @@
title: 【PHP】正しい連絡フォームの作り方(クライアント側をぜったいに信用するな
author: 凛
date: 2023-08-04
tags: blog,jp,プログラミング,php,ウエブ開発,html,javascript
----
## 問題
現在の「モダン」ウェブ開発で、連絡フォームはJavascriptで制御されていますが、これは大きなリスクがあります。\
その理由について、直ぐに説明します。
以下のスクショをご覧いただいたら、何が問題は何だと思いますか?\
[![](https://ass.technicalsuwako.moe/fuanform1.png)](https://ass.technicalsuwako.moe/fuanform1.png)
正解は:送信ボタンは`<form>`タグの外にある事です。\
これでは、Javascriptを無効にした場合、送信ボタンをクリックする事が出来ません。
このフォームを送信する為には、この送信ボタンをフォーム内に移動し、`type="button"`を`type="submit"`に変更する事で、Javascriptなしでもフォームを送信する事が可能になります。\
そんな感じ:\
[![](https://ass.technicalsuwako.moe/fuanform2.png)](https://ass.technicalsuwako.moe/fuanform2.png)
そうして、入力画面で「required=""」というパラメータがあり、これによりJavascriptが無効であってもフィールドが入力されているかどうかを確認できます。\
例:\
[![](https://ass.technicalsuwako.moe/fuanform3.png)](https://ass.technicalsuwako.moe/fuanform3.png)
しかし、このパラメータを削除すると、どのような事態が起こると思いますか?\
正解はこちら:\
[![](https://ass.technicalsuwako.moe/fuanform4.png)](https://ass.technicalsuwako.moe/fuanform4.png)
また、確認画面ではフォームが`<input type="hidden" />`タグを沢山含んでいます。\
その中の「value=""」部分を変更する事が可能です。\
これにより、MySQLインジェクションも可能となります。
## 解決策
上述の問題を解決する為には、サーバー側でのチェックが必要です。\
勿論、クライアント側とサーバー側の両方でチェックを行う事も可能です。
例として、PHPの場合を紹介します(PHPを使用するフォームが多い為)
```php
<?php
session_name("formvals");
session_start([
"cookie_httponly" => true,
]);
if (empty($_SESSION["csrf_token"])) $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
if (!isset($_SESSION["step"])) $_SESSION["step"] = 1;
$errmes = [];
$reqvals = [
"name" => $_SESSION["name"] ?? "",
"kana" => $_SESSION["kana"] ?? "",
];
$optvals = [
"url" => $_SESSION["url"] ?? "",
];
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (!hash_equals($_SESSION["csrf_token"], $_POST["csrf_token"])) {
die("不正なCSRFトークン");
}
if ($_SESSION["step"] == 1) {
foreach ($reqvals as $k => $v) {
$_SESSION[$k] = filter_input(INPUT_POST, $k, FILTER_SANITIZE_STRING);
if ($_SESSION[$k]) $reqvals[$k] = $_SESSION[$k];
else $errmes[] = $k."をご入力下さい。";
}
foreach ($optvals as $k => $v) {
$_SESSION[$k] = filter_input(INPUT_POST, $k, FILTER_SANITIZE_STRING);
$optvals[$k] = $_SESSION[$k];
}
if (empty($errmes)) $_SESSION["step"] = 2;
}
else if ($_SESSION["step"] == 2) {
$_SESSION["step"] = 1;
session_destroy();
header("Location: /success.html");
die();
}
}
else {
$_SESSION["csrf_token"] = bin2hex(random_bytes(32));
$_SESSION["step"] = 1;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>連絡フォーム</title>
</head>
<body>
<?php
if ($_SESSION["step"] == 1) {
if (count($errmes) != 0) {
?>
<ul style="font-width: bolder; color: #f00; list-style: none;">
<?php
foreach ($errmes as $e) {
echo "<li>".$e."</li>";
}
?>
</ul>
<?php
}
?>
<form method="POST" action="/contact.php">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<table>
<tbody>
<tr>
<td>お名前 (必須):</td>
<td><input placeholder="山田 太郎" required="" name="name" type="text" value="<?= $reqvals["name"] ?>" /></td>
</tr>
<tr>
<td>お名前 (かな) (必須):</td>
<td><input placeholder="やまだ たろう" required="" name="kana" type="text" value="<?= $reqvals["kana"] ?>" /></td>
</tr>
<tr>
<td>御社又は関連サイトのURL:</td>
<td><input placeholder="https://076.moe/" name="url" type="text" value="<?= $optvals["url"] ?>" /></td>
</tr>
</tbody>
</table>
<button>確認画面へ</button>
</form>
<?php
} else if ($_SESSION["step"] == 2) {
?>
<form method="POST" action="/contact.php">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
お名前 (必須): <?= $reqvals["name"] ?><br />
お名前 (かな) (必須): <?= $reqvals["kana"] ?><br />
御社又は関連サイトのURL: <?= $optvals["url"] ?><br /><br />
<button>送信する</button>
</form>
<?php
} else {
?>
<p>不明なエラー。</p>
<?php
}
?>
</body>
</html>
```
結果:\
[![](https://ass.technicalsuwako.moe/anzenform1.png)](https://ass.technicalsuwako.moe/anzenform1.png)
[![](https://ass.technicalsuwako.moe/anzenform2.png)](https://ass.technicalsuwako.moe/anzenform2.png)
[![](https://ass.technicalsuwako.moe/anzenform3.png)](https://ass.technicalsuwako.moe/anzenform3.png)
[![](https://ass.technicalsuwako.moe/anzenform4.png)](https://ass.technicalsuwako.moe/anzenform4.png)
[![](https://ass.technicalsuwako.moe/anzenform5.png)](https://ass.technicalsuwako.moe/anzenform5.png)
[![](https://ass.technicalsuwako.moe/anzenform6.png)](https://ass.technicalsuwako.moe/anzenform6.png)
ねぇねぇー!\
簡単でしょー!
以上