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