<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>ジャック・テイラー</title><link>https://jacktaylor.tech/ja/</link><description>Recent content on ジャック・テイラー</description><generator>Hugo</generator><language>ja</language><managingEditor>jack@jacktaylor.tech (Jack Taylor)</managingEditor><webMaster>jack@jacktaylor.tech (Jack Taylor)</webMaster><lastBuildDate>Sun, 12 Apr 2026 00:47:49 +0900</lastBuildDate><atom:link href="https://jacktaylor.tech/ja/index.xml" rel="self" type="application/rss+xml"/><item><title>10万のWordPressサイトにSQLインジェクションを見つけた話</title><link>https://jacktaylor.tech/ja/2026/04/10%E4%B8%87%E3%81%AEwordpress%E3%82%B5%E3%82%A4%E3%83%88%E3%81%ABsql%E3%82%A4%E3%83%B3%E3%82%B8%E3%82%A7%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%A6%8B%E3%81%A4%E3%81%91%E3%81%9F%E8%A9%B1/</link><pubDate>Sun, 12 Apr 2026 00:47:49 +0900</pubDate><author>jack@jacktaylor.tech (Jack Taylor)</author><guid>https://jacktaylor.tech/ja/2026/04/10%E4%B8%87%E3%81%AEwordpress%E3%82%B5%E3%82%A4%E3%83%88%E3%81%ABsql%E3%82%A4%E3%83%B3%E3%82%B8%E3%82%A7%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%A6%8B%E3%81%A4%E3%81%91%E3%81%9F%E8%A9%B1/</guid><description>&lt;p&gt;最近、10万以上のサイトで有効化されていたWordPress検索プラグイン&lt;a href="https://www.relevanssi.com/"&gt;Relevanssi&lt;/a&gt;に、認証不要の&lt;a href="https://ja.wikipedia.org/wiki/SQL%E3%82%A4%E3%83%B3%E3%82%B8%E3%82%A7%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3"&gt;SQLインジェクション&lt;/a&gt;を報告しました。このバグが特に面白かった理由は二つあります。ひとつは、数値のタームIDにしか見えない入力に余分なSQLを載せられる型の取り違えがあったこと。もうひとつは、最初のクエリの制約を回避するために、ひとつのSQLインジェクションのペイロードを別のSQLインジェクションの中に忍ばせるという手口が使えたことです。&lt;/p&gt;
&lt;p&gt;この記事では、実際のコードパスをたどりながら、なぜこのバグが悪用可能だったのか、そして&lt;code&gt;cats&lt;/code&gt;と&lt;code&gt;tags&lt;/code&gt;の検索パラメータを使ってどうやって実用的なブラインドSQLインジェクションにしたのかを説明します。&lt;/p&gt;
&lt;p&gt;Relevanssiは、WordPress標準の検索機能をより高機能な検索エンジンに置き換えるプラグインです。関連度の高い順に並べたり、追加のフィルタリングをしたりできるのですが、今回のバグが公開クエリパラメータ経由で到達可能だったのは、その追加の検索機能のおかげでした。&lt;/p&gt;
&lt;h2 id="脆弱な入力"&gt;脆弱な入力&lt;/h2&gt;
&lt;p&gt;RelevanssiはWordPress検索を拡張していて、検索結果を絞り込むための追加クエリパラメータをいくつか受け付けます。そのうち二つが&lt;code&gt;cats&lt;/code&gt;と&lt;code&gt;tags&lt;/code&gt;で、&lt;a href="https://developer.wordpress.org/themes/classic-themes/basics/categories-tags-custom-taxonomies/#categories"&gt;カテゴリー&lt;/a&gt;と&lt;a href="https://developer.wordpress.org/themes/classic-themes/basics/categories-tags-custom-taxonomies/#tags"&gt;タグ&lt;/a&gt;で絞り込むために使われます。&lt;/p&gt;
&lt;p&gt;カテゴリーの場合、プラグインは&lt;code&gt;cats&lt;/code&gt;パラメータを読み取り、カンマで分割し、その結果をタームIDとして保存します。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;query_vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;query_vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;is_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;get_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;relevanssi_cat&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$tax_query&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="s1"&gt;&amp;#39;taxonomy&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;category&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="s1"&gt;&amp;#39;field&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;term_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="s1"&gt;&amp;#39;terms&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$cat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="s1"&gt;&amp;#39;operator&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;IN&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;tags&lt;/code&gt;パラメータも同じような形で処理されます。どちらの場合も、プラグインはタームIDを扱っているつもりで、後続のコードもその前提に依存しています。&lt;/p&gt;
&lt;h2 id="どこでおかしくなるのか"&gt;どこでおかしくなるのか&lt;/h2&gt;
&lt;p&gt;バグが出るのはもう少し後で、Relevanssiがtaxonomy queryを処理し、ユーザー入力のターム値をterm taxonomy IDに変換しようとする部分です。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$terms_parameter&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;get_term_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$taxonomy&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$term&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;ctype_digit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;strval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="nv"&gt;$numeric_terms&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$term&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;term_id&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;slug&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="nv"&gt;$names&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#39;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;esc_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="nv"&gt;$numeric_terms&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="s1"&gt;&amp;#39;numeric_terms&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$numeric_terms&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="s1"&gt;&amp;#39;term_in&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$names&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重要なのは最後の&lt;code&gt;else&lt;/code&gt;です。&lt;code&gt;get_term_by()&lt;/code&gt;がタームオブジェクトを返した場合、元の値が数値として検証されないまま&lt;code&gt;numeric_terms&lt;/code&gt;配列に追加されます。&lt;/p&gt;
&lt;p&gt;一見すると大したことがないように見えますが、WordPressがIDによるターム検索をどう処理するかを見ると話が変わります。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$field&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ID&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$field&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;term_id&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$field&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;get_term&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$taxonomy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$filter&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;is_wp_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$term&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$term&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="nv"&gt;$term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$term&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;検索前に値が整数へキャストされています。つまり、&lt;code&gt;1foo&lt;/code&gt;のような文字列は検索時にはタームID &lt;code&gt;1&lt;/code&gt; として扱われ、もしID 1のタームが存在すれば検索は成功します。しかし&lt;code&gt;numeric_terms&lt;/code&gt;に追加されるのは元の文字列である&lt;code&gt;1foo&lt;/code&gt;です。&lt;/p&gt;
&lt;p&gt;これは、一見もっともらしく見えるコードでもバグにつながるPHP特有の挙動のひとつです。PHP 8でも警告なしに&lt;code&gt;1foo&lt;/code&gt;のような文字列がこっそり&lt;code&gt;1&lt;/code&gt;になる、というのは、型に関する前提が危険であることをよく示しています。Relevanssiは、本来は汚染された文字列である元の値を、正しい数値IDとして検証済みであるかのように扱ってしまっていました。&lt;/p&gt;
&lt;h2 id="インジェクションポイント"&gt;インジェクションポイント&lt;/h2&gt;
&lt;p&gt;最終的に、それらの値はこのSQLクエリに連結されます。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$numeric_terms&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;term_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$term_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$numeric_terms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$term_in&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$row_taxonomy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sanitize_text_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;taxonomy&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$tt_q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;SELECT tt.term_taxonomy_id
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;			 FROM &lt;/span&gt;&lt;span class="si"&gt;$wpdb-&amp;gt;term_taxonomy&lt;/span&gt;&lt;span class="s2"&gt; AS tt
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;			 LEFT JOIN &lt;/span&gt;&lt;span class="si"&gt;$wpdb-&amp;gt;terms&lt;/span&gt;&lt;span class="s2"&gt; AS t ON (tt.term_id=t.term_id)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;			 WHERE tt.taxonomy = &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;$row_taxonomy&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39; AND t.&lt;/span&gt;&lt;span class="si"&gt;$type&lt;/span&gt;&lt;span class="s2"&gt; IN (&lt;/span&gt;&lt;span class="si"&gt;$term_in&lt;/span&gt;&lt;span class="s2"&gt;)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nv"&gt;$term_tax_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$wpdb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;get_col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$tt_q&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ここでは、&lt;code&gt;$term_in&lt;/code&gt;にカンマ区切りの数値IDしか入っていない前提になっています。しかし元の値が整数に変換されていないため、攻撃者が制御するSQLを&lt;code&gt;IN (...)&lt;/code&gt;句に紛れ込ませることができます。&lt;/p&gt;</description></item><item><title>100日間の機械学習</title><link>https://jacktaylor.tech/ja/2018/03/100%E6%97%A5%E9%96%93%E3%81%AE%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92/</link><pubDate>Sat, 24 Mar 2018 10:18:00 +0900</pubDate><author>jack@jacktaylor.tech (Jack Taylor)</author><guid>https://jacktaylor.tech/ja/2018/03/100%E6%97%A5%E9%96%93%E3%81%AE%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92/</guid><description>&lt;p&gt;2017年8月、初めてソフトウェア開発の仕事についたときに、あることを誓いました。&lt;/p&gt;
&lt;p&gt;IT系仕事につくことが数年の努力の成果でした。日本語学習、プログラミングの勉強、&lt;a href="https://jacktaylor.tech/ja/2017/03/daini-gengo-de-fe-shiken/"&gt;基本情報技術者試験の準備&lt;/a&gt;、そして日本での就職活動：このハードルを全部乗り越えないとできなかったことです。転職した時点は新しいことばかり：新しい職種、新しい環境、初めての第二言語での仕事。慣れるまでには時間がかかることを知っていました。一方で、慣れてきたころに罠があることも知っていました。慣れてきた自分に満足してしまうと成長が止まり、なりたい開発者にはなれなくなってしまうのです。&lt;/p&gt;
&lt;p&gt;誓ったことはこうです：仕事に慣れたら必ず学び続けます。&lt;/p&gt;</description></item><item><title>Fedora 25にHugoをインストール</title><link>https://jacktaylor.tech/ja/2017/04/fedora-25-ni-hugo-wo-install/</link><pubDate>Sat, 22 Apr 2017 13:22:38 +0900</pubDate><author>jack@jacktaylor.tech (Jack Taylor)</author><guid>https://jacktaylor.tech/ja/2017/04/fedora-25-ni-hugo-wo-install/</guid><description>&lt;p&gt;今回の記事は&lt;a href="https://getfedora.org/"&gt;Fedora&lt;/a&gt;リナックスに静的ウェブサイトジェネレーター&lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt;のインストールについてです。Hugo（発音：ヒューゴ）は&lt;a href="http://octopress.org/"&gt;Octopress&lt;/a&gt;や&lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;と同様、コンテンツを&lt;a href="https://ja.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt;で記載し、タイトルなどの設定を決め、テーマを選ぶだけで簡単にウェブサイトを作れます。そしてWordpressなどと違って出来上がるサイトはデータベースを使わないため、サイトのセキュリティと速さが抜群。&lt;/p&gt;
&lt;p&gt;Hugoは機能豊富で処理スピードが恐ろしく早い、本当に優れているツールです。ただ、現時点ではHugoの&lt;a href="https://ja.wikipedia.org/wiki/RPM_%28%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88%29"&gt;RPM&lt;/a&gt;がないので、&lt;a href="https://getfedora.org/"&gt;Fedora&lt;/a&gt;などのRPMディストリを使っている人にインストールが困難かもしれません。&lt;/p&gt;
&lt;p&gt;私の場合は色んなインストールの仕方（非公式RPM、&lt;a href="https://snapcraft.io/"&gt;snap&lt;/a&gt;、ソースコードからコンパイル）を検討したけど、なかなか最新バージョンを簡単にインストールする方法はありませんでした。調べていたときは何度も&lt;a href="https://www.ubuntu.com/"&gt;Ubuntu&lt;/a&gt;でできる&lt;code&gt;sudo apt-get install hugo&lt;/code&gt;を「いいな〜」と思いました。&lt;/p&gt;
&lt;p&gt;でもありがたいことに、結局答えが簡単でした。&lt;/p&gt;</description></item><item><title>第二言語でFE試験</title><link>https://jacktaylor.tech/ja/2017/03/daini-gengo-de-fe-shiken/</link><pubDate>Fri, 24 Mar 2017 17:14:37 +0900</pubDate><author>jack@jacktaylor.tech (Jack Taylor)</author><guid>https://jacktaylor.tech/ja/2017/03/daini-gengo-de-fe-shiken/</guid><description>&lt;p&gt;去年の5月に&lt;a href="https://ja.wikipedia.org/wiki/%E5%9F%BA%E6%9C%AC%E6%83%85%E5%A0%B1%E6%8A%80%E8%A1%93%E8%80%85%E8%A9%A6%E9%A8%93"&gt;基本情報技術者試験&lt;/a&gt;（FE試験）を合格することができました。&lt;/p&gt;
&lt;h4 id="おめでとうございますfe試験ってどんな試験でしたっけ"&gt;おめでとうございます！FE試験ってどんな試験でしたっけ？&lt;/h4&gt;
&lt;p&gt;一言でいうと、ITシステムに関する知識やスキル全般を対象とする国家試験です。&lt;/p&gt;
&lt;h4 id="じゃプログラミングとかを勉強するの"&gt;じゃ、プログラミングとかを勉強するの？&lt;/h4&gt;
&lt;p&gt;ITシステムといえばプログラミングなんだけど、システムを作るのに必要なものはプログラミング以外にもたくさんあります。なので、プログラミングが試験範囲の本の一部です。他に２進数や論理回路、CPUとメモリ、OSの仕組み、ファイルやデータベース、ネットワーク、セキュリティ、プロジェクトマネジメント、システム構成…内容がさまざまです。経営戦略や財務会計だって試験に出てきます。システム開発の部にプログラミング以外にも結構ありますね（たとえばテストのしかたとか）。&lt;/p&gt;
&lt;h4 id="え多くないですか"&gt;え、多くないですか？&lt;/h4&gt;
&lt;p&gt;本当に多かったですね（笑）。こういうような全部を対象とする試験が西洋になかなかありませんね。でもそのおかげでたくさん新しいものを覚えて、結構ためになったと思います。&lt;/p&gt;</description></item><item><title>プロフィール</title><link>https://jacktaylor.tech/ja/page/about/</link><pubDate>Tue, 21 Mar 2017 22:00:00 +0900</pubDate><author>jack@jacktaylor.tech (Jack Taylor)</author><guid>https://jacktaylor.tech/ja/page/about/</guid><description>&lt;p&gt;私のブログへようこそ！札幌に住んでいるソフトウェアエンジニアのジャック・テイラーです。趣味はWikimedia、機械学習とPythonです。どうぞよろしくおねがいします。(^-^)&lt;/p&gt;</description></item></channel></rss>