tag:blogger.com,1999:blog-36895161249532690652024-03-13T20:11:23.864+09:00徳丸浩の日記[PR]<a href="https://en-gage.net/eg-secure_career/">EGセキュアソリューションズ株式会社はエンジニアを募集しています</a>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.comBlogger250125tag:blogger.com,1999:blog-3689516124953269065.post-14810783781439059432023-04-03T10:52:00.000+09:002023-04-03T10:52:01.212+09:002023年4月においてクリックジャッキング未対策のサイトはどの条件で被害を受けるか<h2 id="サマリ">サマリ</h2>
<p>CookieやlocalStorage等でセッション管理しているウェブサイトがクリックジャッキング対策していない場合、どの条件で被害を受けるかを説明する。SameSite属性のないCookieでセッション管理しているウェブサイトは、主要ブラウザのデフォルト設定ではクリックジャッキングの影響を受けない。一方、loaclStorageにトークン類を格納するウェブサイトでは、Google Chrome等のブラウザでクリックジャッキングの影響がある。また、ブラウザの設定を変更した場合の影響についても説明する。</p>
<h2 id="クリックジャッキングとは">クリックジャッキングとは</h2>
<p>クリックジャッキングとは、一言で説明すると「ウェブサイト利用者に意図しないクリック(タップ)をさせる」攻撃です。ウェブサイト上で意図しないクリックを勝手にさせられると、重大な結果になる場合があります。例えば、<a href="https://twitter.com/intent/tweet?text=%E5%A4%A7%E6%97%A5%E8%88%AA%E7%A9%BAXYZ%E4%BE%BF%E3%81%AB%E7%88%86%E7%99%BA%E7%89%A9%E3%82%92%E3%82%BB%E3%83%83%E3%83%88%E3%81%97%E3%81%9F%E3%80%81%E3%81%99%E3%81%90%E5%BC%95%E3%81%8D%E8%BF%94%E3%81%9B%0d%0a%E2%80%BB%E3%81%93%E3%82%8C%E3%81%AF%E3%83%86%E3%82%B9%E3%83%88%E3%81%A7%E3%81%99%E3%80%82%E7%88%86%E7%A0%B4%E7%89%A9%E3%81%AF%E3%82%BB%E3%83%83%E3%83%88%E3%81%97%E3%81%A6%E3%81%84%E3%81%BE%E3%81%9B%E3%82%93">このURL</a>を閲覧すると、以下のようにTwitterにて「犯行予告を投稿する」画面になり、「ツイートする」を押すと、押した人のアカウントで犯行予告をツイートする結果になります。Twitterのこの機能はウェブインテントと呼ばれます。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjapYkubUpwP5-rxGZ8gvsW--c0rxk0gqBhoSxyrYnHKGiTpRJwLMc65K39UhkVua1swwiE4KvHvQaV0GEYk_Kd5qKmVVbdh6MUbpYD7uzlTUKaF8S1cj-_QEPJ2myrEsb5-ZiESQiBH9d6F6giHdilvA8Y9WXYonopkVp2lnoRlyW-FKvilHDihw/s843/clickjacking-tweet.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="435" data-original-width="843" height="330" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjapYkubUpwP5-rxGZ8gvsW--c0rxk0gqBhoSxyrYnHKGiTpRJwLMc65K39UhkVua1swwiE4KvHvQaV0GEYk_Kd5qKmVVbdh6MUbpYD7uzlTUKaF8S1cj-_QEPJ2myrEsb5-ZiESQiBH9d6F6giHdilvA8Y9WXYonopkVp2lnoRlyW-FKvilHDihw/w640-h330/clickjacking-tweet.png" width="640" /></a></div><p>このような拙い「攻撃」だと、被害者は画面内容を見た結果ツイートボタンは押さないわけですが、これにiframeを用いた仕掛けを組み合わせて「知らない間にボタンを押させる攻撃」がクリックジャッキングです。具体的には、iframeを用いて、下図のようにボタンを押させるための罠と、Twitter等の掲示板の画面を重ねます。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh66iYfj6zwKETjkRZM5G6xa9-CxyNowA0rHa7CBEuCU--YEUOa8BoehAV-iok-WutMZehc2GP4xWeqj3LkszxhY_g8dYs9xaR2qV6798gsrlaJY-BTB7CXOpTKlb8bzpD8NKmFaZICeb7t3IWn8dLWp-MCgVN3H6yVdDK6Yx4iXHKaGYBfFDLaDA/s532/clickjacking-mechanism.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="198" data-original-width="532" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh66iYfj6zwKETjkRZM5G6xa9-CxyNowA0rHa7CBEuCU--YEUOa8BoehAV-iok-WutMZehc2GP4xWeqj3LkszxhY_g8dYs9xaR2qV6798gsrlaJY-BTB7CXOpTKlb8bzpD8NKmFaZICeb7t3IWn8dLWp-MCgVN3H6yVdDK6Yx4iXHKaGYBfFDLaDA/w640-h238/clickjacking-mechanism.png" width="640" /></a></div><p>この際、掲示板を手前に配置してCSS設定により透明にします。一方罠サイトは奥側に配置します。すると、見た目上は罠サイトだけが見えますが、ボタンをクリックすると手前側の掲示板のボタンを押したことになり、ログイン中の利用者のアカウントで意図しない投稿がされます。これがクリックジャッキングです。現実のTwitterはクリックジャッキング対策がされているので、この攻撃は成立しません。</p><p>この例では「意図しない投稿」でしたが、これ以外に、設定変更や退会など「クリックだけでできる操作」全てで影響がありえます。</p>
<p>クリックジャッキング対策には、以下のようなレスポンスヘッダを出力することで行われます。</p>
<pre><code><blockquote>Content-Security-Policy: frame-ancestors 'none';
</blockquote></code></pre>
<p>上図はCSPを用いた対策ですが、伝統的なX-Frame-Optionsヘッダを用いた対策も有効です。</p>
<p>しかしながら、上記対策を<strong>していない</strong>場合でも、モダンブラウザでは、iframe内のコンテンツからCookieおよびlocalStorageへのアクセスが制御されていて、クリックジャッキング攻撃ができないケースが多くなっています。<br />
本稿では、モダンなブラウザの利用者がクリックジャッキング被害を受ける条件について説明します。</p>
<h2 id="検証方法">検証方法</h2>
<p>今回は、クリックジャッキングの影響の有無を、iframe内のウェブページがCookieやlocalStorageの値を受け取れるかどうかで判断することにしました。もしもiframe内のページがCookieやlocalStorageにアクセスできない場合、ページはログイン状態にはならず、被害者のアカウントでの投稿や設定変更などはできないからです。<br />
下図は検証環境の模式図です。サイトAがクリックジャッキング脆弱なサイト、サイトBは罠サイトです。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ6ZgIv8j0AE5HHoLgbGW-lVpxT0Tp4U5q3S5J0pBZC56PufGy-No9NgRW3GipC3NCFTF5LZp23I-VCFPAtjPkYgAOJm12j4N_XeoEUK7VKJWC95FAFWsMDRRwnoMxkZ-G_hiEwpMP0ACBcG8ms37Pct9k2-oSW_Ap1IwifqB74Xrw8lpLd7LB6w/s579/clickjacking-test.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="172" data-original-width="579" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ6ZgIv8j0AE5HHoLgbGW-lVpxT0Tp4U5q3S5J0pBZC56PufGy-No9NgRW3GipC3NCFTF5LZp23I-VCFPAtjPkYgAOJm12j4N_XeoEUK7VKJWC95FAFWsMDRRwnoMxkZ-G_hiEwpMP0ACBcG8ms37Pct9k2-oSW_Ap1IwifqB74Xrw8lpLd7LB6w/w640-h190/clickjacking-test.png" width="640" /></a></div><p>サイトAのログインを模して、CookieやlocalStorage、sessionStorageをセットします(図の左側)。この後サイトBに遷移しますが、サイトB内にiframeがあり、その中にサイトAのページがあります(図の右側)。このiframe内のサイトAのページでCookie、localStorage、sessionStorageの値を表示して、これらの値を受け取れているかを確認します。</p><p>
CookieのSameSite属性は以下の3種類のものを用意しました。SameSite属性の意味については<a href="https://blog.tokumaru.org/2022/01/impact-conditions-for-no-CSRF-protection-sites.html">こちらの記事</a>を参照ください。</p>
<ul>
<li>SameSite属性なし</li>
<li>SameSite=None</li>
<li>SameSite=Lax</li>
</ul>
<p>SameSite属性なしは伝統的なセッション管理を想定しています。SameSite=Laxの場合、iframe内のコンテンツにCookieは送信されないはずですが、比較のためテスト条件に加えました。</p>
<h2 id="検証結果">検証結果</h2>
<p>検証結果を下表に示します。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDMjPKQxJEHGVgNfsp_sjnL4lIMQsulTS5Vf4lSQnj3ustOYt9ZKFs2UV6Q8g674l_NNwx30h5tiPkyEVFpUaHejAI3bDi0iGJbp0J6qGy4fWj4Blr91LQ-WcAdMV29KpBSe_YVP3zQEtt3eKW50WFM2W7sBKtupeCrUE12bIvlEu55eXLVZKN8Q/s712/clickjacking-result.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="518" data-original-width="712" height="466" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDMjPKQxJEHGVgNfsp_sjnL4lIMQsulTS5Vf4lSQnj3ustOYt9ZKFs2UV6Q8g674l_NNwx30h5tiPkyEVFpUaHejAI3bDi0iGJbp0J6qGy4fWj4Blr91LQ-WcAdMV29KpBSe_YVP3zQEtt3eKW50WFM2W7sBKtupeCrUE12bIvlEu55eXLVZKN8Q/w640-h466/clickjacking-result.png" width="640" /></a></div><p>結果の要約は下記の通りです。</p>
<ul>
<li>SameSite属性のないCookieでセッション管理しているサイトは全てのブラウザにてクリックジャッキングの被害を受けない</li>
<li>localStorageでセッション管理しているサイトは、Google Chrome、Edge、Opera(デスクトップ、Android)のユーザが被害を受ける</li>
<li>Brave、Firefox、Safariの利用者はいずれのケースでもクリックジャッキングの被害を受けない</li>
</ul>
<p>このように、ブラウザのセキュリティ機能の差により、クリックジャッキング被害の条件が変わっています。</p><p><br /></p>
<h2 id="各ブラウザの防御機能">各ブラウザのセキュリティ機能</h2>
<p>次に、各ブラウザのセキュリティ機能がどのようにクリックジャッキングを防いでいるかを説明します。</p>
<h3 id="chromium系の防御の基本はデフォルトsamesitelax">Chromium系ブラウザの防御はデフォルトSameSite=Lax</h3>
<p>CookieのSameSite属性にてLaxあるいはStrictを指定した場合、iframe内に置かれたページに対してはCookieは送信されません。これはすべてのブラウザに共通の仕様ですが、Chromium系のブラウザ(Google Chrome、Edge、Brave、Opera…)では、SameSite属性のないCookieはSameSite=Laxとして扱われます(<a href="https://web.dev/samesite-cookies-explained/">参考</a>)。<br />
このため、SameSite=Noneを明示的に設定していないCookieは、iframe内のページには送信されないことになります。SameSite属性は、もともとはCSRF対策を意図した機能と思われますが、クリックジャッキング対策にも効果があります。<br />
デフォルトSameSite=Laxの効果はCookieのみであるため、sessionStorageとlocalStorageには効果が及びません。</p>
<p>※注<br />
SameSite属性のないCookieは、Cookieが生成されてから2分以内であれば、クロスサイトのPOSTリクエストに付与されます(<a href="https://groups.google.com/a/chromium.org/g/blink-dev/c/AknSSyQTGYs/m/wRWG65IWBQAJ">参考</a>)が、iframeの場合はこの2分間の猶予期間はなく、ただちにSameSite=Laxとして扱われます。</p>
<h3 id="firefoxの防御機能はトータルクッキー保護(total-cookie-protection)">Firefoxの防御機能は包括的クッキー保護(Total Cookie Protection)</h3>
<p>Firefoxは2022年1月11日にデフォルトSameSite=Laxを導入しましたが、非互換を理由に直後にキャンセルされました(<a href="https://blog.tokumaru.org/2022/01/impact-conditions-for-no-CSRF-protection-sites.html">参考</a>)。現在でも、SameSiteのデフォルトは、SameSite=None相当です。<br />
一方、Firefoxは2022年6月14日に包括的 Cookie 保護(Total Cookie Protection:TCP)と呼ばれる機能を投入しました。TCPはセキュリティ目的というよりはプライバシー保護を目的としたものですが、クリックジャッキング防御にも効果があります。</p>
<p>下図は包括的Cookie保護(TCP)のイメージ図です(<a href="https://blog.mozilla.org/en/mozilla/firefox-rolls-out-total-cookie-protection-by-default-to-all-users-worldwide/">引用元</a>)。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCPf7lZQpzQ3KNX7xp8O2In0MTDflUnU5aDYRNd6-Gg8It7o48AKwh0ZxllKrwqd2clB8uD_kIIiHDAd28VrolUzwf0iJRCrS7aFx7746l19v4C7Nd5xkhQfWXaPD1WpDtdlmRtq81akm7duDPM1MHPr27dW53BBLmN9FKEqXYHvsJF-sxR6wh3w/s800/tcp-cookie-jar.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="400" data-original-width="800" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCPf7lZQpzQ3KNX7xp8O2In0MTDflUnU5aDYRNd6-Gg8It7o48AKwh0ZxllKrwqd2clB8uD_kIIiHDAd28VrolUzwf0iJRCrS7aFx7746l19v4C7Nd5xkhQfWXaPD1WpDtdlmRtq81akm7duDPM1MHPr27dW53BBLmN9FKEqXYHvsJF-sxR6wh3w/w640-h320/tcp-cookie-jar.png" width="640" /></a></div><p>従来は一つのCookieの入れ物(cookie jar)をすべてのサイトで共有していました(上図左)が、TCPが有効になるとサイト毎にcookie jarが独立するようになります(上図右)。これだけだと抽象的でわかりにくいので、サイトAがサイトB、サイトCのiframeにて表示されている様子を用いて説明します(下図)。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjNiPyWcUBOAD4d34Dq4G_Sgm5VW-l_KG9XW0VG5KMiQ25AYyaMt2d1wHIIO70yqBp_DnpFkJMa3uQQyNjXbBjiNtT8B0BlKpvdVXBUHwfLQKqnoIU3HvADvS6AHiOVizYrA5t56bmVF8GMNksablk9MgY5hwa1YM04r0Jye9_Nnz5-GiTRiRDgQ/s685/tcp-iframe.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="128" data-original-width="685" height="120" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjNiPyWcUBOAD4d34Dq4G_Sgm5VW-l_KG9XW0VG5KMiQ25AYyaMt2d1wHIIO70yqBp_DnpFkJMa3uQQyNjXbBjiNtT8B0BlKpvdVXBUHwfLQKqnoIU3HvADvS6AHiOVizYrA5t56bmVF8GMNksablk9MgY5hwa1YM04r0Jye9_Nnz5-GiTRiRDgQ/w640-h120/tcp-iframe.png" width="640" /></a></div><p>上図のように、サイトAのCookieは、単独で表示されている場合(左)、サイトBのiframe内で表示されている場合(中央)、サイトCのiframeで表示されている場合(右)で、それぞれ別の値となります。この機能はサードパーティCookieによるトラッキングを防ぐ目的で導入されたものですが、セッション管理のCookieもiframe内では分離・保護されるため、結果としてクリックジャッキングに対する防御として作用します。また、Cookieだけでなく、sessionStorageやlocalStorageもサイト毎に分離されます。</p>
<p>なお、実験による検証の結果、Braveブラウザにも包括的Cookie保護と同様の機能が実装されているようです(sessionStorageの挙動はFirefoxともSafariとも異なりますが、詳細は省略します)。BraveはChromiumベースのブラウザなので、デフォルトSameSite=Laxも実装されています。</p>
<h3 id="safariの防御機能はitp(intelligent-tracking-prevention)">Safariの防御機能はITP(Intelligent Tracking Prevention)</h3>
<p>Safari(WebKit)にはITP(Intelligent Tracking Prevention)というトラッキング防止機能があります。これにより、結果としてクリックジャッキングも防御されています。<br />
下図は、ITPによりiframe内のページのCookieがどうなるかを模式的に示しています。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHwD0307xSW07bYkQ6hrlJR0R7ddmtMADL2kEh4A6nSj_Pj-4jdFrQYu-LxQt71X5zPKmzmfTHrt5TsEViKBR-q18fwZCl1ASQ9i-AfVfYF6fFxwuxwyW1g5SOJfl-NGiug3sE6yGGa_6u8ZdNTj4iIopqh6yUr3RxQh_3OAdReUUkn3aTO6amqg/s444/itp-iframe.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="128" data-original-width="444" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHwD0307xSW07bYkQ6hrlJR0R7ddmtMADL2kEh4A6nSj_Pj-4jdFrQYu-LxQt71X5zPKmzmfTHrt5TsEViKBR-q18fwZCl1ASQ9i-AfVfYF6fFxwuxwyW1g5SOJfl-NGiug3sE6yGGa_6u8ZdNTj4iIopqh6yUr3RxQh_3OAdReUUkn3aTO6amqg/w400-h115/itp-iframe.png" width="400" /></a></div><p>サイトAでセットされたCookieは、サイトBにiframeで埋め込まれてもCookieは送信されず、またレスポンスのSet-Cookieも無視されます。すなわち、iframe内ではCookieの送信も受信もされないということになります。</p><p>
一方、sessionStorageとlocalStorageについては、FirefoxのTCPのような挙動になります。すなわち、サイト毎に分離された形になります。この挙動は<a href="https://webkit.org/tracking-prevention/">WebKitのドキュメント</a>に記載されています。</p>
<blockquote>
<p>Partitioned Third-Party Storage<br />
Third-party LocalStorage and IndexedDB are partitioned per first-party website and also made ephemeral.<br />
試訳: サードパーティのlocalStorageとindexedDBはファーストパーティのウェブサイト毎に分離され、またエフェメラル(一時的)なものになります。</p>
</blockquote>
<p>すなわち、localStorage等については、iframe等に埋め込まれていた場合、ファーストパーティのウェブサイト毎に別の名前空間になります。<br />
しかし、実験の結果はsessionStorageも上記の分離がなされていました。Safari 16.0まではsessionStorageは分離されておらず、Safari 16.1以降においてsessionStorageもlocalStorage同様のサイト毎の分離がされているようです。私はこの件についてのドキュメントを発見できていませんので、ドキュメントをご存知の方はぜひ教えてください。また、エフェメラルいうのは、ブラウザを終了すると削除されるという意味です。通常localStorageはブラウザを終了しても保持されますが、iframe等のサードパーティで保存されたlocalStorageはブラウザの終了と共に削除されます。一方、FirefoxのlocalStorageはサイト毎に分離されていますが、ブラウザを終了しても、localStorageの値は保持されます。</p><p><br /></p>
<h2 id="防御機能を無効化すると">防御機能を無効化するとどうなるか</h2>
<p>ここまで、ブラウザの提供する保護機能により、クリックジャッキング攻撃から防御される仕組みをご紹介しました。しかし、これらの機能は利用者が無効にすることができます。以下、利用者が保護機能を無効にした場合の影響を調べるため、まずはブラウザ毎にセキュリティ機能を無効化する方法を説明します。以下は、セキュリティを低下させる設定なので、検証以外では使用しないことをお勧めします。また試用した後は忘れずに戻しておいてください。</p>
<h3 id="google-chrome、edgeの場合">Google Chrome、Edgeの場合</h3>
<p>Google Chrome等のChromium系ブラウザにて、デフォルトSameSite=Laxを無効化するには、Windowsの場合レジストリのLegacySameSiteCookieBehaviorEnabledForDomainListにて、無効化するドメインのリストを指定します。<a href="https://www.chromium.org/administrators/policy-list-3/cookie-legacy-samesite-policies/">参考記事</a>によると、この設定は暫定的なもので将来変更される見込みです。設定例を下図に示します。この設定では、すべてのドメインにてデフォルトSameSite=Laxを無効化しています。危険な設定なので、検証用途以外には使わないでください。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic_IawAx4g4nss2DEa7Ij12Cu1NTzZTOpdnXn2SZez01wb9cPsxBxWlrH3mOXfui9kFbHpn-IMx7GmPmtdjK5fb0dDFKcCNcdXuTxGHtFaHbqFkfm-pt0OP54r0MJXag_yEYK35jsEthyLt1PuJRcYWxftHtlSlcP45ond1YZy2rcYDLDlbYcbjg/s739/samesite-legacy-regedit.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="187" data-original-width="739" height="162" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic_IawAx4g4nss2DEa7Ij12Cu1NTzZTOpdnXn2SZez01wb9cPsxBxWlrH3mOXfui9kFbHpn-IMx7GmPmtdjK5fb0dDFKcCNcdXuTxGHtFaHbqFkfm-pt0OP54r0MJXag_yEYK35jsEthyLt1PuJRcYWxftHtlSlcP45ond1YZy2rcYDLDlbYcbjg/w640-h162/samesite-legacy-regedit.png" width="640" /></a></div><br /><p>同じ方法でEdgeでも設定可能です。</p>
<h3 id="firefoxの場合">Firefoxの場合</h3>
<p>FirefoxのTCPを無効化するためには、設定|プライバシーとセキュリティから、強化型トラッキング防止機能から、カスタムを選択して、Cookieのチェックを外すか、その右のセレクトボックスで「クロスサイトトラッキングCookie(一番上)」を選択します。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJoGQ69HpiVmhaEA-yrLV7_XSVq_CYlOnFVTBFmuhaXuskF600vmTpYKyVSMfWLe8sT3waU8cZNDrCrNRMpd0nyBrUkLPVAKlj711aEItA-uZbmmDh-tEklWwvQ1RfrgJ-smD6P9A20MzikKXRdPmiD1SL_InXkKS6g3d8e4xyyv_0nIBAavA5LA/s813/firefox-settings.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="564" data-original-width="813" height="445" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJoGQ69HpiVmhaEA-yrLV7_XSVq_CYlOnFVTBFmuhaXuskF600vmTpYKyVSMfWLe8sT3waU8cZNDrCrNRMpd0nyBrUkLPVAKlj711aEItA-uZbmmDh-tEklWwvQ1RfrgJ-smD6P9A20MzikKXRdPmiD1SL_InXkKS6g3d8e4xyyv_0nIBAavA5LA/w640-h445/firefox-settings.png" width="640" /></a></div><p><br /></p>
<h3 id="safari">Safari</h3>
<p>SafariのITPを無効化するには、Safariの設定からプライバシーを選び、「サイト超えトラッキングを防ぐ」のチェックを外します。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE1XQTJ2flc2sBrCEGOYC8nYNYNfvJlhKqOJnM4-uDZ9nWiSu2ibP_9OiI9lS50IM5O3-GBu8at82erAQcyCVLoFYR3VNncHROr1o4W2ikCq65LLPATuYH7KQ_x45lbbzFLRDkbgF6eXrhgmZw_p-K0GG8t2kYw-_sIf9XmHkqSBbeS_6SBxoq7g/s700/macOS-Safari-3rdparty-cookie.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="117" data-original-width="700" height="106" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE1XQTJ2flc2sBrCEGOYC8nYNYNfvJlhKqOJnM4-uDZ9nWiSu2ibP_9OiI9lS50IM5O3-GBu8at82erAQcyCVLoFYR3VNncHROr1o4W2ikCq65LLPATuYH7KQ_x45lbbzFLRDkbgF6eXrhgmZw_p-K0GG8t2kYw-_sIf9XmHkqSBbeS_6SBxoq7g/w640-h106/macOS-Safari-3rdparty-cookie.png" width="640" /></a></div><p><br /></p>
<h3 id="結果">結果</h3>
<p>上記の設定変更をした結果を下表に示します。赤字で(*)付きで示した項は設定変更により条件が変わっていることを示します。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqjgEm7Me5gfm5oGyHUEUxFh_l8gbYY5CydL7pPjMZvY1FXN5bllgbdvfYF2nkNdYt9MsXmEFT3wY9eO5lvSogjbImX2NxWZeU9HAGW19vYwBfXaEjm6ZeGRWFaFzfqs0JOUyVLxEOG3ciDjnAZKy6f9I3eYQ-YnrSuFA_qOeZlI22bnb0rlVxew/s780/clickjacking-samesite-policy-change.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="218" data-original-width="780" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqjgEm7Me5gfm5oGyHUEUxFh_l8gbYY5CydL7pPjMZvY1FXN5bllgbdvfYF2nkNdYt9MsXmEFT3wY9eO5lvSogjbImX2NxWZeU9HAGW19vYwBfXaEjm6ZeGRWFaFzfqs0JOUyVLxEOG3ciDjnAZKy6f9I3eYQ-YnrSuFA_qOeZlI22bnb0rlVxew/w640-h178/clickjacking-samesite-policy-change.png" width="640" /></a></div><p><br /></p>
<h2 id="考察">考察</h2>
<p>前述の設定変更により、以下の状態となりました。</p>
<ul>
<li>ブラウザ側の変更により、ほぼ全ての条件でクリックジャッキングの影響を受けるようになる</li><li>FirefoxのTCP、およびSafariのITPはプライバシー強化のための機能だが、セキュリティの効果もある</li><li>SameSite=Lax設定はクリックジャッキング対策としても効果が高い</li>
</ul>
<p>各ブラウザの設定変更の中で、もっとも現実にありそうなものがSafariのITPの無効化です。「サイト超えトラッキングを防ぐ」でGoogle検索するとトップに表示されるのが、以下のYahoo! JAPANの記事です。</p>
<blockquote>
<p>iOS 11以降では、「サイト越えトラッキングを防ぐ」がオン(有効)になっている場合があります。<br />
「サイト越えトラッキングを防ぐ」がオンになっている場合、Cookie(クッキー)がSafari上に残らなくなります。<br />
Yahoo! JAPANでは、複数のサービスでCookieを使用しているため、「サイト越えトラッキングを防ぐ」をオンにしていると、サービス内の機能が限定されるなど、一部のサービスを利用できません。<br />
以下の手順を参考に、「サイト越えトラッキングを防ぐ」機能をオフにしてからYahoo! JAPANのサービスをご利用ください。<br />
<a href="https://support.yahoo-net.jp/SccYjcommon/s/article/H000012099">iPhone向けSafariで「サイト越えトラッキングを防ぐ」機能をオフにする</a>より引用</p>
</blockquote>
<p>しかし、当該の設定変更を行うと、Yahoo!以外のサイトでもセキュリティを弱くする結果になります。このページには以下のような記載もありますが、</p>
<blockquote>
<p>注意<br />
「サイト越えトラッキングを防ぐ」機能の設定変更は、お客様ご自身の責任において変更してください。</p>
</blockquote>
<p>「お客様ご自身の責任において変更」と書いてありますが、変更による影響は何も説明されていないため、この書き方はいかがなものかという感想を持ちました。</p><p><br /></p>
<h2 id="まとめ">まとめ</h2>
<ul>
<li>SameSite属性のないCookieによるセッション管理という伝統的なサイトはモダンなブラウザのデフォルト設定ではクリックジャッキング攻撃の影響はない</li>
<li>localStorageにトークン類を保存する実装の場合、Chromium系のブラウザ(Chrome、Edge、Opera)ではクリックジャッキング攻撃の影響がある</li>
<li>ブラウザのトラッキング機能を解除するとクリックジャッキング攻撃の影響を受けやすくなる</li>
<li>アプリケーション提供者は、ブラウザの設定によらずクリックジャッキングを防御するために、CSPやX-Frame-Optionsによりクリックジャッキング対策を実施すること</li><li>サイト運営者は、ブラウザの設定変更を促さないこと</li><li>サイト利用者は、ブラウザの設定を安易に変更しないこと</li>
</ul>
ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-9109360392478148182023-03-27T13:36:00.004+09:002023-03-31T10:45:38.485+09:00[書評] ハッキングAPI ―Web APIを攻撃から守るためのテスト技法<h2 id="サマリ">サマリ</h2>
<p><a href="https://amzn.to/42IrWx6">ハッキングAPI―Web APIを攻撃から守るためのテスト技法(2023年3月27日発売)</a>を読んだ。本書は、Web APIに対するセキュリティテストの全体像と具体的なテスト方法を記載している。ペンテスターは、APIの検出、APIエンドポイントの分析、攻撃(テスト)を行う必要があり、そのために必要な情報がすべて記載されている。また、実習のためのツールと「やられサイト」を複数紹介し、具体的なトレーニング方法を解説している。単にツールやサイトの使い方の説明にとどまらず、本格的なペネトレーションテストの考え方を説明している。<br>
本書の想定読者はAPIのペネトレーションテストを実施するペンテスター及びペンテスターを目指す人であるが、API開発者やウェブアプリケーション脆弱性診断員にとっても有益な内容を多く含む。</p>
<h2 id="重要事項説明">重要事項説明</h2>
<ul>
<li>本書の監修者の一人(洲崎俊氏)と評者は知人関係にある</li>
<li>評者が読んだ書籍はご恵贈いただいたものである</li>
<li>この記事のリンクにはアフィリエイトが含まれる</li>
</ul>
<h2 id="apiセキュリティテストとは何か">APIセキュリティテストとは何か</h2>
<p>まずは、本書の主題である「APIセキュリティテスト」を本書でどのように定義しているかを紹介しよう。以下は、本書p3からの引用である。</p>
<blockquote>
<p>APIセキュリティテストは、一般的なペネトレーションテストともWebアプリケーション診断とも異なります。APIが持つ攻撃対象領域(Attack Surface)の幅広さと複雑さのため、多くのペンテスターはAPIセキュリティテストを独自サービスとして位置づけています。</p>
</blockquote>
<p>通常のWebアプリケーション脆弱性診断でもAPIのテストは当然含まれるが、APIはウェブアプリケーションからだけ利用されるものではなく、IoT機器などからも呼び出される。この場合、IoTシステムとしてセキュリティテストあるいはAPI単体でのセキュリティテストが行われる。本書で説明されるAPIセキュリティテストは、脆弱性診断というよりはペネトレーションテストである。</p>
<p>APIのペネトレーションテストを業務で経験できるエンジニアは限られていると思うが、例えばバグバウンティプログラムに参加して、公開APIをテストするような機会は、意欲と技量さえあれば誰にでも与えらるし、本書はバグバウンティプログラムに参加するバグハンター向けの注意や心構えについても度々言及している。</p>
<h2 id="本書の構成">本書の構成</h2>
<p>ここで、本書の構成について紹介しよう。本書は4つの部、16の章で構成される。ここからも分かるように、本書は非常に広範囲のトピックについて扱っている。</p>
<h4 id="第1部-api-セキュリティの原理">第1部 API セキュリティの原理</h4>
<p>「第1部 API セキュリティの原理」では、後述の章立てからもわかるように。Web APIの基礎知識と、0章としてAPIテストのスコープ(範囲)、顧客からテスト許可を取得することの必要性などを説明している。2章 Web APIの解剖学では、RESTful APIに加えてGraphQLについても簡単な説明をしている。JSON/XML/YAML等のデータ形式、APIの認証方式(BASIC認証、APIキー、JWT、HMAC、OAuth2.0…)について説明する。REST APIおよびGraphQLを前提とすることから、Cookieによるセッション管理は本書では取り扱われておらず、そのためかCSRF脆弱性やCookieのSameSite属性に関する説明は割愛されている。</p>
<ul>
<li>0章 セキュリティテストへの準備</li>
<li>1章 Webアプリケーションの仕組み</li>
<li>2章 Web APIの解剖学</li>
<li>3章 一般的なAPI脆弱性</li>
</ul>
<p>3章の「一般的なAPI脆弱性」としては以下が紹介されている。概ね<a href="https://owasp.org/www-project-api-security/">OWASP API Security Top 10 2019</a>と重なっているが、3.1 情報漏えいと3.11 ビジネスロジックの欠陥は独自に追加したものである。</p>
<ul>
<li>3.1 情報漏えい</li>
<li>3.2 オブジェクトレベルの認可不備 (BOLA)</li>
<li>3.3 ユーザ認証の不備</li>
<li>3.4 過剰なデータ露出</li>
<li>3.5 リソース不足とレート制限</li>
<li>3.6 機能レベルの認可不備 (BFLA)</li>
<li>3.7 マスアサインメント</li>
<li>3.8 セキュリティ設定ミス</li>
<li>3.9 インジェクション</li>
<li>3.10 不適切な資産管理</li>
<li>3.11 ビジネスロジックの欠陥</li>
</ul>
<h4 id="第2部-apiテストラボの構築">第2部 APIテストラボの構築</h4>
<ul>
<li>4章 API ハッキングラボの構築</li>
<li>5章 脆弱なAPIラボ環境の準備</li>
</ul>
<p>第2部はAPIテストのための実習環境として、診断ツールと脆弱なAPI環境を準備する。診断ツールとしては、Burp Suite、Postman、Wfuzzが大活躍することになるが、これら以外にOWASP Amass、KiterunnerなどAPIを発見するためのツールも紹介されており興味深い。脆弱なAPI環境としては<a href="https://github.com/OWASP/crAPI">crAPI</a>など数種を紹介している。また、訳注の形で拙作の<a href="https://github.com/ockeghem/badtodo/">Bad Todo</a>も紹介頂いていた(APIではなく古典的なWebアプリケーションとして)。</p>
<h4 id="第3部-apiへの攻撃">第3部 APIへの攻撃</h4>
<p>第3部は、いよいよAPIの実際のテスト(攻撃)を説明する。第3部の章立ては以下の通りである。</p>
<ul>
<li>6章 APIの検出</li>
<li>7章 エンドポイント分析</li>
<li>8章 認証への攻撃</li>
<li>9章 ファジング</li>
<li>10章 認可への攻撃</li>
<li>11章 マスアサインメント</li>
<li>12章 インジェクション攻撃</li>
</ul>
<p>まず6章と7章でAPIの検出と分析を行うが、この2章は十分な紙面を使って詳しく説明されている。このあたりが、よくある「ハッカー入門」的な書籍とは異なり、本書が実務的な内容になっていることの表れのように思える。<br>
6章 APIの検出では、OSINT的な手法を活用してAPIを検出する。通常の脆弱性診断ではAPIエンドポイントの情報は開示されるので、このステップは省略されるが、本書でいう「ブラックボックス型テスト」では、APIの検出から始まるのでこのステップが必要になる。</p>
<p>7章のエンドポイント分析は、APIの正常系の仕様を把握するステップであり、ウェブアプリケーション脆弱性診断のサイト巡回(クローリング)に相当する。APIの仕様はドキュメント化されている場合も多いが、そうでない場合、アプリケーション経由のAPI通信をキャプチャすることになる。このステップは本書ではリバースエンジニアリングと表現されている。通信のキャプチャはBurp Suite等の診断プロキシを用いることもできるが、本書では主にPostmanを使用する。Postmanのプロキシ機能(評者は本書を読むまで知らなかった)を用いて、API仕様を明確にする。7章の後半は情報漏洩や過剰なデータ露出等、ファジングを伴わない脆弱性検査を行う。</p>
<p>8章から12章はさまざまな攻撃によるセキュリティテストである。8章の前半はパスワード認証に対する辞書攻撃やパスワードスプレイなど古典的な攻撃だが、Hydraのようなパスワード解析専用ツールではなく、Burp Suite IntruderやWfuzz等のファジング用のツールを使っている。パスワードスプレイの場合、ユーザIDのリストが必要になるが、6章で解説した偵察や7章の過剰なデータ露出脆弱性を利用できるとする。8章の後半は、トークンやJWTに対する攻撃で、ブルートフォースを含む攻撃手法を解説している。</p>
<p>9章で説明されるファジングとは異常なデータをAPIに送信して意図しない結果を引き起こすことである。ファジングにはワイドファジング(広く浅く)とディープファジング(狭く深く)があり、前者にはPostmanのCollection Runnerを用い、後者にはBurp Suite IntruderやWfuzzを用いている。評者はPostmanを使ってはいたが、もっぱらAPIの機能的なテストの目的であり、ファジングに用いる発想がなかったので勉強になった。</p>
<p>10章は認可への攻撃である。認可脆弱性に関しては特に目新しいものではないが、テストの観点とツールの効果的な使い方は興味深い。ここでも、Postmanを効果的に使用している。</p>
<p>11章はマスアサインメントに対する攻撃である。マスアサインメント脆弱性とは、主に更新系のAPIで、「変更できてはいけない項目を変更できる」問題であり、<a href="https://www.infoq.com/jp/news/2012/03/GitHub-Compromised/">過去のGithub</a>の事例が有名である。例えば、利用者が自身のメールアドレスやパスワードを変更できるのは問題ないが、テーブル上のadmin列をtrueに勝手に変更できたら大問題である。API呼び出しにadmin=trueを追加するだけで意図しないadmin列の更新ができる場合がマスアサインメント脆弱性である。Ruby on Rails 4以降ではStrong Parametersと言う機能によりマスアサインメントに対策している。マスアサインメントのテスト自体は簡単なのだが、問題は「変更できてはいけないパラメータ(先の例ではadmin)」をどう発見するかである。本章では、マスアサインメント可能なパラメータの探索についていくつかの方法を説明している。</p>
<p>12章はインジェクションということで、クロスサイトスクリプティング(XSS)やSQLインジェクションなどが含まれる。有名な脆弱性であるからか、この章の説明は簡潔なものである。12.4.1項で説明されるSQLインジェクション攻撃文字列は、あまり凝ったものではないし、本番環境で試すには危険なものが含まれていて評者はぎょっとした(p304)。だが、直後に訳者による適切なコラム「SQLインジェクションのテストを実施する際の注意と補足(pp305-306)」があり、評者は安心した。もっとも、コラム内の文字列「’||’」や「’+’」もMySQLの場合は危険なので、SQLインジェクション検査は厳重な注意が必要である(参考:<a href="https://www.slideshare.net/zaki4649/sql-35102177">とある診断員とSQLインジェクション</a>)。</p>
<h4 id="第4部-実世界におけるapi攻撃">第4部 実世界におけるAPI攻撃</h4>
<p>第4部は、「実世界におけるAPI攻撃」というテーマで、13章はバイパス技術の応用とレート制限テストとして、バリデーションやWAF、APIの利用制限を回避する技術といういかにもハッカー的なテーマ、14章は皆さん大好きGraphQLへの応用、15章では、APIの脆弱性が情報漏洩やバグバウンティでどのように発見されたかを紹介している。</p>
<ul>
<li>13章 バイパス技術の応用とレート制限テスト</li>
<li>14章 GraphQLへの攻撃</li>
<li>15章 データ侵害とバグバウンティ</li>
</ul>
<h2 id="本書のすごいところ">本書のすごいところ</h2>
<p>本書の眼目は、APIセキュリティテストの方法論に徹底しているところである。それは、APIを検出するところから始まる。種々の検出手法は、APIそのものを探すだけではなく、マスアサインメント可能なパラメータを探す場合にも応用できるもので、この種の解説は非常に貴重なものだ。<br>
また、テストに使うツールの説明も詳しい。筆者は主にBurp SuiteやPostmanなどの汎用的なツールを使い、Burp SuiteのIntruderやPostmanのCollection Runnerによりファジングだけでなく、パスワードに対する辞書攻撃やマスアサインメントのパラメータ探索にも用いる。その他にも多くのツールが紹介されているが、評者には、汎用ツールの使いこなしこそ勉強すべきだと思えた。<br>
さらには、テストに対する心構えや、方針の立て方など、現役のペンテスターならではの知見が学べるところも、本書ならではの価値といえる。</p>
<h2 id="本書で扱ってない内容">本書で扱ってない内容</h2>
<p>本書で扱っていない項目についても紹介しよう。<br>
まず、現在APIの主流であるRESTfulAPIはステートレスだからCookieによるセッション管理は行わないという主張から、Cookie関連の記載は一切ない。これに関連して、以下の項目もない。</p>
<ul>
<li>Cookieに関する説明(例えばSameSite属性を含め、Cookieの言及がない)</li>
<li>セッション管理(トークン類の保存場所はlocalStorageかCookieか等)</li>
<li>APIのCSRF(Content-Type、CORSとの関連を含むが、そもそもCSRFの言及がない)</li>
</ul>
<p>また、Web APIの基礎としてJSONなどの説明はあるが、同一オリジンポリシーやCORSの説明はない。<br>
XSSの話題は少し出ているが、Content-Typeの話題はない。例えば、以下のケースは、レスポンスボディの見かけ上はXSSだが、Content-Type: application/jsonなのでJavaScriptは実行されない。この種の話題は、他の本(例えば<a href="https://amzn.to/3JPgyXh">拙著</a>)で学ぶ必要がある。</p>
<blockquote><pre><code>HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 43
{"message": "<img src=x onerror=alert(1)>"}
</code></pre></blockquote>
<p>その他、触れられてないテーマの例として下記がある。</p>
<ul>
<li>JSONP(RESTfulAPIだから関係ない)</li>
<li>CSP(API側だから無関係というのは分かる)</li>
<li>X-Content-Type-OptionsやHSTSなどのレスポンスヘッダ(侵入とは直接関係がないからか?)</li>
<li>脆弱性の対策方法</li>
</ul>
<h2 id="注意点">注意点</h2>
<p>筆者は根っからのペンテスター気質という感じで、例えば、以下のような記載がある(p301)。</p>
<blockquote>
<p>プロバイダのAPIがコンテンツを追加したり、そのWebアプリケーションに変更を加える場合、脆弱性が存在しないか探すべきかもしれません。</p>
</blockquote>
<p>これに対しては、下記の訳注がついている。</p>
<blockquote>
<p>一般に、許可なくサードパーティAPIをテストすることは許されていないケースが多いため、注意が必要です。</p>
</blockquote>
<p>また、以下のような記載もある(p115)。</p>
<blockquote>
<p>筆者は、Webアプリケーションの存在を発見すると、すぐにNiktoでスキャンを行います。</p>
</blockquote>
<p>おそらく文脈的には「セキュリティテストの過程でWebアプリケーションを発見すると…」ということであろうが、Niktoも脆弱性診断ツールの一つではあるので、許可なくNiktoスキャンを行うことは問題がある。</p>
<h2 id="本書の眼目は訳者の知見にある">本書の眼目は訳者の知見にある</h2>
<p>本書は、本格的なAPIセキュリティテストの書籍の和訳というだけで価値が高いものであるが、次の点において、価値が倍加していると考える。</p>
<ul>
<li>原書の出版後に利用サイトやツールのバージョンアップ、サイトの閉鎖等に伴う利用法の変更や代替手段の説明があること</li>
<li>豊富な訳注と訳者コラムにより、原書の説明不足や、日本の読者になじみのない一般知識(例えばAcmeの意味)が補足されていること</li>
<li>訳注での参考情報の記載がとても豊富であり、追加の勉強のために役立つこと</li>
</ul>
<p>したがって、読者が英語に堪能であったとしても、それでもこの和訳を選択する価値は十分にある。また、参考リンクが豊富であることから、買うなら<a href="https://www.oreilly.co.jp/books/9784814400249/">電子版(PDF)</a>が圧倒的にお勧めである。評者はレビューを紙版で行ったが、電子版も購入した。<br>
翻訳はこなれていて、問題となるような箇所はなかった。</p>
<h2 id="本書の使い方">本書の使い方</h2>
<p>本書のお勧めの活用法は以下のようなものであろう。</p>
<ul>
<li>4章「APIハッキングラボの構築」と5章「脆弱なAPIラボ環境の準備」に従い実習環境を準備する</li>
<li>各章を読む</li>
<li>章末に示された実習を行う</li>
<li>本書を読むだけでは分からない箇所を訳注の参考文献に従って勉強する</li>
</ul>
<p>かなり時間がかかるはずだが、それだけの価値は必ずある。</p>
<h2 id="まとめ">まとめ</h2>
<ul>
<li>本書はAPIセキュリティテストに関する実践的なガイドである</li>
<li>ツールの紹介が多いが、読者はツールを能動的に活用する必要があり、そのための情報が本書にはある</li>
<li>訳注および訳者コラムは価値が非常に高く、それだけで訳書を選択する理由になる</li>
<li>買うなら絶対に<a href="https://www.oreilly.co.jp/books/9784814400249/">電子版(PDF)</a>をお勧めする</li>
</ul>
ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-86776641751060664582022-12-20T12:20:00.003+09:002022-12-20T12:20:46.101+09:00Ruby cgi gemのHTTPヘッダインジェクション脆弱性CVE-2021-33621の概要と発見の経緯<style>
div.code-frame {
background-color: #2f3232;
color: #e3e3e3;
padding: 0.1em 0 0.1em 1.2em;
}
</style>
<p>この記事は<a href="https://qiita.com/advent-calendar/2022/ruby">Ruby Advent Calendar 2022</a>の第20日の記事です。前日の記事は<a href="https://qiita.com/ydah">@ydah</a>さんによる「<a href="https://ydah.netlify.app/blog/posts/20221219/">RuboCopのバージョンを最新に保つ技術</a>」でした。</p>
<p>2022年11月22日に、Ruby cgi gemの<a href="https://nvd.nist.gov/vuln/detail/CVE-2021-33621">HTTPヘッダインジェクション脆弱性CVE-2021-33621</a>が発表がされました。</p>
<ul>
<li><a href="https://www.ruby-lang.org/en/news/2022/11/22/http-response-splitting-in-cgi-cve-2021-33621/">CVE-2021-33621: HTTP response splitting in CGI</a></li>
<li><a href="https://hackerone.com/reports/1204695">RubyのCGIライブラリにHTTPレスポンス分割脆弱性があり、秘密情報が漏洩する - HackerOne</a></li>
<li><a href="https://hackerone.com/reports/1204977">CGI::Cookieクラスにおけるセキュリティ上好ましくない仕様および実装 - HackerOne</a></li>
</ul>
<p>私は<a href="https://hackerone.com/reports/1204695">HackerOneを通じてこの脆弱性を報告</a>しました。この記事では、当該脆弱性の概要と発見の経緯などについて報告します。</p>
<ul>
<li><a href="#%E6%A6%82%E8%A6%81">概要</a></li>
<li><a href="#%E8%84%86%E5%BC%B1%E6%80%A7%E7%99%BA%E8%A6%8B%E3%81%AE%E7%B5%8C%E7%B7%AF">脆弱性発見の経緯</a></li>
<li><a href="#%E5%BD%B1%E9%9F%BF%E3%82%92%E5%8F%97%E3%81%91%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">影響を受けるアプリケーション</a></li>
<li><a href="#%E5%BD%B1%E9%9F%BF">影響</a></li>
<li><a href="#%E5%AF%BE%E7%AD%96">対策</a></li>
<li><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></li>
</ul>
<h2 id="概要">概要</h2>
<p>この脆弱性はRubyでCGIプログラムを記述際に用いられるcgi gem のheaderメソッドに内在するものです。PoCを以下に示します。このスクリプトは、クエリ文字列 num を受け取り、<a href="http://example.jp/?num=">http://example.jp/?num=</a><指定された数値>にリダイレクトするものです。</p>
<div class="code-frame"><pre><code>#!/usr/bin/ruby
require 'cgi'
cgi = CGI.new
num = cgi.params['num'][0]
print cgi.header({'status' => '302 Found', 'Location' => "http://example.jp/?num=#{num}"})
</code></pre></div>
<p>ここで、numとして下記を指定した場合、</p>
<div class="code-frame"><pre><code>num=3%0d%0aSet-Cookie:+SESSID%3d1234567890
</code></pre></div>
<p>%0d%0aは改行なので、以下のレスポンスヘッダが出力されます。</p>
<div class="code-frame"><pre><code>Status: 302 Found
Location: http://example.jp/?num=3
Set-Cookie: SESSID=1234567890
</code></pre></div>
<p>すなわち、リダイレクトする「ついで」に任意のクッキーをセットすることができます。</p>
<p>クッキのセット以外に、任意URLへのリダイレクトもできます。</p>
<div class="code-frame"><pre><code>num=3%0d%0aLocation:+http://trap.example.com/
</code></pre></div>
<p>この結果のレスポンスヘッダは下記となりますが、</p>
<div class="code-frame"><pre><code>Status: 302 Found
Location: http://example.jp/?num=3
Location: http://trap.example.com/
</code></pre></div>
<p>Apacheの場合複数のLocationヘッダがあった場合、最後のLocationヘッダのみがブラウザに返されるので、結果として、trap.example.comにリダイレクトされます。これにより、偽ログイン画面に遷移してパスワードを盗むなどができます。</p>
<p>また、以下のようにHTTPレスポンスボディを出力することもできます。</p>
<div class="code-frame"><pre><code>num=3%0d%0a%0d%0a<script>alert(1)</script>
</code></pre></div>
<p>レスポンスは下記となります。空行の後にJavaScriptコードが出力されていることが分かります。</p>
<div class="code-frame"><pre><code>Status: 302 Found
Location: http://example.jp/?num=3
<script>alert(1)</script>
</code></pre></div>
<p>ただ、このままだとリダイレクトが優先されるのでレスポンスボディは表示されません。リダイレクトを避けるテクニックとして、ヘッダインジェクションによりStatus 500等を出力する方法があります。このテクニックは<a href="https://twitter.com/harupuxa">はるぷ</a>さんに教えていただきました。</p>
<div class="code-frame"><pre><code>num=3%0d%0aStatus:+500%0d%0a%0d%0a<script>alert(2)</script>
</code></pre></div>
<p>このケースでの出力は下記となります。</p>
<div class="code-frame"><pre><code>Status: 302 Found
Location: http://example.jp/?num=3
Status: 500
<script>alert(1)</script>
</code></pre></div>
<p>Statusヘッダが2つありますが、前述のようにApacheは最後のStatusヘッダのみをブラウザに返すため、リダイレクトは無効となり、JavaScriptが実行されます。これはXSSと同等の脅威となります。</p>
<h2 id="脆弱性発見の経緯">脆弱性発見の経緯</h2>
<p>なぜ今どきRubyのcgi gemを調べたか、それにはちょっとした理由があります。
元々は、2020年1月にRuby on RailsのHTTPヘッダインジェクションに脆弱性を見つけて、<a href="https://hackerone.com/rails?type=team">HackerOne</a>にて報告したところ、これはRailsの脆弱性ではなくて、ウェブサーバーのPumaの脆弱性だということになり、<a href="https://github.com/puma/puma/security/advisories/GHSA-84j7-475p-hp8v">Puma側で修正された(CVE-2020-5247)</a>ことがきっかけです。</p>
<p>この脆弱性について、適当な場で紹介したいと思っていたところ、2021年6月18日に<a href="https://ginza-rails.connpass.com/event/213943/">銀座Rails#34@リンクアンドモチベーション</a>がオンライン開催されることがわかりましたので、CfPに応募して採択されました。</p>
<ul>
<li><a href="https://ginza-rails.connpass.com/event/213943/">Rails周辺におけるHTTPヘッダインジェクション脆弱性の動向</a></li>
</ul>
<p>この内容は私のYouTubeチャンネルにて公開しています。動画データを主催者様に提供いただきました。ありがとうございます。</p>
<iframe width="800" height="450" src="https://www.youtube.com/embed/cTFN7C44dBM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p>この発表(動画)では、冒頭のHTTPヘッダインジェクション入門をPHP(の古いバージョン)にて行っていますが、元々はRubyのCGIで説明するアイデアがありました。そのサンプルプログラムを作成する過程でRubyのcgi gemに脆弱性があることに気づきました。ゼロデイの脆弱性を説明に使うわけにもいかず、本番ではPHPの旧版の脆弱性を用いた形になりました。</p>
<p>さて、cgi gemに脆弱性があることは疑いのないものでしたが、「RubyでCGIを書いているサイトはどれくらいあるのだろう」という疑いもあり、gem側で修正しなくても、注意喚起してアプリケーション側での改修でもよいのではないかと思いました。とはいえ、当方が勝手に「ゼロデイ脆弱性」を公開することもよくありません。</p>
<p>このため<a href="https://twitter.com/hsbt/">Rubyコミッタ柴田さん</a>に相談したところ、チームで検討するのでHackerOne経由で申告してほしい(日本語でOK)旨の連絡をいただきましたので、HackerOneに投稿した内容が<a href="https://hackerone.com/reports/1204695">冒頭で紹介したもの</a>です。こちらは脆弱性と認められ、改修されることになりました。
また、CGI::Cookieにもセキュリティ上好ましくない実装を見つけましたので、<a href="https://hackerone.com/reports/1204977">追加の報告</a>として提出しました。</p>
<ul>
<li><a href="https://hackerone.com/reports/1204977">CGI::Cookieクラスにおけるセキュリティ上好ましくない仕様および実装 - HackerOne</a></li>
</ul>
<p>内容は、Cookieのname、path属性、domain属性設定時に外部由来の値を使うとインジェクションが可能というものでした。こちらも脆弱性と認められ、同時に修正されています。詳しくは<a href="https://hackerone.com/reports/1204977">HackerOneの報告</a>を御覧ください。</p>
<h2 id="影響を受けるアプリケーション">影響を受けるアプリケーション</h2>
<p>当脆弱性の影響を受けるアプリケーションは下記のとおりです。</p>
<ul>
<li>cgi.headerメソッドに外部由来の値をセットしている場合</li>
<li>CGI::CookieによりCookieのname、path属性、domain属性設定時に外部由来の値を使っている場合</li>
</ul>
<p>※ Cookieのpath属性およびdomain属性へのセミコロンを用いたインジェクションは、PHP-7.2以前にも存在していました(<a href="https://bugs.php.net/bug.php?id=69948">バグ#69948</a>)。</p>
<h2 id="影響">影響</h2>
<p>当脆弱性の影響は<a href="#%E6%A6%82%E8%A6%81">概要</a>で示したとおりですが、まとめると以下のようになります。</p>
<ul>
<li>任意のレスポンスヘッダの追加・改変
<ul>
<li>(その結果)クッキーの追加、改変</li>
<li>(同上)任意URLのリダイレクト</li>
</ul>
</li>
<li>レスポンスボディの追加・改変
<ul>
<li>(その結果)任意JavaScriptの実行(XSSと同等の脅威)</li>
</ul>
</li>
<li>Cookieのname、path、domainを通じた属性等の改変
<br/></li>
</ul>
<h2 id="対策">対策</h2>
<p>cgi gemを最新版(0.3.5, 0.2.2, 0.1.0.2)にアップデートすることで対策されます。</p>
<h3 id="ruby本体のバージョンアップ">Ruby本体のバージョンアップ</h3>
<p>Rubyのバージョン 3.1.3 / 3.0.5 / 2.7.7 にて最新のcgi gemを含む形で対応されています。
現時点では、主要Linuxディストリビューションはアップデートを提供していないようです。</p>
<ul>
<li><a href="https://access.redhat.com/security/cve/cve-2021-33621">Red Hat</a></li>
<li><a href="https://ubuntu.com/security/CVE-2021-33621">Ubuntu</a></li>
<li><a href="https://security-tracker.debian.org/tracker/CVE-2021-33621">Debian</a></li>
</ul>
<h3 id="cgi-gemのアップデート">cgi gemのアップデート</h3>
<p>前述のように、Linuxディストリビューション経由でのyumやaptによるアップデートでは本問題の対応はできませんが、gemコマンドによるアップデートは可能です。</p>
<div class="code-frame"><pre><code>$ sudo gem update cgi
</code></pre></div>
<p>ただし、Rubyのバージョンが2.7以降である必要があります(<a href="https://www.ruby-lang.org/ja/news/2019/12/25/ruby-2-7-0-released/">参考</a>)。Ruby 2.7未満を使用している場合は、Ruby自体をバージョンアップする(推奨)か、以下のアプリケーション側の対策を実施してください。</p>
<h3 id="アプリケーションでの対応">アプリケーションでの対応</h3>
<p>アプリケーション側で対策するには、以下を実施してください。</p>
<ul>
<li>cgi.headerメソッドに渡すパラメータを検証して改行(\rや\n)が混入しないようにする</li>
<li>CGI::Cookieに渡すname属性やdomain属性を検証する</li>
</ul>
<h2 id="まとめ">まとめ</h2>
<p>Ruby cgi gemのHTTPヘッダインジェクション脆弱性CVE-2021-33621について、脆弱性の内容と経緯について説明しました。今時RubyでCGIプログラムを記述するケースもほとんどないとは思いますが、HTTPヘッダインジェクションという今となってはレアな脆弱性が発見され、きちんと修正されたことをご報告したいと思い本稿を書きました。</p>
<p>脆弱性そのものは拙著「<a href="https://amzn.to/3YCd2pN">安全なWebアプリケーションの作り方</a>」に記載されている範囲のものですので、バグハンターを目指す皆さんは、「著名ソフトウェアだけどあまり使われていない機能」に着目すると、比較的容易に脆弱性が見つけられ、インターネットの安全にも貢献できるのではないでしょうか。</p>
<p>また、今回の脆弱性対応くださった<a href="https://twitter.com/hsbt/">柴田(@hsbt)</a>さん、<a href="https://github.com/mame">@mame</a>さん、<a href="https://github.com/nobu">@nobu</a>さんに感謝申し上げます。きちんと対応いただき、発見者としてうれしく思います。ありがとうございました。</p>
<iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//rcm-fe.amazon-adsystem.com/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=jisakucompile-22&language=ja_JP&o=9&p=8&l=as4&m=amazon&f=ifr&ref=as_ss_li_til&asins=4797393165&linkId=60cc213e456bdc7c43536790ce8ae34e"></iframe>
ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-40945555344776789922022-09-09T11:32:00.001+09:002022-10-01T11:02:49.812+09:00 PHPカンファレンス2022にてSPAセキュリティ超入門の話をします<p>今年もPHPカンファレンスにてトークさせていただくことになりまして、以下のようなお話をいたします。</p><p>日時:9月25日(日) 14:40〜15:40<br />場所:<a href="https://www.pio-ota.net/" target="_blank">大田区産業プラザPiO</a> および YouTube<br />費用:無料<br />講演タイトル:<a href="https://fortee.jp/phpcon-2022/proposal/934a985e-fc87-4cab-8e13-99ea5b1b5ce1" target="_blank">SPAセキュリティ超入門</a><br />申し込み: <a href="https://phpcon.connpass.com/event/255324/" target="_blank">connpass</a><br />アジェンダ:<br /></p><blockquote>SPA(Single Page Application)の普及が一層進んでおり、従来型のMPAを知らないウェブ開発者も生まれつつあるようです。SPA対応のフレームワークでは基本的な脆弱性については対策機能が用意されていますが、それにも関わらず、脆弱性診断等で基本的な脆弱性が指摘されるケースはむしろ増えつつあります。<br />本セッションでは、LaravelとReactで開発したアプリケーションをモデルとして、SQLインジェクション、クロスサイトスクリプティング、認可制御不備等の脆弱性の実例を紹介しながら、現実的な対策について紹介します。LaravelやReact以外のフレームワーク利用者にも役立つ説明を心がけます。</blockquote><p></p><p><br /></p><p>実は昨年も「SPAセキュリティ入門」というテーマでトークをしておりまして、今年はもう少し易しめということで「超入門」としました。</p><p>昨年の講演のスライドおよび動画は以下から参照ください。</p><p></p><ul style="text-align: left;"><li><a href="https://www.docswell.com/s/ockeghem/ZM6VNK-phpconf2021-spa-security" target="_blank">スライド</a></li><li><a href="https://www.youtube.com/watch?v=pc57hw6haXk" target="_blank">動画</a></li></ul><p></p><p>昨年は主にSPAにまつわるセッション管理に焦点をあてて、JWTによるセッション管理は是か非か、JWT等トークン類の保存場所はCookieかlocalStorageかという基本的な考え方(しばしば論争になる)のお話をしました。</p><p>今年はもう少し現実的なテーマとして、アジェンダにもあるように、LaravelによるサーバーサイドAPI、Reactによるフロントエンドという構成のSPAを題材として、以下のような「よくある」脆弱性がいかにして混入するか、およびその対策について説明します。</p><p></p><ul style="text-align: left;"><li>認可制御不備</li><li>SQLインジェクション</li><li>クロスサイトスクリプティング(XSS)</li></ul><p></p><p>これら脆弱性は、LaravelおよびReactを普通に使っていれば混入しないはずのものです。しかし脆弱性診断等ではこれら脆弱性はしばしば目にします。普通に作れば混入しないはずの脆弱性がなぜ混入するのか。私はその原因を追い続けていましたが、どうも従来型のアプリケーション(マルチページアプリケーション=MPA)では常識だった知識が、SPAでは伝承されていないのではないかと考えるに至りました。この要因ファクターXについて紹介するとともに、なぜ今基本的な脆弱性が多いのかを説明しようと思います。</p><p>なお、前述のようにファクターXは従来の常識ですので、「なぁんだ、そんなことか」となること請け負いですが、「そんなこと」レベルのことが問題になっているように考えています。</p><p>それでは、よろしくお願いいたします。</p>
<br/>
<br/>
<br/>
<p>無事終了しましたのでスライドと動画を添付いたします。</p>
<script async class="docswell-embed" src="https://www.docswell.com/assets/libs/docswell-embed/docswell-embed.min.js" data-src="https://www.docswell.com/slide/K2PPNK/embed" data-aspect="0.5625"></script><div class="docswell-link"><a href="https://www.docswell.com/s/ockeghem/K2PPNK-phpconf2022">SPAセキュリティ超入門 by @ockeghem</a></div>
<br/>
<iframe width="820" height="462" src="https://youtube.com/embed/jZ6sWyGxcCs" frameborder="0"></iframe>
ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-85658398864537296932022-07-04T10:39:00.004+09:002022-07-04T15:48:38.706+09:00メタップスペイメント不正アクセス事件の第三者報告書から攻撃の模様を読み解く<p>株式会社メタップスペイメントの運営する決済代行システムから約288万件のクレジットカード情報が漏洩した不正アクセス事件について、第三者委員会の報告書および経済産業省の行政処分(改善命令)があいついで公開されました。</p><p><a href="https://www.metaps-payment.com/company/report_metapspayment_20220701.pdf">第三者委員会調査報告書(公表版)</a><br /><a href="https://www.meti.go.jp/press/2022/06/20220630007/20220630007.html">クレジットカード番号等取扱業者に対する行政処分を行いました (METI/経済産業省)</a></p><p>本稿では、主に第三者委員会の調査報告書(以下「報告書」と表記)をベースとして、この事件の攻撃の様子を説明します。</p><h2 style="text-align: left;">システムの概要</h2><p>報告書にはシステム構成図やネットワーク構成図は記載されていないため、報告書の内容から推測によりシステムの構成を以下のように仮定しました。</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoRoU4S93AKz-5naDoa6TVdM4YRu9-LwOH2363XBeddZqqWRVxldYTB4qwycu0F14fuwxsnlWyvhNzkhz6B2E3_Aqoo13I8O_0_Ffe5Rk1QOo4loI0wPGascHxOYPm511zyBhC8du5jQp275Hm5wAMNdEixvKNyzWxNspGd1rE4a856PSBT6QHQA/s1600/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%891.PNG" style="clear: left; display: block; float: left; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="429" data-original-width="840" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoRoU4S93AKz-5naDoa6TVdM4YRu9-LwOH2363XBeddZqqWRVxldYTB4qwycu0F14fuwxsnlWyvhNzkhz6B2E3_Aqoo13I8O_0_Ffe5Rk1QOo4loI0wPGascHxOYPm511zyBhC8du5jQp275Hm5wAMNdEixvKNyzWxNspGd1rE4a856PSBT6QHQA/s1600/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%891.PNG" /></a></div>
<p>図中のサーバー名は報告書の記載に従っています。以下、概要を説明します。</p>
<table>
<tbody>
<tr><th>サーバ名</th><th>概要</th></tr>
<tr><td>A社アプリ</td><td>一般社団法人A 会員向け申込みフォーム<br />経産省改善命令では、「同社とコンビニ決済に係る契約を締結していた加盟店にサービスを提供するために開発、運用していたアプリケーション(以下「加盟店向けアプリ」という。)」が該当すると思われる</td></tr>
<tr><td>K管理画面</td><td>社内用決済管理画面。A社アプリのサーバーと同居していた(*1)</td></tr>
<tr><td style="white-space: nowrap;">決済サーバ</td><td>決済機能を提供するサーバー。攻撃対象ではなかった可能性があるが機能としては存在するので記載した</td></tr>
<tr><td>データベース</td><td>各システム共用のデータベース<br />報告書内にデータベースの種類は明記されていないが、用語集にMySQLの説明があるので、MySQLを使用していると推測</td></tr>
<tr><td style="white-space: nowrap;">復号化サーバ</td><td>報告書に登場する。暗号化されたクレジットカード情報を復号を提供するAPIと思われるが、不正アクセスに関する記載はない(*2)</td></tr>
</tbody></table>
<p>注 *1 : 報告書中には、A社アプリとK管理画面が同居していた旨は明記されていませんが、時系列表には、事故後の対応として「管理画面サーバから確認用サイト、A社アプリの分離(2022年1月7日)」を実施した旨が記載されているので、元々これらは同一サーバに同居していたと考えられます。</p><p>注 *2 : 報告書には「Web2系に暗号化されたカード情報に係る復号化サーバが配置されていた。Web2系には、決済システムも配置されており、そこで暗号化されたカード情報も管理されていた。」と記載されています。報告書中では「サーバー」、「サイト」、「画面」、「機能」の使い分けが明確でないため断言は難しいのですが、「Web2系」がサーバーを意味すると解釈すると、決済サーバと復号化サーバは同一マシンに同居していた可能性があります。</p><p><br /></p><h2 style="text-align: left;">時系列の流れ</h2><p>第三者委員会の報告書に加えて、<a href="https://ssl4.eir-parts.net/doc/6172/tdnet/2090707/00.pdf">メタップス社の2月28日づけリリース</a>も合わせて時系列の流れをまとめました。</p>
<table>
<tbody><tr><th>日時</th><th>出来事</th></tr>
<tr><td>2021年8月2日</td><td>メタップスペイメントの決済データセンターに対する不正アクセスが開始。K管理画面へのXSS攻撃か?</td></tr>
<tr><td>2021年8月31日</td><td>K管理画面に対する不正ログイン始まる</td></tr>
<tr><td style="white-space: nowrap;">2021年10月14日</td><td>A社アプリにSQLインジェクション攻撃始まる</td></tr>
<tr><td>2021年10月15日</td><td>A社アプリに、SQLインジェクションで得たパスワードによる不正ログイン</td></tr>
<tr><td>2021年10月19日<br />~10月27日</td><td>A社アプリのSQLインジェクションにより2万5千件のカードデータ窃取</td></tr>
<tr><td>2021年10月25日<br />~12月14日</td><td>K管理画面不正ログインによりカード番号全桁の検索が実行される(2万件程度)</td></tr>
<tr><td>2021年10月25日</td><td>メタップスペイメントがA社アプリのメンテナンス中にSQLインジェクション攻撃を発見</td></tr>
<tr><td>2021年10月27日</td><td>メタップスペイメントがA社アプリのSQLインジェクション対策を完了</td></tr>
<tr><td>2021年11月11日</td><td>A社アプリのアップロード機能よりバックドアが設置され、攻撃開始。最終的にカード情報データベースの全データが窃取されたとみられる(460,395件 + 2,415,750件)</td></tr>
<tr><td>2021年12月14日</td><td>アクワイアラ E 社からイベントペイで不正利用懸念の連絡</td></tr>
<tr><td>2021年12月16日</td><td>イベントペイでクレジットカード決済を停止</td></tr>
<tr><td>2022年1月6日</td><td>K管理画面の管理用サイトにBasic認証を追加</td></tr>
<tr><td>2022年1月7日</td><td>K管理画面サーバから確認用サイト、A社アプリの分離、A社用DBとペイメントDBの分離</td></tr>
<tr><td>2022年1月8日</td><td>A社アプリのサーバの分離を実施</td></tr>
<tr><td>2022年1月24日</td><td>メタップスペイメントがバックドアプログラムの存在を確認、削除</td></tr>
<tr><td>2022年1月24日</td><td>メタップスペイメントが不正アクセス被害を公表</td></tr>
<tr><td>2022年1月28日</td><td>管理用サイトのクロスサイト・スクリプティングに対応、Basic認証の追加</td></tr>
<tr><td>2022年2月28日</td><td>メタップスペイメントが不正アクセスによる情報流出を公表</td></tr>
</tbody></table>
<p>攻撃の流れの詳細については以下で説明します。</p><p><br /></p>
<h2 style="text-align: left;">1. K管理画面 のアカウント情報の窃取、不正ログイン</h2>
<p>報告書によると、以下の順で攻撃が行われました。</p><p></p><ol style="text-align: left;"><li>K管理画面のXSS脆弱性を悪用し、当管理画面のID・パスワードを窃取(報告書には時期の記載がないが、<a href="https://ssl4.eir-parts.net/doc/6172/tdnet/2090707/00.pdf">2022年2月28日づけメタップス社のリリース</a>記載の2021年8月2日の不正アクセスが該当するか?)</li><li>X氏アカウントによる不正ログイン(2021年8月31日~)</li><li>メタップス社内にてX氏アカウントのパスワードを変更(2021年9月末から2021年10月初旬までの間)これ以降X氏アカウントの不正ログインなし</li><li>Y氏アカウントにて不正ログイン(2021年10月6日以降)</li></ol><p></p><p>攻撃の模様を下図に示します。</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjIlhcTrOWiIwCQyBA6-Qdm958s5zg2que-j9i4Vsql3mjNxxhCwkRDz37d3wxJS2FMt15X3yFkkrURUntd2XXlgr4cRxW1lQ2VSjQ3X3Q8X_AT7rmneGyb4y1cdKfoR-3NUXx_62aBWDZCMFr-H37wWsLhl9AWknDz52d60m9X4B_PftD4BEZRA/s1600/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%892.PNG" style="clear: left; display: block; float: left; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="429" data-original-width="840" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjIlhcTrOWiIwCQyBA6-Qdm958s5zg2que-j9i4Vsql3mjNxxhCwkRDz37d3wxJS2FMt15X3yFkkrURUntd2XXlgr4cRxW1lQ2VSjQ3X3Q8X_AT7rmneGyb4y1cdKfoR-3NUXx_62aBWDZCMFr-H37wWsLhl9AWknDz52d60m9X4B_PftD4BEZRA/s1600/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%892.PNG" /></a></div>
<p>報告書によると、XSS攻撃を許してしまった理由は下記のとおりです。</p><p></p><ul style="text-align: left;"><li>自社で実施した脆弱性診断ではXSS脆弱性が検出されていたが、高危険度の脆弱性があるとPCI DSSの審査に通らないため、報告書を改ざんして脆弱性自体をなかったことにした</li><li>WAFが導入されていなかった(PCI DSSではコードレビューまたはWAFのいずれかの導入を求められているので、WAF導入は必須ではない)</li></ul><p></p><h3 style="text-align: left;">XSS攻撃の詳細</h3><p>K管理画面は社員向けのシステムであるので、画面の詳細は外部の攻撃者にはわからないはずです。報告書ではXSS攻撃の詳細は発表されていませんが、管理画面を狙うXSS攻撃自体は最近よく見かけるもので、おそらく問い合わせ画面のように外部から入力できるフォームからJavaScriptによる攻撃コードを注入したのではないかと予想されます。その種の攻撃の例としては、<a href="https://blogs.jpcert.or.jp/ja/2021/07/water_pamola.html">Water Pamola</a>が知られています。Water Pamola型のXSS攻撃の例については以下の動画を参照ください。</p>
<div class="separator" style="clear: both; text-align: center;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/FpCabifwhKg" title="YouTube video player" width="560"></iframe>
</div>
<br /><h3 style="text-align: left;">なぜパスワードが窃取できたか?</h3><p>次にXSS攻撃でK管理画面のパスワードが窃取できた理由を考察します。こちら報告書には書かれていないので、「ありそうな経路」を列挙するにとどめます。</p><p></p><ol style="text-align: left;"><li>管理機能としてパスワードを平文で表示する箇所があった</li><li>ログイン状態で自分自身のパスワードを変更した(パスワード再入力なし)</li><li>他の管理者のパスワードを変更する機能を悪用した(パスワード再入力なし)</li><li>ログイン中のユーザあるいは他の管理者のパスワードをリセットする機能を悪用した</li><li>偽のタイムアウトを引き起こして「IDとパスワードを入力してください」という画面を表示した(12:56追記)</li></ol><p></p><p>1は通常はあり得ないのですが、この事件では「あり得ないことが幾つも起こっている」のでないと断言することもできません。ありそうな経路は2または3ですが、そうすると、本来の管理者がログインできなくなります。その段階で気づきそうなものですが、報告書には当初不正ログインに使われていたX氏のパスワードが変更されたとあるものの変更した理由は明記されていない(ちなみに時期も明確ではない)ため、「X氏は攻撃者にパスワードを変更されたことに気づかないままパスワードをリセットした」可能性も考えられると思います。</p><h3 style="text-align: left;">社内用決済管理画面はインターネットからアクセスできた</h3><p>K管理画面に不正ログインされたということは、社内用決済管理画面にインターネットからアクセスできたことを意味します。これも奇異な状態ですが、報告書の時系列表には、2022年1月6日に「管理用サイトにBasic認証を追加」とあるので、それまではインターネットから自由に当該システムにアクセスできたものと推測されます。</p><p><br /></p><h2 style="text-align: left;">2. SQLインジェクション攻撃によるカード情報とパスワードの窃取</h2><p>報告書には以下のように記載されています。以下引用です。</p><p></p><blockquote>攻撃者は、2021年10月14日から2021年10月27日に渡り、A社アプリに対するSQLインジェクション攻撃により、暗号化されたカード番号、マスクされたカード番号及びA社管理画面の管理者アカウント情報をそれぞれ不正取得した。</blockquote><p></p><p>この攻撃の様子を下図に示します。</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbSrZ8ltaZGusTLa6W9S6eXXK1vKm1HTOFjvzQBPwjXU6qnIYBw7Epev55awPvp8UAhI0HY_HxrsbYYInABH6m5kALEcavKuysxJrrlq6lqs-L9sKU2IISAAoTatNo109fb6N30CrG3jbnHHMlzd8I46I9zqQzmiKGN1yjydoDt09iuaaglB5jwA/s1600/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%893.PNG" style="clear: left; display: block; float: left; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="429" data-original-width="840" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbSrZ8ltaZGusTLa6W9S6eXXK1vKm1HTOFjvzQBPwjXU6qnIYBw7Epev55awPvp8UAhI0HY_HxrsbYYInABH6m5kALEcavKuysxJrrlq6lqs-L9sKU2IISAAoTatNo109fb6N30CrG3jbnHHMlzd8I46I9zqQzmiKGN1yjydoDt09iuaaglB5jwA/s1600/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%893.PNG" /></a></div>
<br /><p><br /></p><p><br /></p><p>A社アプリは元々他のクラウドに設置されていたものですが、2018年に東京データセンターに移設され、その際、データベースのテーブルはカード情報データベースと同じスキーマに設置されていたとのことです(データベーススキーマの未分離)。このため、A社アプリに対するSQLインジェクション攻撃により、カード情報まで窃取されたことになります。</p><h3 style="text-align: left;">SQLインジェクションによるパスワード窃取と不正ログイン</h3><p>また、A社管理画面の管理者アカウントの窃取と不正ログインについては以下のような時系列となっています。</p>
<table>
<tbody><tr><td>2021年10月15日 05:09</td><td>SQLインジェクションによりID・パスワードの窃取</td></tr>
<tr><td>2021年10月15日 05:12</td><td>不正ログイン</td></tr>
</tbody></table>
<p>IDとパスワードの窃取から、わずか3分後に不正ログインされていることから、パスワードは平文で保存されていた可能性が高いと推測されます。仮にハッシュ値等で保存されていたとしても3分間で平文パスワードを復元されたのでは意味がありません。この不正ログインが次項の「バックドアプログラムの設置」につながります。</p><h3 style="text-align: left;">SQLインジェクション脆弱性が残置された理由</h3><p>SQLインジェクション攻撃を許してしまった理由は、前述のWAFの未設置の他、A社管理画面の開発経緯が原因だったようです。以下報告書からの引用です。</p><blockquote><p>上記ソースコード・レビューに関する社内規程が作成されたのは、2012年10月であるところ、A社アプリが委託先であるB社によって開発されたのは、2007年頃であるため、当時は、社内的にも同アプリに対してSQLインジェクション攻撃への対策としてソースコード・レビューを実施することは必須とされていなかった。また、MPにおいては、以前より「決済システム以外は脆弱性対策をする必要がない」との認識があったため、同規定の作成時において、当時フロントシステムにあったA社アプリが見直し的にソースコード・レビューの対象となることもなかった。</p></blockquote><p>A社アプリが東京データセンターに移設された後も、同様の認識により、ソースコード・レビューや脆弱性診断の対象にはなっていなかったようです。</p><p><br /></p><h2 style="text-align: left;">3. バックドアプログラムの設置と攻撃</h2><p>こちらも報告書から引用します。</p><p></p><blockquote><p>攻撃者は、2021年10月15日、A社管理画面に一度不正アクセスしているが、更に2021年11月11日、A社管理画面に不正アクセスを行い、A社アプリの管理機能の一つであるファイルアップロード機能を悪用し、バックドアプログラムを設置した。</p><p>そして、不正ファイル経由で、データベース内から、暗号化されたカード情報を含む当時格納されていた全ての情報を不正取得したと考えられる。</p></blockquote><p></p><p>この様子を下図に示します。</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsDNyZXVkNFZWnP_ylq1iieVAc1MHUEKmXTnDcPIQpjX2oLbCPp_-2WC-E0kKZM970DjAOuEOW0g8uQ6FWdJ3-c-s_eapeAbq4PtS0GHDmeo8eemYYgKL7X4j5tUNJtBJPYfOyH298kJGdhsRTrfF9aInwnTMe7LukPbEsHO2KOFkAeXFyz-H-XQ/s1600/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%894.PNG" style="clear: left; display: block; float: left; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="429" data-original-width="840" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsDNyZXVkNFZWnP_ylq1iieVAc1MHUEKmXTnDcPIQpjX2oLbCPp_-2WC-E0kKZM970DjAOuEOW0g8uQ6FWdJ3-c-s_eapeAbq4PtS0GHDmeo8eemYYgKL7X4j5tUNJtBJPYfOyH298kJGdhsRTrfF9aInwnTMe7LukPbEsHO2KOFkAeXFyz-H-XQ/s1600/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%894.PNG" /></a></div>
<br /><p><br /></p><p><br /></p><p>ファイルアップロード機能によるバックドアプログラム(おそらくWebShell)を設置するのは定番の攻撃方法です。報告書には「東京DCがA社アプリと同じJavaで構築されているため」という記述があるのため、A社アプリはJavaで構築されていることがわかります。なので、バックドアプログラムは、JSP記述のWebShellと推測されます。</p><p>また、「不正ファイル」という用語はバックドアプログラムとは別のものとして記載されているようです。不正ファイルの中身は判然としませんが、WebShellから起動されたリバースシェルあるいはデータベース(MySQL)アクセス用のツールではないかと思われます。</p><h3 style="text-align: left;">バックドア設置はSQLインジェクション対策後に行われた</h3><p>A社アプリのSQLインジェクション脆弱性対策は2021年10月27日に完了していますが、A社アプリのパスワードを悪用してのバックドア設置は2021年11月11日に実行されています。SQLインジェクション対応の一環として、A社アプリのパスワード変更を実施しておけば、バックドア設置はできなかったはずです。</p><h3 style="text-align: left;">復号鍵の窃取</h3><p>また、報告書には記載がありませんが、経産省の改善命令には復号鍵について以下の記載があります。</p><blockquote><p>当該クレジットカード決済システム内のデータベースに保存していた暗号化されたクレジットカード番号(マスキングされたクレジットカード番号を含む。)、有効期限、セキュリティコード及びこれらを復号化するための<span style="color: red;">復号鍵が窃取され</span></p></blockquote><p>復号鍵が窃取されたと明記されていますが、復号鍵がデータベースに保存されていたとは考えにくく、また「復号化サーバ」が存在する以上は、復号鍵はそこにあると考えるのが自然です。</p><p>報告書には復号鍵の漏洩ルートに関する記載はないようですが、時系列表には以下の記載があります。</p><blockquote><p>2022年1月28日 復号APIのディレクトリトラバーサルに対応。</p></blockquote><p><span> このため、復号APIのディレクトリトラバーサル脆弱性が悪用されて復号鍵が窃取された可能性があります。</span><br /></p><h3 style="text-align: left;"><span>任意形式のファイルをアップロード可能だった</span></h3><p>管理画面にファイルアップロード機能があること自体はよくあることですが、この場合、任意ファイル名で任意の内容のファイルをアップロードできると、簡単にWebShell等を設定されてしまうので通常はファイル名やファイルの中身に制限をつけます。しかし、報告書によると、以下のような記載があり、</p><blockquote><p>アップロード機能の設定の不備 により、想定以外のファイルをアップロード可能だった為、バックドアプログラムを設置されていた。</p></blockquote><p>拡張子.jspのファイル等をアップロードできていたことがわかります。</p><h3 style="text-align: left;">ファイル改ざん検知の不備</h3><p>バックドアプログラムの設置は一種の「改ざん」にあたるため、ファイル改ざん検知システムが導入されていれば、バックドア設置を早期に発見できた可能性があります。メタップスペイメント社はPCI DSS 3.2.1完全準拠をうたっていましたし、PCI DSSではファイル改ざん検知システムの導入を要求しています。A社アプリはPCI DSSの対象外だったようですが、このアプリケーションにも改ざん検知システム(ファイル整合性監視ツール)を導入して監視しておけば、早期にバックドアの設置を検知して対応できた可能性があります。また、この記事の冒頭で推測しているようにA社アプリとK管理画面が同一サーバーに同居していたとするならば、PCI DSSの観点からも、ファイル改ざん検知の対象にすべきであったと考えます。</p><p>もっとも当社の監視体制については、報告書に以下の記載もあるため、改ざん検知システムを導入していただけでは有効に機能しなかった可能性が高いです。</p><blockquote><p>従業員のヒアリング結果によれば、実際はセキュリティアラートにする検証ができる人材が不足しており、また、必要な範囲でセキュリティアラートを発信するようにするためのシステム面での調整(チューニング)が出来ていないことも相まって、<span style="color: red;">MPの従業員は、セキュリティアラートが発信されても、特段気にして監視していなかった</span>とのことであり、十分な検証がなされていなかったことが認められた。</p></blockquote><p>事故後の対応として、2022年1月29日に「改ざん検知設定を修正」とあるため、改ざん検知システム自体は元々導入されていたようです。</p><p><br /></p><h2 style="text-align: left;">4. 管理画面 への再度の不正アクセス 及び カード番号照会開始</h2><p>報告書には以下のように書かれさています(2021年10月25日から同年12月14 日)。</p><blockquote><p>上記SQLインジェクション攻撃及びバックドアプログラムにより、既にデータベースからマスクされたカード番号を不正取得しており、K管理画面上で不正取得したマスクされたカード番号を検索照会することによって、平文のフル桁のカード番号を閲覧することができたと考えられる。</p></blockquote><p>また時系列表には以下のように記載されています。</p><p></p><blockquote>海外IPアドレスによりUserID「Y氏」を利用してカード番号の検索が行われ、不正取得した閲覧用パスワード入力後、平文のフル桁のカード番号の検索結果が表示された。<br />なお、この間、平文のフル桁のカード番号を確認可能なURLに対し、約2万回不審な連続アクセスが確認された。</blockquote><p></p><p>この攻撃の様子を下図に示します。</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS9Q7Bwdfy-1f3v7dCg6hqFNibeeuCFlv9AQFFcX4Tai2NrHaIgMAiBcoyNY7JRHjh5pwgbobz1kJEUGTq45YLFrt5JeyEgKCsy1beziqpeFBvPxk5gvEOW45XndNgir60usrwfZpghy0dyRbf9YbFcxKaua5NTxllgXWMFa60V3VHS3fUDCZsHw/s1600/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%895.PNG" style="clear: left; display: block; float: left; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="429" data-original-width="840" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS9Q7Bwdfy-1f3v7dCg6hqFNibeeuCFlv9AQFFcX4Tai2NrHaIgMAiBcoyNY7JRHjh5pwgbobz1kJEUGTq45YLFrt5JeyEgKCsy1beziqpeFBvPxk5gvEOW45XndNgir60usrwfZpghy0dyRbf9YbFcxKaua5NTxllgXWMFa60V3VHS3fUDCZsHw/s1600/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%895.PNG" /></a></div>
<h2>クレジットカード窃取は3経路存在した</h2>
<p></p>
<p>ここまで説明した攻撃手法から、クレジットカード情報を攻撃者が入手した経路は以下の3経路があることになります。</p><p></p><ol style="text-align: left;"><li>A社アプリ経由: SQLインジェクション攻撃によりマスク化カード情報と暗号化カード情報を入手(2万5千件、2021年10月27日まで)</li><li>バックドア経由: バックドアにより暗号化カード情報と復号鍵を入手(288万件、2022年1月18日まで)</li><li>K管理画面経由: K管理画面のもつクレジットカード番号検索機能の悪用による平文全桁カード情報取得(2万件、2021年12月14日まで)</li></ol><p></p><p>3のK管理画面経由での平文カード情報取得は2021年12月14日で終わっています。攻撃者がなぜ経路3をこの時期にやめたかという理由を推測すると、この時期に復号鍵が入手できたため、経路3を悪用する理由がなくなったから、というのが私の推測です。</p><p>事故対応の過程で、当初メタップスペイメント社は上記1および3の経路のみを把握していたようですが、以下のように、1および3以外の経路があることに気づきます。</p><blockquote><p>MPは、2022年1月21日に受領したK社12件のログの社内検証結果として、判明している原因(K管理画面に対する不正アクセスやSQLインジェクション攻撃)以外でクレジットカード情報が窃取されていると判断した(1月8日までの対策のみでは情報漏えいを防ぎきれていないことを認識した。)。</p></blockquote><p>その後の調査にて、2022年1月24日にバックドアプログラムの発見、削除に至ります。</p><p><br /></p><h2 style="text-align: left;">まとめ</h2><p>メタップスペイメントのクレジットカード情報漏えい事件の概要を第三者委員会の報告書を元に説明しました。決済代行事業社からクレジットカード情報が大量に漏洩するという衝撃的な事件でありましたが、漏洩に至る経緯も驚くべきもので、第三者委員会報告書はセキュリティ関係者にとって非常に学びの多い資料だと思います。</p><p>侵入の発端となった脆弱性は、クロスサイトスクリプティングおよびSQLインジェクションという非常に基本的なものでありますが、仮にそれらの脆弱性があっても、他の保険的な対策や侵入発見後の対処が適切であれば、被害を最小限に留められたはずという点でも学びが多く、機会があれば別の記事で紹介したいと思います。</p>
<p>クロスサイトスクリプティングやSQLインジェクションなどの基本的な脆弱性や、当事件で悪用されたファイルアップロード機能やアカウント管理に関する対策方法については下記の書籍にて説明しています。</p>
<iframe frameborder="0" marginheight="0" marginwidth="0" sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" scrolling="no" src="//rcm-fe.amazon-adsystem.com/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=jisakucompile-22&language=ja_JP&o=9&p=8&l=as4&m=amazon&f=ifr&ref=as_ss_li_til&asins=4797393165&linkId=8920d5589e27f747c377695da91ba0bc" style="height: 240px; width: 120px;"></iframe>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-5192431671317960132022-05-16T10:53:00.004+09:002022-05-16T13:42:02.558+09:00DNSリバインディング(DNS Rebinding)対策総まとめ<h1 id="dnsリバインディングdns-rebinding対策総まとめ">サマリ</h1>
<p>DNSリバインディングが最近注目されている。Google Chromeは最近になってローカルネットワークへのアクセス制限機能を追加しており、その目的の一つがDNSリバインディング対策になっている。Googleが提供するWiFiルータGoogle Nest WiFiはデフォルトでDNSリバインディング対策機能が有効になっている。
DNSリバインディング対策は、攻撃対象アプリケーションで行うべきものであるが、ブラウザ、PROXYサーバー、リゾルバ等でも保護機能が組み込まれている。本稿ではそれら対策機能の状況と対策の考え方について説明する。</p>
<h2 id="dnsリバインディングdns-rebindingとは">DNSリバインディング(DNS Rebinding)とは</h2>
<p>DNSリバインディングはDNS問い合わせの時間差を利用した攻撃です。DNSのTTL(キャッシュ有効期間)を極めて短くした上で、1回目と2回目の問い合わせ結果を変えることにより、IPアドレスのチェック等を回避する攻撃です。下図は、DNSリバインディング時のDNS問い合わせの様子です。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvrqoh4zHYHmUrjZCOJCdR3V08wrZD-PxtAE-WcjVtY6C_N-6Vu52sw_UrLfbcZjS9coc9R_Yel_KrMcqPgZeTxWk7AkKavHJRBNiLPS75K6aL-F9VX9CrNjUdHINfH4vO0kW1YZpg67xRMaEhDHM68qaZd801WEL2EiQCjVfzhnfw60BHM22BNA/s849/dns-rebinding-query.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="350" data-original-width="849" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvrqoh4zHYHmUrjZCOJCdR3V08wrZD-PxtAE-WcjVtY6C_N-6Vu52sw_UrLfbcZjS9coc9R_Yel_KrMcqPgZeTxWk7AkKavHJRBNiLPS75K6aL-F9VX9CrNjUdHINfH4vO0kW1YZpg67xRMaEhDHM68qaZd801WEL2EiQCjVfzhnfw60BHM22BNA/s16000/dns-rebinding-query.png" /></a></div><br /><p><br /></p><p><br /></p>
<h2 id="dnsリバインディングの主な脅威">DNSリバインディングの主な脅威</h2>
<p>DNSリバインディングによる脅威は、DNS問い合わせがあるところ全てにありえますが、典型的な脅威として下記があります。</p>
<ul>
<li>外部からアクセスできない対象へのブラウザ経由での攻撃</li>
<li>SSRF攻撃のチェック回避</li>
</ul>
<h3 id="ブラウザ経由での攻撃">ブラウザ経由での攻撃</h3>
<p>ブラウザ経由での攻撃の概要図を示します。</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqS0_1YKjQS9TXIjbV0QleaVCQjv_d4OHOHS5aHLSlgOYhIDmxayFQV7qeHXSx8kH1KXKaErbzWBThS8gpX3_SAd7RJ7nscmn6KWWQ38RsxnxzGMYJqn3kbc34ckUHfMBGNUEpXZ3WCY3qD2l6ksXjRf7uUHl0dyaNh63rc7ylcrEpN2pXrbzEZw/s762/dns-rebinding.png" style="clear: left; display: inline; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="292" data-original-width="762" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqS0_1YKjQS9TXIjbV0QleaVCQjv_d4OHOHS5aHLSlgOYhIDmxayFQV7qeHXSx8kH1KXKaErbzWBThS8gpX3_SAd7RJ7nscmn6KWWQ38RsxnxzGMYJqn3kbc34ckUHfMBGNUEpXZ3WCY3qD2l6ksXjRf7uUHl0dyaNh63rc7ylcrEpN2pXrbzEZw/s16000/dns-rebinding.png" /></a></p><p>上図は、攻撃者の誘導により、被害者が罠のページ(<a href="http://trap.example.org/">http://trap.example.org/</a>)を閲覧しているところです。罠ページが閲覧された瞬間にDNSのAレコード(IPv4アドレス)を変更し、罠ページは10秒後にXMLHttpRequestにより <a href="http://trap.example.org/secret.html">http://trap.example.org/secret.html</a> をアクセスします。この時点ではIPアドレスは変更されているためイントラネット内のサーバーにアクセスします。この攻撃により、外部からは直接アクセスできないサーバーにアクセスできます。</p>
<p>このように、DNSリバインディングは、イントラネット内のサーバーの他、ルーターやファイアウォール、IoT機器など、利用者のパソコン自身など、「外部からはアクセスできないが内部ネットワークからはアクセスできる」機器やサーバーが主な攻撃対象です。IPアドレスのみでアクセス制御されているサイトも攻撃対象になります。</p>
<p>攻撃対象の例として、Ruby on Railsの開発環境がありました。Railsの開発支援機能web-consoleはDNSリバインディングに脆弱で、任意のコードを外部から実行できる問題が指摘されていました。</p>
<p><a href="https://prathamesh.tech/2019/09/02/dns-rebinding-attacks-protection-in-rails-6/#dns-rebinding-attack">DNS rebinding attacks protection in Rails 6</a></p>
<p>このため、バージョン6以降で開発版でのDNSリバインディング対策が入るようになりました)。具体的にはHostヘッダのチェックであり、これはDNSリバインディングの定石的な対策方法の一つです。</p>
<ul>
<li><a href="https://blog.saeloun.com/2019/10/31/rails-6-adds-guard-against-dns-rebinding-attacks">Rails 6 adds guard against DNS rebinding attacks | Saeloun Blog</a></li>
<li><a href="https://techracho.bpsinc.jp/hachi8833/2020_02_05/83154">Rails 6にDNSリバインディング攻撃防止機能が追加された(翻訳)|TechRacho by BPS株式会社</a></li>
<li><a href="https://github.com/rails/rails/pull/33145">Guard against DNS rebinding attacks by permitting hosts by gsamokovarov · Pull Request #33145 · rails/rails</a></li>
</ul>
<p>この機能は開発環境のみでプロダクション環境では無効になります。
これは、Web-consoleのようなデバッグ支援環境でリモートコード実行の脆弱性が入りやすい(現実にあった)ので、開発環境だとDNSリバインディングの脅威があるという想定だと思います。</p>
<p>【参考】</p>
<ul>
<li><a href="https://xtech.nikkei.com/it/article/COLUMN/20130218/456766/">[4]DNSリバインディング | 日経クロステック(xTECH)</a></li>
<li><a href="https://blog.tokumaru.org/2010/03/hacking-router-by-dns-rebinding.html">DNSリバインディングによるルータへの侵入実験 | 徳丸浩の日記</a></li>
</ul>
<div style="text-align: left;"><br /></div><h3 id="ssrf攻撃との組み合わせ">SSRF攻撃との組み合わせ</h3>
<p>SSRF攻撃の際に使われるDNSリバインディングについては以下の記事をお読みください。</p>
<p><a href="https://blog.tokumaru.org/2019/03/ssrf-with-dns-rebinding.html">EC2上でDNS RebindingによるSSRF攻撃可能性を検証した | 徳丸浩の日記</a></p>
<p>ここでは概要を説明します。以下のようなスクリプトがEC2上で動いているとします。外部からURLを取得して、その内容を表示するものです。</p>
<pre><code><code><div>$url = $_GET['url'];
$urlinfo = parse_url($url);
$host = $urlinfo['host']; // URLからホスト名を取り出し
$ip = gethostbyname($host); // 接続先IPアドレスを取得
if ($ip == "169.254.169.254") { // AWSのIMDSチェック
die("Invalid host $host"); // IPアドレスが169.254.169.254ならエラー
}
$ch = curl_init($url); // URLからコンテンツ取得、表示
</div></code></code></pre>
<p>上記のスクリプトに、IPアドレス 169.254.169.254 のチェックをしていますが、これはEC2の<a href="https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html">IMDS (Instance Metadata Service)</a>という機能を悪用されることを防ぐためです。IMDSは下記のように、169.254.169.254という仮想的なエンドポイントにアクセスすると、参照元インスタンスの設定を返します。以下は、EC2インスタンスに付与されたIAMクレデンシャルを参照する様子です。</p>
<pre><code><code><div>[ec2-user@web ~]$ curl http://<span style="color: red;">169.254.169.254</span>/latest/meta-data/iam/security-credentials/XXXX
{
"Code" : "Success",
"LastUpdated" : "2022-05-08T04:17:09Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIAR6Mxxxxxxxxxxxxx",
"SecretAccessKey" : "Wrt7en1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Token" : "IQoJb3JpZ2luX2VjEAQaDxxxxxxxxxxxxxxxx...",
"Expiration" : "2022-05-08T10:52:42Z"
}
</div></code></code></pre>
<p>先に示したスクリプトは複数の抜け道があるのですが、その一つがDNSリバインディングによる攻撃です。以下は、DNSサーバーにdigコマンドにより連続してアクセスしたところ、2回目にIMDSのIPアドレスが返っている様子です。</p>
<pre><code><code><div>[ec2-user@web ~]$ dig example.net +short ; dig example.net +short
203.0.113.2
<span style="color: red;">169.254.169.254</span>
[ec2-user@web ~]$
</div></code></code></pre>
<p>これにより、IPアドレスチェックの際は無害な内容を返し、本番アクセスの際はIMDSのIPアドレスということになって、SSRF攻撃によりIMDSの内容を盗むことができます。</p>
<p>【参考】</p>
<ul>
<li><a href="https://blog.tokumaru.org/2019/03/ssrf-with-dns-rebinding.html">EC2上でDNS RebindingによるSSRF攻撃可能性を検証した | 徳丸浩の日記</a></li>
<li><a href="https://www.docswell.com/s/ockeghem/52QN1Z-line-dev-day-2019">XXE、SSRF、安全でないデシリアライゼーション入門 | ドクセル</a></li>
</ul>
<div style="text-align: left;"><br /></div><h1 id="dnsリバインディングの対策">DNSリバインディングの対策</h1>
<p>下図はDNSリバインディングの対策ができる場所を示しています。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPC9_3UIRJmMS7drNQsZWkxaungmxDH_J5LFEmpTwzQGARByJYavmuf83pcMNtfNFm8JICqFlwAMojJgjX3UXBhPpb-Qsy2x2a5kFVs5b-BmsxvYT4q_jIZbKlK7A_TL6AAIgip2Gg7omBaAvTHNnJErqyCqmjaf_IZaspd1PAeMxyBcGafWCCeg/s578/dns-rebinding-protection.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="364" data-original-width="578" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPC9_3UIRJmMS7drNQsZWkxaungmxDH_J5LFEmpTwzQGARByJYavmuf83pcMNtfNFm8JICqFlwAMojJgjX3UXBhPpb-Qsy2x2a5kFVs5b-BmsxvYT4q_jIZbKlK7A_TL6AAIgip2Gg7omBaAvTHNnJErqyCqmjaf_IZaspd1PAeMxyBcGafWCCeg/s16000/dns-rebinding-protection.png" /></a></div>
<br clear="all" />
<p>以下、図中の数字の順に説明します。</p>
<h2 id="-対象サイト側の対策">① 対象サイト側の対策</h2>
<p>DNSリバインディングの根本対策は、攻撃対象のサイトや機器側で行われるべきです。DNSリバインディングの対策は以下のいずれかにより対策できます。</p>
<ul>
<li>ホスト名のチェック、あるいはダミーのデフォルトバーチャルホスト</li>
<li>認証機能の実装(安全なパスワードを設定すること)</li>
</ul>
<p>以下はNginxでダミーのバーチャルホストを設定している例です。本番サイトはexample.jpですが、それ以外のホスト名のリクエストはすべてダミーのコンテンツが返されることになります。ダミー側には機密情報や機能などはないので、DNSリバインディング攻撃を「受け流す」ことができます。</p>
<pre><code><code><div># ダミーのバーチャルホスト(Hostヘッダがexample.jp以外はこちらが設定される)
server {
listen 80 default_server;
server_name '_';
root /var/www/dummy; # 無害なコンテンツが返される
}
# 本番サイトのバーチャルホスト
server {
listen 80;
server_name example.jp;
…
</div></code></code></pre>
<p>あるいは、認証機能の実装でも対策になります。DNSリバインディング攻撃は利用者のセッションを乗っ取れるものではないからです。IoT機器やルーターなど初期パスワードが設定されているものは、必ず初期パスワードを変更する必要があります。</p>
<p>【参考】</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=s4EZgsfftL8">DNSリバインディング入門 - YouTube</a></li>
</ul>
<p>SSRF対策のパイパスについては、SSRF自体の対策の中で考える必要があります。詳しくは以下のコンテンツを参照ください。</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=2_ojaEpf1qs">SSRF 対策としてAmazonから発表された IMDSv2 の効果と破り方 - YouTube</a></li>
<li><a href="https://blog.tokumaru.org/2018/12/introduction-to-ssrf-server-side-request-forgery.html">SSRF(Server Side Request Forgery)徹底入門 | 徳丸浩の日記</a></li>
</ul>
<h2 id="-ブラウザ側対策">② ブラウザ側対策</h2>
<h3 id="dns-pinning">DNS Pinning</h3>
<p>主要ブラウザでは、DNSリバインディング対策としてDNS Pinning(DNSピニング)が実装されています。DNS Pinningとは、DNSのTTLが短い場合でも、DNSの参照結果を一定時間保持することです。下図は主要ブラウザのDNS Pinningの期間です。</p>
<table>
<thead>
<tr>
<th style="text-align: left;">Google Chrome</th>
<th style="text-align: left;">Firefox</th>
<th style="text-align: left;">Safari</th>
<th style="text-align: left;">IE</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">1分程度</td>
<td style="text-align: left;">1秒~70秒</td>
<td style="text-align: left;">15秒~30秒</td>
<td style="text-align: left;">無期限?</td>
</tr>
</tbody>
</table>
<p>DNSリバインディング対策という観点からはDNS Pinningの期間は長いほど安全ですが、DNSリバインディング以外の正当な状況でサーバーのIPアドレスが変化する場合もあるので、過度にDNS Pinningの期間が長いと副作用もあると考えられます。DNS Pinningの期間は時代とともに変化しており、最近はウェブサイトの本来のIPアドレスが短期間で変化するため、DNS Pinningの期間は短めにされる傾向があります。</p>
<h3 id="google-chromeで提唱されている対策">Google Chromeで提唱されている対策</h3>
<p>Google ChromeではDNS Pinning以外に「ローカルネットワークに対する攻撃」への防御機能が実装され始めています。Google Chrome 94では、XMLHttpRequestやFetch APIにより「パブリックネットワークからプライベートネットワークへの送信」については、HTTPSであることを要求するようになりました。</p>
<blockquote>
<p>Starting in Chrome 94, public non-secure contexts (broadly, websites that are not delivered over HTTPS or from a private IP address) are forbidden from making requests to the private network.</p>
<p><a href="https://developer.chrome.com/blog/private-network-access-update/">Private Network Access update: Introducing a deprecation trial - Chrome Developers</a> より引用</p>
<p>Chrome 94以降、パブリックな非セキュアコンテキスト(広義には、HTTPSで配信されていないウェブサイトや、プライベートIPアドレスからでないウェブサイト)は、プライベートネットワークへのリクエストが禁止されるようになりました(私訳)。</p>
</blockquote>
<p>これにより、Google ChromeでDNSリバインディング攻撃しようとすると以下のようなエラーになります(ホスト名は例示用のもの)。</p>
<pre style="white-space: pre-wrap;"><code><code><div>Access to XMLHttpRequest at 'http://example.com/secret.txt' from origin 'http://example.org' has been blocked by CORS policy: The request client is not a secure context and the resource is in more-private address space `private`.
</div></code></code></pre>
<p>エラーメッセージとしてはCORSエラーとなっていますが、DNSリバインディングは通常同一オリジンポリシーの範囲内のアクセスであり、当然CORSのエラーではありません。しかし、Google Chromeの最近のセキュリティ機能では、パブリックネットワークからプライベートネットワークへのリクエスト、およびプライベートネットワークからローカルホストへのアクセスについては、通常より厳しい制限を課していることになります。</p>
<p>このセキュリティ機能は、ローカルネットワークに対するCSRF緩和策として導入されたようですが、DNSリバインディング対策としても機能すると思われます。なぜなら、HTTPSによりDNSリバインディング攻撃しようとすると、攻撃先サーバーへのアクセスで証明書のエラーエラーになり、アクセスは停止するからです。</p>
<p>さらに、Google Chrome 102 以降では、このような状況でプリフライトリクエストが送信されるようになる予定です。</p>
<blockquote>
<p>Preflight requests for PNA are also sent for same-origin requests, if the target IP address is more private than the initiator. This is unlike regular CORS, where preflight requests are only for cross-origin requests. Preflight requests for same-origin requests guard against DNS rebinding attacks.</p>
<p><a href="https://developer.chrome.com/blog/private-network-access-preflight/">Private Network Access: introducing preflights - Chrome Developers</a> より引用</p>
<p>PNA(Private Network Access)のプリフライトリクエストは、ターゲットIPアドレスがイニシエーターよりもプライベートである場合、同一オリジンのHTTPリクエストに対しても送信されます。これは、プリフライトリクエストがクロスオリジンリクエストに対してのみ送信される通常のCORSとは異なります。同一生成元リクエストのプリフライトリクエストは、DNSリバインディング攻撃を防止します(私訳)。</p>
</blockquote>
<p>引用元にもDNSリバインディング攻撃防止のためと明記されています。</p>
<p>このようにGoogle Chrome(あるいはChromium系ブラウザ)を使うことでDNSリバインディングの対策が強化されますが、これら対策はProxyサーバー経由での通信には適用されません。Proxy経由での通信の場合、ブラウザからはパブリックあるいはプライベートという区別がつかないからです。これはDNS Pinningについても同様であり、各ブラウザ共通の挙動になります。</p>
<h2 id="-proxyサーバーでの対策">③ Proxyサーバーでの対策</h2>
<p>前述のようにProxy経由でのブラウザ利用の際には、ブラウザ側のDNSリバインディング対策は機能しないため、Proxy側での対策が求められます。よく理由されるFoward ProxyであるSquidには、以下のDNS Rebindin対策が利用できます。一方、Apache HTTPdをProxyとして使う場合、DNSリバインディングに使える機能は調査の範囲では見つかっていません。ご存じの方はぜひご指摘ください。</p>
<h3 id="1-ipアドレスによるアクセス制御">(1) IPアドレスによるアクセス制御</h3>
<p>Squidのアクセス制御機能から、接続先(Destination)のIPアドレスを制限することができます。以下は、192.168.0.0/24に対するアクセスを拒否する例です。</p>
<pre><code><code><div>acl localnet dst 192.168.0.0/24
http_access deny localnet
</div></code></code></pre>
<h3 id="2-dns-pinning">(2) DNS Pinning</h3>
<p>SquidはDNSキャッシュの上下限を指定することができます。</p>
<table>
<thead>
<tr>
<th style="text-align: left;">ディレクティブ</th>
<th style="text-align: left;">意味</th>
<th style="text-align: left;">デフォルト値</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">positive_dns_ttl</td>
<td style="text-align: left;">DNSキャッシュの上限</td>
<td style="text-align: left;">6時間</td>
</tr>
<tr>
<td style="text-align: left;">negative_dns_ttl</td>
<td style="text-align: left;">DNSキャッシュの下限</td>
<td style="text-align: left;">1分</td>
</tr>
</tbody>
</table>
<p>これらのうち、negative_dns_ttl はDNS Pinningの用途に使うことができます。
しかしながら、negative_dns_ttlをむやみに長くすると副作用もあります。もしも正規のDNSクエリに失敗した場合、その失敗の結果もキャッシュされるからです。また、前述のように、正常なIPアドレスが短期間で変わる場合もありえます。このため、negative_dns_ttlをデフォルトより長くすることはお勧めできません。</p>
<p>結論としては、SquidでDNSリバインディング対策する場合は、(1)のアクセス制御による方法がお勧めです。</p>
<p>【参考】</p>
<ul>
<li><a href="https://blog.tokumaru.org/2010/04/dns-rebinding-thru-proxy.html">PROXY(プロキシ)経由でのDNSリバインディングと対策 | 徳丸浩の日記</a></li>
</ul>
<h2 id="-dnsキャッシュサーバーでの対策">④ DNSキャッシュサーバーでの対策</h2>
<p>リゾルバ(DNSキャッシュサーバー)による対策も可能です。Squidの項で説明したDNS Pinningとアクセス制御の両方が主要リゾルバで用意されています。</p>
<h3 id="dns-pinning-1">DNS Pinning</h3>
<p>下表に主要ブラウザでDNS Pinningを指定するディレクティブを示します。DNSコンテンツサーバー側でTTL=0秒などと短い秒数が指定されていた場合でも、こちらで指定した秒数に切り上げられます。</p>
<table>
<thead>
<tr>
<th style="text-align: left;">リゾルバ</th>
<th style="text-align: left;">bind9</th>
<th style="text-align: left;">unbound</th>
<th style="text-align: left;">dnsmasq</th>
<th style="text-align: left;">PowerDNS</th>
<th style="text-align: left;">KNOT DNS</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">設定項目</td>
<td style="text-align: left;">min-cache-ttl</td>
<td style="text-align: left;">cache-min-ttl</td>
<td style="text-align: left;">min-cache-ttl</td>
<td style="text-align: left;">minimum-ttl-override</td>
<td style="text-align: left;">cache.min_ttl()</td>
</tr>
<tr>
<td style="text-align: left;">備考</td>
<td style="text-align: left;">90秒以下</td>
<td style="text-align: left;">制限など記載なし</td>
<td style="text-align: left;">1時間以下</td>
<td style="text-align: left;">デフォルト1秒</td>
<td style="text-align: left;">デフォルト5秒</td>
</tr>
</tbody>
</table>
<p>PowerDNSとKNOT DNSは最短のTTLがデフォルトとして指定(1秒~5秒)されており、TTL=0は許容されない設定になっています。このような控え目の設定でもSSRF攻撃の緩和には役立つと思われます。</p>
<h3 id="特定ipアドレスの拒否">特定IPアドレスの拒否</h3>
<p>また、多くのDNSサーバーは、DNSリバインディング対策として、IPアドレスのフィルタリング機能を提供しています。下表はIPアドレスの拒否リストあるいはDNSリバインディング対策として使えるプライベートIPアドレス等を拒否するためのディレクティブの一覧です。</p>
<table>
<thead>
<tr>
<th style="text-align: left;">リゾルバ</th>
<th style="text-align: left;">bind9</th>
<th style="text-align: left;">unbound</th>
<th style="text-align: left;">dnsmasq</th>
<th style="text-align: left;">PowerDNS</th>
<th style="text-align: left;">KNOT DNS</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">設定項目</td>
<td style="text-align: left;">deny-answer-addresses</td>
<td style="text-align: left;">private-address</td>
<td style="text-align: left;">stop-dns-rebind</td>
<td style="text-align: left;">設定は見つからなかった</td>
<td style="text-align: left;">modules.load('rebinding < iterate')</td>
</tr>
<tr>
<td style="text-align: left;">備考</td>
<td style="text-align: left;">サブネットマスク等で指定</td>
<td style="text-align: left;">サブネットマスク等で指定</td>
<td style="text-align: left;">一括停止</td>
<td style="text-align: left;">Luaスクリプトで可能</td>
<td style="text-align: left;">一括停止、詳細にやるならLuaスクリプト</td>
</tr>
</tbody>
</table>
<p>私の調査では、PowerDNSのみ該当する項目を探せなかったのですが、Luaスクリプトで特定IPアドレスを拒否することは可能でした。
また、dnsmasqとKNOT DNSはIPアドレス指定ではなく、DNSリバインディングに使われそうなサブネットマスクを一括して拒否する形になっています。</p>
<p>また、単体のリゾルバではありませんが、Googleが提供するGoogle Nest WiFiにはDNSリバインディング防御機能が提供されてます。</p>
<blockquote>
<p>Google Nest スピーカー、ホームメディア サーバー、IoT(モノのインターネット)デバイスのような接続されたデバイスをホストするホーム ネットワークは、DNS リバインディングと呼ばれる攻撃を受けるおそれがあります。Google Wifi では、この種の攻撃を防止するため、DNS リバインディングに対する保護機能で、公開ドメインにプライベート IP アドレス範囲を使用されないようブロックすることができます。この機能はデフォルトで有効になっています。</p>
<p><a href="https://support.google.com/googlenest/answer/9144137?hl=ja">DNS リバインディングに対する保護機能 - Google Nest ヘルプ</a>より引用</p>
</blockquote>
<p>試したところ、192.168.10.1 のようなプライベートIPアドレスはブロックされる一方で、127.0.0.1 のようなlocalhostのアドレスはブロックされません。また、当該機能は副作用の可能性もあるため、無効にすることもできます。</p>
<h2 id="リゾルバでのdnsリバインディング対策をどう考えるか">リゾルバでのDNSリバインディング対策をどう考えるか</h2>
<p>多くのリゾルバがDNSリバインディング対策の機能を提供しますが、デフオルトは無効化されていることから、いずれも「積極的に使っていきましょう」という姿勢ではないように感じます。その典型はbindです。min-cache-ttlの最大値は90秒なので「これで守り切る」という数字ではありません。また、IPアドレスの拒否リストについては以下のような注意書きがマニュアルに付記されています。</p>
<blockquote>
<p>The “rebinding” attack must primarily be protected at the application that uses the DNS. For a large site, however, it may be difficult to protect all possible applications at once. This filtering feature is provided only to help such an operational environment; turning it on is generally discouraged unless there is no other choice and the attack is a real threat to applications.</p>
<p><a href="https://ftp.isc.org/isc/bind9/9.19.0//doc/arm/html/reference.html#content-filtering">4. BIND 9 Configuration Reference — BIND 9 9.19.0 documentation</a> より引用</p>
<p>リバインディング攻撃は、主にDNSを使用するアプリケーション側で保護されなければなりません。しかし、大規模なサイトでは、可能性のあるすべてのアプリケーションを一度に保護することは困難な場合があります。このフィルタリング機能は、そのような運用環境を支援する目的にのみ提供されています。他に選択肢がなく、攻撃がアプリケーションにとって本当に脅威とならない限り、この機能を有効にすることは一般に推奨されません。(私訳)</p>
</blockquote>
<p>この「DNSを使用するアプリケーション側で保護されなければなりません」という指摘に私も同意します。
また、以下の資料にはDNSリゾルバの防御機能の抜け穴が報告されています。例えば、CNAMEとしてlocalhost.を返すようなケースが紹介されています。</p>
<ul>
<li><a href="https://media.defcon.org/DEF%20CON%2027/DEF%20CON%2027%20presentations/DEFCON-27-Gerald-Doussot-Roger-Meyer-State-of-DNS-Rebinding-Attack-and-Prevention-Techniques-and-the-Singularity-of-Origin.pdf">State of DNS Rebinding - DEF CON 27 Hacking Conference Presentation</a></li>
</ul>
<p>このため、DNSリゾルバでの対策はあくまで緩和策としてとらえておくべきだと思います。</p>
<h2 id="結局dnsリバインディング対策はどうすればよいか">結局DNSリバインディング対策はどうすればよいか</h2>
<p>ここまで説明したように、DNSリバインディング対策が「出来る」ポイントは以下の4箇所あります。</p>
<ul>
<li>攻撃対象</li>
<li>ブラウザ</li>
<li>PROXYサーバー</li>
<li>リゾルバ</li>
</ul>
<p>これらの中で、確実な対策がとれるのは攻撃対象のみです。対策自体は難しいものではないので、まずはこれを検討するべきです。
しかし、以下のような状況はありえます。</p>
<ul>
<li>社内ネットワーク内に攻撃対象となり得る機器が多すぎで把握すら難しい</li>
<li>対象のサーバーや機器側の仕様上対策が難しい</li>
</ul>
<p>このため、以下のステップで対策を検討するとよいでしょう。</p>
<ol>
<li>ネットワーク内に存在するDNSリバインディング攻撃対象の機器やパソコンの洗い出し</li>
<li>対策の要否検討</li>
<li>対策方法の検討</li>
<li>対策実施</li>
</ol>
<p>DNSリバインディングはマイナーな攻撃方法であり、他のウイルス感染などの方が簡単に侵入できるため、あまり過敏になることもないとは思いますが、原理的には可能であるので、「重要情報が盗まれる」などの状況は放置しない方がよいでしょう。DNSリバインディングの確実な対策の一つが「認証ちゃんとやる」ですので、結局のところ、</p>
<ul>
<li>社内ネットワークといえども認証とアクセス制御をきちんとやる</li>
</ul>
<p>この当たり前のことをちゃんとやっていれば大丈夫です。
後は、保険的な対策としてPROXYやリゾルバの対応をどうするかですが、近年はPROXYやリゾルバはセキュリティ製品の一機能である場合も多く、自前でオープンソースソフトウェアを構築するケースは少ないと思います。なので、PROXYやリゾルバでの対策は、積極的に活用するというよりは、「使える場合は使うことも考える」くらいのレベル感ではないでしょうか。</p>
<h2 id="まとめ">まとめ</h2>
<p>DNSリバインディングの対策として使える機能について説明しました。Google社がDNSリバインディング対策に熱心なことが印象的ですが、まずは攻撃可能性の洗い出しと、ローカルネットワークでも認証をおろそかにしないという基本的な対策を推奨いたします。</p>
ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-36348058670894573412022-03-14T10:48:00.001+09:002022-03-14T10:48:35.071+09:00とある通販サイトに学ぶ自動ログイン機能のバッドプラクティス<h3 style="text-align: left;"> サマリ</h3><p>とある通販サイトにて「<span face="-apple-system, BlinkMacSystemFont, Meiryo, "Hiragino kaku Gothic ProN", sans-serif" style="background-color: white; color: #555555; font-size: 15.04px; letter-spacing: 0.752px;"> </span><span face="-apple-system, BlinkMacSystemFont, Meiryo, "Hiragino kaku Gothic ProN", sans-serif" style="background-color: white; color: #555555; font-size: 15.04px; letter-spacing: 0.752px;">メールアドレス・パスワードを保存する</span>」機能がありますが、サイトにクロスサイトスクリプティング(XSS)脆弱性がサイトにあると生のパスワードが漏洩する実装となっています。本稿では、この実装方式を紹介し、なぜ駄目なのか、どうすべきなのかを紹介します。</p><p>記事の最後にはセミナーのお知らせを掲載しています。</p><p><br /></p><h3 style="text-align: left;">はじめに</h3><p>家人がテレビを見ていて欲しい商品があるというので、あまり気は進まなかったのですが、その商品を検索で探して購入することにしました。「気が進まない」というのは、利用実績のないサイトはセキュリティが不安だからという理由ですが、この不安は的中してしまいました。</p><p>最初の「えっ?」はパスワード登録のところでして、パスワードを再入力する箇所で「確認のためもう一度、コピーせず直接入力してください」とあるのですよ。私は乱数で長く複雑なパスワードを入力しかけていたのですが、コピペができないとなると長すぎるので、パスワードを短くしました。セキュリティ上は逆効果だと思うのですが、なぜこうするのでしょうかね。しかし、これは本題ではありません。</p><p><br /></p><h3 style="text-align: left;">「パスワードを保存する」チェックボックスの存在</h3><p>会員登録が終わってサイトにログインしようとすると、パスワード欄の下に下記のようなチェックボックスがあり、デフォルトはONになっています。</p><p><input checked="1" type="checkbox" />メールアドレス・パスワードを保存する</p><p>「ログイン状態を保持」とかならよくある機能ですが、「パスワードを保存」とは匂いますね。そこで、そのままログインをした後、ログアウト後にもう一度ログイン画面を表示させると、なんということでしょう! メールアドレスとパスワードが初期値として入っているではありませんか! 以下のようなHTMLがサーバー側で生成されていました。</p><pre><input name="email" value="<span style="color: red;">tokumaru@example.jp</span>">
<input type="password" name="pwd" value="<span style="color: red;">P@ssw0rd</span>"></pre><p><br /></p><h3 style="text-align: left;">「パスワードを保存する」機能の実装は?</h3><p>こういう実装を見ると「サーバー側でパスワードが平文で保存されている」と思う人が多いようですが、そうではないようです。</p><p>メールアドレスとパスワードは、あるクッキー(ここではXとする)に紐づけられています。当初は、クッキーXをキーとしてサーバー側でパスワード等を保存しているのかと思いましたが、そうではなく、クッキーXにメールアドレスとパスワードが暗号化して保存されているようです。そう判断した理由は、メールアドレスとパスワードの長さを変えるとクッキーの長さも変わるからです。また、暗号化に際し<a href="https://note.com/ockeghem/n/n221476285007">初期化ベクトル</a>も使ってないようです。</p><p><br /></p><h3 style="text-align: left;">クッキーが漏洩すると平文パスワードまで漏洩する</h3><p>「初期化ベクトルを使わないなんてダメじゃないか」と思いますよね。そうなんですが、このサイトの場合、もっとダメな理由があります。というのは、暗号化されたクッキーをセットしてログイン画面を表示させると、先に紹介したように、平文でメールアドレスとパスワードがHTML上に表示されるのです。つまり、暗号化していても簡単に平文に戻せるのですよね。なので、初期化ベクトル云々という次元ではなくなってしまっているわけです。</p><p>このサイトの場合、クッキーXにsecure属性はついていますが、HttpOnly属性はついていません。なので、サイト上にクロスサイトスクリプティング(XSS)脆弱性があると、クッキーは簡単に漏洩します</p><p><br /></p><h3 style="text-align: left;">仮にHttpOnly属性がついていてもXSSで平文パスワードが漏洩する</h3><p>しかし、仮にクッキーXにHttpOnly属性がついていても、XSS攻撃により平文のメールアドレスとパスワードは漏洩します。XSS攻撃によりXMLHttpRequestでログイン画面をリクエストすると、そのレスポンスのログイン画面HTMLにはメールアドレスとパスワードが平文で記載されているからです。</p><p>そもそもサイトにXSSがあると、クッキーにHttpOnly属性がついていても「なりすまし」により情報漏えいやサイト機能の悪用は避けられません。このあたりは以下の動画にて詳しく説明しています。</p><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/4JREwhSC2dQ" title="YouTube video player" width="560"></iframe><p>しかし、パスワードまで漏洩してしまうと、そのパスワードがパスワードリスト攻撃により悪用されたり、「パスワードを入力しないと利用できない機能」まで悪用できるので通常のXSSよりも被害が増大することになります。</p><p><br /></p><h3 style="text-align: left;">この場合のベストプラクティスは?</h3><p>ログインを簡便にするために、いったんログインした後はパスワードを入力しなくてもログインを継続したいというニーズ自体はよくあるものであり、拙著では、「5.1.4 自動ログイン」にて解説しています。</p><p><a href="https://www.amazon.co.jp/dp/4797393165/?tag=jisakucompile-22">体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践</a></p><p>本項では、自動ログインの危険な実装を紹介した上で、トークンを使うなど安全な自動ログインの実装方法について紹介しています。詳しくは上記書籍を参照してください。</p><p><br /></p><h3 style="text-align: left;">サイトオーナーはどうすればよかったのか?</h3><p>とある通販サイトの自動ログイン機能がイケてないことを紹介しましたが、このサイトの運営者はどうすればよかったのでしょうか。</p><p>まず思いつくのは「脆弱性診断はしていなかったのか?」ということです。脆弱性診断していたかどうかは外部の者には分かりませんが、以下の可能性が考えられます。</p><p></p><ul style="text-align: left;"><li>脆弱性診断はしていなかった</li><li>脆弱性診断はしていたが指摘されなかった</li><li>脆弱性診断で指摘されていたが、改修はしなかった</li></ul><p></p><p>最後のケースを考えると、仮に脆弱性診断で指摘されていたとしても、サイトが出来上がった後では簡単に修正できるものでもなく、いささか「手遅れ」という感があります。なので、サイトを実装する前に自動ログインのセキュリティを検討しておくべきでした。</p><p>自動ログインは珍しい機能ではなく、「徳丸本にも載っている」ようなよくある機能なのですから、ウェブアプリケーションのセキュリティガイドラインのようなものがあれば、安全な実装ができていたかもしれないと考えられます。このガイドラインには、自動ログインのような機能面だけでなく、SQLインジェクションのような実装面にも言及されているとよいですね。</p><p><br /></p><h3 style="text-align: left;">宣伝</h3><p>では、「徳丸本にも載っていないような機能」についてはどうすればよいでしょうか。こちらについては個別に検討するしかありません。その際に類似機能を持つ先行サイトを調べてもよいでしょうが、たまたま参考にしたサイトがセキュリティ的に強固な仕様であるとは限りません。</p><p>また、「そもそもセキュリティ上の問題があるのか」という脅威分析をしないことには、セキュリティを検討する・しないの俎上にも上がらないということになります。</p><p>ということから考えると、セキュアな開発にも到達度レベルがあって、以下のようになるのではないかと考えています。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjFDhGnWhYHIH2-keybbPoynrDkaQ0Nok9yWWRrd1Fvi1QgV1EkGWGSVDeGo6kNTmNEAYgHmZTVSryQQUg8bGkLgU_0GrdVoPRI5njk3-eWD_GzDsfy0DKPOWHxC2LyWFhZGwjmtcNrjf1EDwLAuWthHrRPa6-uaRDeQC8yCllVMLelFNCyQffhcA=s863" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="455" data-original-width="863" height="338" src="https://blogger.googleusercontent.com/img/a/AVvXsEjFDhGnWhYHIH2-keybbPoynrDkaQ0Nok9yWWRrd1Fvi1QgV1EkGWGSVDeGo6kNTmNEAYgHmZTVSryQQUg8bGkLgU_0GrdVoPRI5njk3-eWD_GzDsfy0DKPOWHxC2LyWFhZGwjmtcNrjf1EDwLAuWthHrRPa6-uaRDeQC8yCllVMLelFNCyQffhcA=w640-h338" width="640" /></a></div><p>今回紹介したケースは、脆弱性診断はしていたかもしれないが、指摘されたか否かは分からず、少なくとも危ない実装が修正されないまま本番リリースされたという意味で、「レベル1にも到達していない」と考えられます。</p><p>私は拙著を書く際に、ウェブアプリケーションに存在する問題を列挙してできるだけ多くのパターンを解説してしまおう、と目論みました。なので徳丸本はあんなに分厚いのですが、それでも全てのパターンを列挙することは当然できなません。なので、徳丸本をいくら読んでも到達できるのは上記の「レベル2」です。</p><p>私の最近の関心はその上のレベルに到達するための方法論です。これについては2022年3月16日のセミナーにて簡単に紹介する予定ですので、お時間がある方は参加いただけると幸いです(宣伝)。</p><p></p><ul style="text-align: left;"><li>2022年3月16日(水)16:00~17:00(オンライン)</li><li>講演タイトル:セキュア開発ライフサイクル(SDLC)実践入門</li><li>イベント詳細・お申し込みは<a href="https://www.eg-secure.co.jp/seminar/20220216/">こちら</a>から</li></ul><p></p><p></p><p></p>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-81157197598711776212022-01-26T13:02:00.007+09:002022-01-29T13:42:59.113+09:002022年1月においてCSRF未対策のサイトはどの条件で被害を受けるか<h1 style="text-align: left;">サマリ</h1><p>2020年2月にGoogle ChromeはCookieのデフォルトの挙動をsamesite=laxに変更しましたが、2022年1月11日に<a href="https://www.mozilla.org/en-US/firefox/96.0/releasenotes/">Firefoxも同様の仕様が導入されました</a>。この変更はブラウザ側でCSRF脆弱性を緩和するためのもので、特定の条件下では、ウェブサイト側でCSRF対策をしていなくてもCSRF攻撃を受けなくなります。この記事では、デフォルトsamesite=laxについての基礎的な説明に加え、最近のブラウザの挙動の違いについて説明します。</p><p>(2022年1月29日追記)<br />本日確認したところ、Firefoxにおけるデフォルトsamesite=laxはキャンセルされ、従来の挙動に戻ったようです(Firefox 96.0.3にて確認)。デフォルトsamesite=lax自体は先行してGoogle Chromeにて実装されていましたが、細かい挙動の差異で既存サイトに不具合が生じたようで、いったん巻き戻されたようです。<br />このため、Firefoxについての現状の挙動はSafari等と同じになります。以下、当面の間読み替えていただければと思います。</p><p>デフォルトsamesite=laxキャンセルの情報は、Twitterにて<a href="https://twitter.com/myakura">Masataka Yakura</a>さんから教えていただきました。以下のスレッドにて確認することができます。<br /><blockquote class="twitter-tweet"><p lang="ja" dir="ltr">そのFirefox 96のSameSite=Laxデフォルト化なんですが、既存サイトの互換性に影響したためバックアウトされるようです(Schemeful SameSiteはすでにバックアウトされました)。<a href="https://t.co/otiTV0uf9y">https://t.co/otiTV0uf9y</a></p>— Masataka Yakura (@myakura) <a href="https://twitter.com/myakura/status/1486587796122845184?ref_src=twsrc%5Etfw">January 27, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <br />(追記終わり)</p><p><br /></p><h1 style="text-align: left;">Cookieのsamesite=laxとは </h1><p>Cookieのsamesite属性は、元々Google Chrome 51にて導入されたセキュリティ機能で、その後他の主要ブラウザにも導入されています。samesiteのとり得るパターンは、指定なし、none、lax、strictですが、本稿では主にsamesite=laxについて取り上げます。</p><p>Cookieにsamesite=laxを指定した場合、異なるサイトから遷移してきた場合にCookieが送信されるか否かは文脈によって変わります。下図の状況においては、example.jpに対してGETメソッドで遷移する場合はCookieが送信されますが、POSTメソッドの場合はCookieは送信されません。<a href="https://blogger.googleusercontent.com/img/a/AVvXsEj-CHeyb2JYlSTXJojwcQePbvce4q5Fe7XrVgWdjBv3CIHY0IgvqwSiyleEr8x8KuaDIQdDRr3NFl_83K8BmDALDwaFh9nguZc-Puvj_bZOHQx7s8HPNO9L1fMHXTTewXEG4_z5WK3VEZePG8GVZSZN4Apz06Vt1blLBwSkjZaQ-TGGN0lKIAuJlw=s780" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="273" data-original-width="780" src="https://blogger.googleusercontent.com/img/a/AVvXsEj-CHeyb2JYlSTXJojwcQePbvce4q5Fe7XrVgWdjBv3CIHY0IgvqwSiyleEr8x8KuaDIQdDRr3NFl_83K8BmDALDwaFh9nguZc-Puvj_bZOHQx7s8HPNO9L1fMHXTTewXEG4_z5WK3VEZePG8GVZSZN4Apz06Vt1blLBwSkjZaQ-TGGN0lKIAuJlw=s16000" /></a></p>GETリクエストとPOSTリクエストで挙動が変わるのは合理的な仕様です。例えば、gmailからtwitterにリンクをたどって遷移する場合はGETメソッドが使われるためtwitterにCookieが送信され、twitterをログイン状態で閲覧することができます。一方、罠サイトから投稿ページに偽投稿を送信する場合は、「更新処理にはPOSTメソッドを使う」という原則によりPOSTメソッドのはずなので、samesite=laxのクッキーはサイトに送信されず、ログイン状態にはならないためCSRF攻撃も成立しないことになります。<div>このように、Cookieのsamesite=lax指定は、サイト閲覧時の利便性と安全性のバランスがよく、優れた仕様であると考えられます。<br /><div><br /></div><h2 style="text-align: left;">デフォルトsamesite=laxの流れ</h2><div><p>samesite属性がブラウザに導入された当初は、従来のサイトの互換性は維持されていましたが、Google Chorme 80において、samesite無指定時の挙動がsamesite=laxに変更され、Firefoxもバージョン96にて追随しました。</p><p>この変更により「サイト側でCSRF対策していなくてもブラウザ側にてCSRF防御する」ことが可能になります。この変更は過去のサイトの互換性を崩す大胆なものであるため議論を呼んだようですが、Google ChromeおよびFirefoxという主要ブラウザが対応したことにより、今後の主流となることが確定しました。</p><h2 style="text-align: left;">その他のブラウザはどうか</h2><p>本稿執筆時点(2022年1月26日)において、Google Chrome、Edge、Opera、Firefox等主要ブラウザの多く(Chromium系およびFirefox)にてデフォルトでsamesite=laxとなっています。一方、IEおよびSafariはsamesite属性を実装しているものの、現時点ではデフォルトでsamesite=laxにはなっていません(<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite?utm_source=pocket_mylist">参考</a>)。また、iOS上のブラウザはすべてWebKitベースとなっているので、iOS上のChrome等もSafariと同じ挙動になります。</p><p><br /></p><h1 style="text-align: left;">2022年1月においてCSRF攻撃を受ける条件</h1><p>それでは、「サイト側でCSRF対策していない場合」において、CSRF攻撃を受ける条件はどうでしょうか。以下、ケース別に説明します。</p><h2 style="text-align: left;">古いブラウザを使っている場合</h2><p>samesite属性未実装の古いバージョンのブラウザを使っているユーザーは、サイト側のsamesite属性の設定に関係なくCSRF攻撃を受けることになります。最近のブラウザは自動アップデートの機能を実装していますが、自動アップデートを無効にしていたり、スマホ版のブラウザのバージョンが古い、スマホのOSをアップデートしていないケースではsamesite属性が無視される場合があります。</p><p><br /></p><h2 style="text-align: left;">最新のIEやSafariを使っている場合</h2><p>利用者が最新のIEやSafariを使っている場合、デフォルトではsamesite=laxではないため、サイト側でCSRF対策しておらずsamesiteの指定がない場合はCSRF攻撃の影響を受けます。一方、サイト側で明示的にsamesite=laxを使っている場合はCSRF攻撃を受けません。ただし、サイト側で、「GETメソッドで更新処理を受け付ける」実装になっている場合は、GETメソッドを用いたCSRF攻撃を受けます。脆弱性診断でも、この「GETメソッドでCSRF攻撃を受けるサイト」は時々見かけます。更新処理はPOSTメソッドのみ受け付けるようにすべきです。</p><p><br /></p><h2 style="text-align: left;">最新のGoogle ChromeやFirefoxを使っている場合</h2><p>利用者が最新のGoogle ChromeやFirefoxを使っている場合、Cookieのデフォルトがsamesite=laxになっているため、samesiteが、「指定しない」、lax、strictのいずれかであり、かつGETメソッドによる更新処理を許容していない場合は、それだけでCSRF攻撃をブラウザが防ぎます。samesite=noneが指定されている場合は、異なるサイトからのPOSTメソッドでもCookieが送信されます(すなわちCSRF攻撃の影響を受ける)が、samesite=noneを指定する場合はsecure属性も指定する決まりになっています。</p><p>ただし、デフォルトsamesite=laxには「2分間ルール」というものがあり、現実的な可能性は低いものの、CSRF攻撃を受ける余地があります。</p><h2 style="text-align: left;">2分間ルール</h2><p>最新のGoogle ChromeおよびFirefoxにおいて、samesite属性を指定しないCookieはsamesite=laxの扱いを受けますが、Cookieが生成されてから2分経過してからsamesite=laxになる仕様になっています。このため、ログイン処理などで新たにCookieが発行してから2分間であれば、CSRF攻撃の影響を受ける可能性があります。</p><p><br /></p><h2 style="text-align: left;">2分間ルールの挙動の違い</h2><p>さらに、Google ChromeとFirefoxでは「2分間ルール」の実装に違いがあるようです。</p><p>Google Chromeの場合、Cookieが新規生成されなくても、同名のCookieが上書きされた場合、2分間ルールが延長されます。これは、セッション固定攻撃対策などでセッションIDの値を再生成(PHPの場合はsession_regenerate_idを使う)した場合が該当します。</p><p>これに対して、Firefoxの場合、Cookieを同名で上書きした場合は2分間ルールは延長されず、新規にCookieが生成された時刻が起点になります。このため、脆弱性のデモなどで敢えて2分間ルールを使いたい場合は、いったんCookieを削除してから新規にCookieを生成することにより、2分間ルールの恩恵を受けることができます。</p><p>下図は上記挙動を図示したものです。時刻=1.5分のところでCookie SESSIDを再生成したところ、Google Chromeはsamesite=noneの時間が2分間延長されているのに対して、Firefoxは延長されていないこと、一旦Cookieを削除すると、どちらのブラウザでもsamesite=noneの期間が2分間復活することが図から見て取れます。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhqohJWJc_cUL-ohSlsrflv0XLCEGr6vKe4Z1OVeFBd1FtevXxGeH36RD8PGlygT7_JjkbapWsmd2fOs9_ecNO_JQqdhsE12nz4hil7_-6ZqySNVms82EKbYo6iufqPUQfohisz97Mv8tdw424MDL2lacavTbqmupXOjIugMsKcYbMSGJQymGHczA=s801" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="243" data-original-width="801" src="https://blogger.googleusercontent.com/img/a/AVvXsEhqohJWJc_cUL-ohSlsrflv0XLCEGr6vKe4Z1OVeFBd1FtevXxGeH36RD8PGlygT7_JjkbapWsmd2fOs9_ecNO_JQqdhsE12nz4hil7_-6ZqySNVms82EKbYo6iufqPUQfohisz97Mv8tdw424MDL2lacavTbqmupXOjIugMsKcYbMSGJQymGHczA=s16000" /></a></div><p>Google ChromeとFirefoxのこれら挙動は実験により確かめたもの(Google 97.0.4692.99、Firefox 96.0.2にて確認)なので、将来のバージョンアップなどで変更される可能性があります。</p><p><br /></p><h1 style="text-align: left;">まとめ</h1><p>2022年1月時点でのCSRFの影響について説明しました。ブラウザ側のセキュリテイ強化により、CSRF攻撃はかなり影響を受けにくくなっていはいますが、まだ「サイト側で何もしなくてもCSRF攻撃を受けない」状況ではありません。</p><p>引き続きCSRFの対策は必要であり、以下を推奨いたします。</p><p></p><ul style="text-align: left;"><li>アプリケーションフレームワークの提供するCSRF防御機能を使う</li><li>セッションIDのCookieにはsamesite=laxを明示する</li><li>更新処理ではGETメソッドを受け付けないことを確認する<br /></li></ul><div><br /></div><h3 style="text-align: left;">おわび・訂正</h3><div>当初の版では「IEはsamesite属性に対応していない」と記載していました。これはMDNの記載を根拠としたものですが、実際にはIEの最新版ではsamesite属性に対応しています。本来であれば該当箇所を打消し線で訂正すべきところですが、煩わしくなるため、単に修正しております。</div><div><br /></div><div><br /></div><p></p></div></div>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-9206138242989137912021-12-20T16:13:00.000+09:002021-12-20T16:13:42.834+09:00PHPにはエスケープ関数が何種類もあるけど、できればエスケープしない方法が良い理由<p>このエントリは、<a href="https://qiita.com/advent-calendar/2021/php">PHP Advent Calendar 2021</a> の20日目のエントリです。19日目は <a href="https://qiita.com/takoba">@takoba</a> さんによる <a href="https://qiita.com/takoba/items/fe87b038780ba8a2c6f3">PHPプロジェクトのComposerパッケージをRenovateで定期アップデートする</a> でした。</p><p>SQLインジェクションやクロスサイトスクリプティング(XSS)の対策を行う際には「エスケープ処理」をしましょうと言われますが、その割にPHP以外の言語ではあまりエスケープ処理の関数が用意されていなかったりします。それに比べてPHPはエスケープ処理の関数が非常に豊富です。これだけ見ても、PHPはなんてセキュアなんだ! と早とちりする人がいるかもしれませんが、しかし、他言語でエスケープ処理関数があまりないのはちゃんと理由があると思うのです。</p><p>本稿では、PHPのエスケープ処理用の関数を紹介しながら、その利用目的と、その関数を使わないで済ませる方法を説明します。</p><p><br /></p><h3 style="text-align: left;">SQL用のエスケープ関数</h3><p>セキュリティとエスケープといえば、真っ先に思いつくのがSQL文字列のエスケープです。関数名にescapeが含まれるものとして下記があります。これらのエイリアスもありますが、割愛します。</p><p></p><ul style="text-align: left;"><li><a href="https://www.php.net/manual/ja/function.mysql-escape-string.php">mysql_escape_string</a></li><li><a href="https://www.php.net/manual/ja/function.mysql-real-escape-string.php">mysql_real_escape_string</a></li><li><a href="https://www.php.net/manual/ja/mysqli.real-escape-string.php">mysqli_real_escape_string</a></li><li><a href="https://www.php.net/manual/ja/function.pg-escape-string">pg_escape_string</a></li><li><a href="https://www.php.net/manual/ja/function.pg-escape-bytea.php">pg_escape_bytea</a></li><li><a href="https://www.php.net/manual/ja/function.pg-escape-identifier.php">pg_escape_identifier</a></li><li><a href="https://www.php.net/manual/ja/function.pg-escape-literal.php">pg_escape_literal</a></li><li>sqlite_escape_string(廃止)</li><li><a href="https://www.php.net/manual/ja/sqlite3.escapestring.php">SQLite3::escapeString</a></li><li><a href="https://www.php.net/manual/ja/function.db2-escape-string.php">db2_escape_string</a></li><li><a href="https://www.php.net/manual/ja/function.cubrid-real-escape-string">cubrid_real_escape_string</a></li></ul><p></p><p>これらのうち、pg_escape_identifierは識別子のエスケープをする関数です。SQLの識別子の問題については、下記の連作を御覧ください。</p><p></p><ul style="text-align: left;"><li><a href="https://blog.tokumaru.org/2013/12/sql.html">間違いだらけのSQL識別子エスケープ</a></li><li><a href="https://blog.tokumaru.org/2013/12/sql_26.html">SQL識別子エスケープのバグの事例</a></li><li><a href="https://blog.tokumaru.org/2013/12/sql_27.html">SQL識別子は結局どうすればよいか</a><br /></li></ul><p></p><p>結論として、SQL識別子のエスケープは、phpMyAdminのようなツールを開発する場合は必要になるが、一般のアプリケーション開発であれば識別子のエスケープをしなくてもすむようにした方がよい、というものです。</p><p>また、pg_escape_literalは、エスケープするだけでなく文字列を引用符で囲ってリテラルの形にしてくれるものです。PDOだと以下のメソッドが該当します。</p><p></p><ul style="text-align: left;"><li><a href="https://www.php.net/manual/ja/pdo.quote.php">PDO::quote</a></li></ul><p></p><p>単にエスケープだけするのに比べて、引用符で囲ってリテラルを生成する関数・メソッド(pg_escape_literalやPDO::quote)を使ったほうが安全です。これらquote系の関数は、対象が数値の場合も適切に処理してくれることが期待できるからです(ただし、伝統的にPDOは数値の扱いがイマイチですが)。</p><p></p><ul style="text-align: left;"><li><a href="https://blog.tokumaru.org/2011/08/pdo.html">PDOにおける一応の安全宣言と残る問題点</a></li><li><a href="https://blog.tokumaru.org/2016/04/pdoint.html">PDOのサンプルで数値をバインドする際にintにキャストしている理由</a></li><li><a href="https://blog.tokumaru.org/2020/03/php-72-pdo-param-int.html">PHP 7.2以降におけるPDO::PARAM_INTの仕様変更</a></li></ul><p></p><p>さて、SQLインジェクション対策にはエスケープが重要とはよく言われることですが、数値が対象の場合はエスケープしない(してはいけない)ことや、データベース・ソフトウェア毎にエスケープのやり方が異なるなど、現実には難易度の高い処理です。このあたりの事情についてはIPAの安全なSQLの呼び出し方をぜひご一読ください。</p><p></p><ul style="text-align: left;"><li><a href="https://www.php.net/manual/ja/sqlite3.escapestring.php">安全なSQLの呼び出し方</a></li></ul><p></p><p>結論としては、リテラル中の文字列をエスケープするのではなく、プレースホルダを用いてSQLを呼び出すべきであり、さらに言えば、今どきのモダンなアプリケーション・フレームワークを使う場合は、フレームワーク付属のO/Rマッパーを使うので、現実のアプリケーション開発でSQLのエスケープ関数を利用するケースは多くはないと思います。</p><p><br /></p><h3 style="text-align: left;">HTMLエスケープ</h3><p>HTMLエスケープ、すなわち文字を文字参照に変換する関数には以下があります。</p><p></p><ul style="text-align: left;"><li><a href="https://www.php.net/manual/ja/function.htmlspecialchars.php">htmlspecialchars</a></li><li><a href="https://www.php.net/manual/ja/function.htmlentities.php">htmlentities</a></li></ul><p></p><p>通常クロスサイトスクリプティング対策にはhtmlspecialchars関数を用いますが、こちらについてもフレームワークを使う場合はテンプレートエンジンの機能で自動エスケープさせるのが吉でしょう。</p><p>また、PHPはJavaScriptの文字列リテラルのエスケープについては提供していません。json_encodeでできないこともないですが、JavaScriptの動的生成は非常に難易度の高い処理なので、拙著2版では、カスタムデータ属性に値を書いて、それをJavaScriptから参照する方法を推奨しています。これにより、JavaScriptのエスケープではなくHTMLのエスケープで統一できます。</p><p></p><ul style="text-align: left;"><li><a href="https://www.amazon.co.jp/gp/product/4797393165/?tag=jisakucompile-22">体系的に学ぶ 安全なWebアプリケーションの作り方 第2版</a></li></ul><p></p><p><br /></p><h3 style="text-align: left;">OSコマンド(シェル)</h3><p>PHPは、system関数などOSコマンド呼び出しのために、シェル形式のエスケープ関数を提供しています。これは他の言語ではあまり見当たらず、比較的珍しい機能だと思います。</p><p></p><ul style="text-align: left;"><li><a href="https://www.php.net/manual/ja/function.escapeshellarg.php">escapeshellarg</a></li><li><a href="https://www.php.net/manual/ja/function.escapeshellcmd.php">escapeshellcmd</a></li></ul><p></p><p>これらのうち、escapeshellcmdについては仕様にまつわる脆弱性があり修正も不可能ということで、使用禁止となっています。</p><p></p><blockquote>警告<br />escapeshellcmd() はコマンド文字列全体に適用しなければなりません。 また、そうしたところで、まだ<span style="color: red;">任意の数の引数を渡すことによる攻撃を許してしまいます</span>。 単一の引数をエスケープするには、かわりに escapeshellarg() を使わねばなりません。<br /><a href="https://www.php.net/manual/ja/function.escapeshellcmd.php">https://www.php.net/manual/ja/function.escapeshellcmd.php</a> より引用</blockquote><p></p><p>この脆弱性は、下記ブログ記事で私が報告したものです。</p><p></p><ul style="text-align: left;"><li><a href="https://blog.tokumaru.org/2011/01/php-escapeshellcmd-is-dangerous.html">PHPのescapeshellcmdの危険性</a></li></ul><p></p><p>この脆弱性を見つけたきっかけは、拙著の初版を書いている際に、OSコマンドインジェクション対策としてエスケープ処理で本当に大丈夫だろうかと心配になり色々試している中で見つけたものです。心配になった理由は、シェルのエスケープ処理の難易度が高いためです。</p><p>そして、この心配はPHPMailerの脆弱性CVE-2016-10033という形で現実のものになりました。</p><p></p><ul style="text-align: left;"><li><a href="https://blog.tokumaru.org/2016/12/PHPMailer-Vulnerability-CVE-2016-10033.html">PHPMailerの脆弱性CVE-2016-10033について解析した</a></li><li><a href="https://blog.tokumaru.org/2016/12/phpescapeshellcmdmailcve-2016-10033.html">PHPのescapeshellcmdを巡る冒険はmail関数を経てCVE-2016-10033に至った</a></li><li><a href="https://blog.tokumaru.org/2017/05/phpmailer-cve-2016-10033-exim4-wordpress4.6.html">PHPMailerの脆弱性CVE-2016-10033はExim4やWordPress4.6でも影響があった</a></li></ul><p></p><p>さて、シェル形式のエスケープ処理関数は比較的珍しいと書きましたが、他の言語ではどうしているのでしょうか。実はもっと良い方法、すなわちシェルを経由しないコマンド呼び出しが提供されています。以下の記事で解説しています。</p><p></p><ul style="text-align: left;"><li><a href="https://blog.tokumaru.org/2013/12/php_21.html">PHPだってシェル経由でないコマンド呼び出し機能が欲しい</a></li></ul><p></p><p>そして、PHP 7.4にて、ようやくシェル経由でないOSコマンド呼び出しが提供されました。</p><p></p><ul style="text-align: left;"><li><a href="https://blog.tokumaru.org/2019/12/osphp74.html">シェルを経由しないOSコマンド呼び出しがPHP7.4で実装された</a></li></ul><p></p><p>このため、今後はOSコマンドインジェクション対策としては、エスケープ処理ではなく、proc_openによる「シェル経由でないOSコマンド呼び出し」を用いるべきだと思います。この場合はescapeshellarg等によるエスケープ処理は必要ありません。</p><p><br /></p><h3 style="text-align: left;">正規表現</h3><p>PHPは正規表現用のエスケープ関数も提供しています。</p><p></p><ul style="text-align: left;"><li><a href="https://www.php.net/manual/ja/function.preg-quote.php">preg_quote</a></li></ul><p></p><p>これはどのような時に用いるかと言うと、正規表現パータン中に外部入力を含める場合です。そんなことあるのかと思ってしまいますが、これが原因でRCE(リモートコード実行)可能な脆弱性が混入した例があります。</p><p></p><ul style="text-align: left;"><li><a href="https://blog.tokumaru.org/2013/05/phpMyAdmin3.5.8-script-injection-CVE-2013-3238.html">phpMyAdmin3.5.8以前に任意のスクリプト実行を許す脆弱性(CVE-2013-3238)</a></li></ul><p></p><p>なので、外部入力を正規表現パターンに含める場合はpreg_quoteを使いましょう…とは私は思いません。そんな危険な行為そのものを避けるべきだと思います。現に、上記で紹介したphpMyAdminの脆弱性(CVE-2013-3238)も、当該箇所で正規表現を使わない形で改修しています。詳細は上記ブログ記事を参照ください。</p><p>ということで、preg_quoteもよほどのことがない限り使わない関数だと私は思います。</p><p><br /></p><h3 style="text-align: left;">URL</h3><p>URL中に記号文字やマルチバイト文字を含める場合はパーセントエンコード(URLエンコード)しますが、これも一種のエスケープ処理と考えることができます。PHPで用意されているパーセントエンコードの関数には以下があります。</p><p></p><ul style="text-align: left;"><li><a href="https://www.php.net/manual/ja/function.urlencode">urlencode</a></li><li><a href="https://www.php.net/manual/ja/function.rawurlencode.php">rawurlencode</a></li><li><a href="https://www.php.net/manual/ja/function.curl-escape.php">curl_escape</a></li><li><a href="https://www.php.net/manual/ja/function.oauth-urlencode">oauth_urlencode</a></li></ul><p></p><div>概ね似たような処理を行う関数が4種類もあるのがPHPらしいですが、通常はrawurlencodeを用いればよいでしょう。urlencodeの方は、空白を%20ではなくプラス記号にエンコードします。こちらはHTMLフォームのapplication/x-www-form-urlencoded形式ということでしょうが、その差が問題になるケースはあまりないと思います。</div><p>URL中で記号をエスケープしないとまずい理由は、クエリ文字列では = や & の記号が区切り文字として特別な意味を持つからです。たとえば、a=値という形式で、値が「b&c=d」だとすると、全体ではa=b&c=dとなって、値がbのみで千切れてしまいます。</p><p>このように、URLのエスケープ(パーセントエンコード)自体は必要な処理ですが、目的がクエリ文字列の組み立てである場合は、rawurlencode等よりも便利な機能があります。それは、<a href="https://www.php.net/manual/ja/function.http-build-query.php">http_build_query</a> です。この関数は配列を引数として、クエリ文字列形式の文字列を返します。この過程でパーセントエンコードもしてくれます。特に、項目数が多い場合に便利ですが、単一の場合でもhttp_build_queryを使うことをお勧めします。一般的に、ミクロな処理の関数を組み合わせて使うよりも、マクロな機能の関数を用いた方がプログラムの意図が分かりやすくなり、バグ、ひいては脆弱性が入る余地が少なくなるからです。
</p><p><br /></p><h3 style="text-align: left;">LDAP</h3><p>古来から知られたLDAPインジェクションという脆弱性があります。私自身は、LDAPを使った検索で「*」を指定したら全件マッチになった例は脆弱性診断で見つけたことがあります。また、LDAPのクエリの式を変更するという、文字通りのインジェクションもありえます。</p><p>LDAPのクエリに外部入力を含めることは十分考えられるため、エスケープが必要になる場合があります。PHPでは5.6から下記の関数でLDAPのエスケープを提供しています。</p><p></p><ul style="text-align: left;"><li><a href="https://www.php.net/manual/ja/function.ldap-escape">ldap_escape</a></li></ul><p></p><p><br /></p><h3 style="text-align: left;">目的不明なエスケープ関数</h3><p>以下の3つの関数はPHP4時代からある由緒正しいものですが、利用シーンがよく分かりません。</p><p></p><ul style="text-align: left;"><li><a href="https://www.php.net/manual/ja/function.addcslashes.php">addcslashes</a></li><li><a href="https://www.php.net/manual/ja/function.addslashes.php">addslashes</a></li><li><a href="https://www.php.net/manual/ja/function.quotemeta.php">quotemeta</a></li></ul><p></p><p>これらのうち、addcslashesはリファレンスに「C 言語と同様にスラッシュで文字列をクォートする」とありますが、スラッシュ「/」ではなくバックスラッシュ「\」ですよね。PHP言語でC言語のソースコードを生成する時に使うのでしょうか。</p><p>もっとわけが分からないのは、addslashesとquotemetaです。</p><p></p><blockquote><p><b>addslashes </b></p></blockquote><blockquote><p>エスケープすべき文字の前にバックスラッシュを付けて返します。 エスケープすべき文字とは、以下のとおりです。</p><p></p><ul style="text-align: left;"><li>シングルクォート (')</li><li>ダブルクォート (")</li><li>バックスラッシュ (\)</li><li>NUL (null バイト)</li></ul><p></p><p><a href="https://www.php.net/manual/ja/function.addslashes.php">https://www.php.net/manual/ja/function.addslashes.php</a> より引用</p><p></p></blockquote><p>なんとなく、MySQLのエスケープルールを思い起こしますが、わざわざ以下のように「SQLインジェクション対策に使うな」と念押しがしてあります。</p><p></p><blockquote>addslashes() 関数は、 <span style="color: red;">SQLインジェクション を防止しようとして誤った使い方がされることがあります。 この関数を使うのではなく</span>、データベース特有のエスケープ関数 および/もしくは プリペアドステートメントを使うようにしてください。</blockquote><p></p><p>quotemetaの説明は以下のとおりで、正規表現のエスケープを連想しますが、</p><p></p><blockquote><p><b>quotemeta </b></p></blockquote><blockquote><p>文字列 str について、</p><pre>. \ + * ? [ ^ ] ( $ )</pre><p>の前にバックスラッシュ文字 (\) でクォートして返します</p><p><a href="https://www.php.net/manual/ja/function.quotemeta.php">https://www.php.net/manual/ja/function.quotemeta.php</a> より引用</p></blockquote><p></p><p>正規表現用とするには、ハイフン「-」が抜けていますし、正規表現用にはpreg_quoteがあるので、そちらを使うべきです(そもそも正規表現パターンに外部入力を含めるなということはありますが)。また、Perlにも同名の関数がありますが、こちらは記号類をすべてエスケープするので仕様が異なります。ということで、quotemetaの使い道は謎です。ご存じの方は教えてください。</p><p><br /></p><h3 style="text-align: left;">まとめ</h3><p>PHPに多数用意されているエスケープ用関数について紹介しました。</p><p>エスケープすべき局面でエスケープを怠る、あるいはエスケープ方法が不適切だと脆弱性になります。そして、エスケープ処理はしばしば難しいのです。エスケープをなめてはいけない。そして、本稿を書くにあたってケッサクな例を見かけました。以下は富士通の<a href="https://software.fujitsu.com/jp/manual/manualfiles/M050008/B1WN5851/01Z200/irep02/irep0034.htm">Interstage Application Server Smart Repository運用ガイド</a>からの引用ですが、</p><p></p><blockquote><p><b>SDK(JNDI)の場合</b></p><p> JNDIは注意が必要です。</p><p> LDAP、JNDI、Java言語それぞれで、\(エンマーク(バックスラッシュ))文字を特殊文字と扱うためです。いくつか例を示します。</p><p> \を含むcn属性を指定するとします。</p><p>a\b</p><p> LDAPの規約により\をエスケープする必要があります。</p><p>cn=a\\b</p><p> JNDIの仕様により、この名前を指定するために、それぞれの\をエスケープする必要があります。</p><p>cn=a\\\\b</p><p> Java言語の仕様により、この名前を文字列リテラルとして指定するために、それぞれの\をエスケープする必要があります。</p><p>String name1 = "cn=a\\\\\\\\b";</p><p> 同様に、,(カンマ)、"(ダブルクォーテーション)の場合はそれぞれ次のようになります。</p><p>String name2 = "cn=a\\\\,b";<br />String name3 = "cn=a\\\\\"b";</p><p> 最初の例をJNDIで使用する場合は、次のように記述します。</p><p>String name = "cn=a\\\\\\\\b,o=Fujitsu\\\\, Inc.,c=jp";</p><p></p></blockquote><p>多重のエスケープが必要なために、バックスラッシュを8個重ねる必要が生じています。</p><p>実は、私も類似の記事を過去に書いたことがあります。以下の記事ではバックスラッシュを12個重ねています。</p><p></p><ul style="text-align: left;"><li><a href="https://ockeghem.hatenablog.jp/entry/20080711/p1">「\%foo」から始まる文字列を検索するクエリをハードコーディングする</a></li></ul><p></p><p>ということで、まとめは以下の通りです。</p><p></p><ul style="text-align: left;"><li>エスケープすべき局面でエスケープを忘れると脆弱性になる(前提)</li><li>エスケープ処理は意外にややこしい(現実)</li><li>エスケープしなくてもよい書き方(SQLのプレースホルダ、シェル経由しないOSコマンド呼び出し等)があればそちらを採用しよう(お勧め)</li></ul><p></p><p><br /></p>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com2tag:blogger.com,1999:blog-3689516124953269065.post-29158278498419437722020-12-19T17:21:00.000+09:002020-12-19T17:21:27.471+09:00PHPビルドの楽しみ、あるいはポケモンとしてのPHPについて<p>この記事は<a href="https://qiita.com/advent-calendar/2020/php">PHP Advent Calendar 2020</a>の19日目です。18日目は<a href="https://qiita.com/You-saku">@You-saku</a>さんの<a href="https://qiita.com/You-saku/items/9ef9f6b996192e21a371">PHPでYoutubeのAPIを利用したい</a>でした。</p><div><br /></div><div>私は以前、PHPのすべてのバージョンを使う環境として、<a href="https://hnw.hatenablog.com/entries/2009/01/10">phpall</a>、<a href="https://blog.tokumaru.org/2016/12/phpcgiall.html">phpcgiall</a>、<a href="https://blog.tokumaru.org/2016/12/modphpall.html">modphpall</a>を紹介してきました。これらは、PHPのすべてのバージョンをそれぞれコマンドライン、CGI、mod_phpの形式で動作させるものです。</div><div>現在では、PHPのバージョンを切り替えて動作させる仕組みは<a href="https://github.com/phpenv/phpenv">phpenv</a>、<a href="https://github.com/phpbrew/phpbrew">phpbrew</a>等何種類もありますし、<a href="https://github.com/php-build/php-build">php-build</a>という、PHPをビルドするというそのものずばりの仕組みも広く使われています。</div><div>しかし、短いPHPスクリプトをPHPの全バージョンで試すという私の使用法からは、phpall(およびその派生としてphpcgiall、modphpall)はメリットがあり、私は今もPHPのビルドを続けています。</div><div><br /></div><div>そう、私は今も、PHPのすべてのバージョンのバイナリをCLI、CGI、Apacheモジュールの形式で持っているのです。そして、元々は目的があって始めたことなのですが、いまや、「PHPのすべてのバージョンをコンプする」こと自体が目的化してしまっています。この感覚、何かに似ているなと思い返したら…そうです。ポケモンを集めるような感覚なのです。いまや、</div><div><br /></div><h1 style="text-align: left;">徳丸にとってPHPはポケモン</h1><div><br /></div><div>と言っても過言ではありません。</div><div>PHPがポケモンだという主張は認めていただくとして、PHPにもポケモン同様にレアキャラがあるでしょうか…</div><div><br /></div><div>あります。ビルドが困難であればあるほど、レアなポケモン(に相当するPHP)と言ってよいでしょう。</div><div>経験上、以下の条件を満たすPHPがレアな(ビルドが困難な)ものと言えます。</div><div><ul style="text-align: left;"><li>古いバージョンのPHP(PHP4、PHP5.0、5.1、PHP5.2等)</li><li>新しい環境で動く(Ubuntu 20.04等)</li><li>できるだけ多くの拡張モジュールを備える</li><li>32ビット版よりも64ビット版</li></ul></div><div>以下、その理由を簡単に説明しましょう。</div><div><br /></div><h4 style="text-align: left;">古いバージョンのPHP</h4><div><div>次の項と関連しますが、古いPHPはビルドすること自体が困難です。その一端は<a href="https://twitter.com/hnw">@hnw</a>さんの以下の記事で読むことができます。</div><div><br /></div><div><a href="https://hnw.hatenablog.com/entry/20090110">phpallコマンドでPHPの全バージョンの挙動を試す</a></div><div><br /></div><div>古いPHPがビルドが困難な理由は以下の通りです。</div><div><ul style="text-align: left;"><li>Cコンパイラの新バージョンでは文法チェックが厳しくなった結果、コンパイルエラーになりコンパイルできない</li><li>PHPが利用するライブラリの非互換なバージョンアップ</li></ul></div><div>これらの回避策としては以下が考えられます。</div><div><ul style="text-align: left;"><li>古いコンパイラ(gcc)を使う</li><li>コンパイルエラーを緩和するオプションを設定する</li><li>古いライブラリを使う</li><li>PHPにパッチをあてる</li></ul></div><div>最終的にはPHPにパッチをあてざるを得ない場合もありますが、これはできれば避けたいところです。新しいgccでコンパイルエラーになる箇所を見ていると「これ、単にバグじゃないか」と思うものが多い(それ以外ではライブラリの非互換)のですが、他の方法で回避できるのであれば回避したい…ということで、古いgccを試したりしましたが、今は以下のgccのオプションで回避して、最低限PHPソースにパッチをあてています。</div><div><div><span style="background-color: white;"></span></div></div><blockquote><div><div><span style="background-color: white;">-</span><span face=""Helvetica Neue", Helvetica, Arial, "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", メイリオ, Meiryo, "MS Pゴシック", "MS PGothic", sans-serif" style="background-color: white; color: #222222; font-size: 15px;">std=gnu89</span></div><div>-fgnu89-inline</div></div><div></div></blockquote><div>ライブラリも結構ハマるところでして、以下は複数のバージョンを使っています。</div><div><ul style="text-align: left;"><li>zlib(1.1.4、1.2.11)</li><li>openssl(0.9.8、1.1.1)</li><li>curl(7.15.0、7.16.0、7.68.0、7.72.0)</li><li>libxml2(2.7.8、2.9.8、2.9.10)</li></ul></div><div>意外にハマるのがcurl(libcurl)でして、非互換なバージョンアップのため、PHPのバージョンによってcurlのバージョンも変える必要がありました。</div><div><br /></div><h4 style="text-align: left;">新しい環境で動く</h4><div>前項の裏返しになりますが、新しい環境(OS)であるほど、古いPHPをビルドすることが困難になります。特にライブラリの非互換は避けようのないところで、OS(Linuxディストリビューション)にバンドルされているライブラリでうまくビルドできない場合は、古いライブラリを探してきてビルドすることから始めなければなりません。ライブラリ毎にビルド方法に癖があったりして苦労する場合もありますが、そこがまた楽しいのです。</div><div><br /></div><h4 style="text-align: left;">できるだけ多くの拡張モジュールを備える</h4><div>前項と関連しますが、できるだけ多くの拡張モジュールを指定した方がビルドの難易度が上がります。OSにバンドルされていないライブラリや、バンドルはされているが非互換のため古いライブラリを別途導入しないケースはなおさらです。</div><div><br /></div><h4 style="text-align: left;">32ビット版よりも64ビット版</h4></div><div>意外なことを書くと思われるかもしれませんが、古いPHP(4.1、4.2あたり)はAMD64でのビルドが困難です。ビルド自体はできるのですが、正常に動作しません。この時代はまだAMD64の現物がなかった(AMD64の出荷は2003年4月)からと思われます。PHP 4.0はビルドでき動作もするので、調整すればPHP 4.1や4.2も動作すると思うのですが、私はまだ成功していません。</div><div><br /></div><h4 style="text-align: left;">phpallをUbuntu16.04(32ビット)からUbuntu20.04(64ビット)に移行</h4><div>さて、著者はphpallの環境を当初Ubuntu12.04(32ビット)に構築し、その後Ubuntu16.04(32ビット)に移行していましたが、PHP 8.0α版が出た際にUbuntu20.04(64ビット)に移行しました。</div><div>そのきっかけとなったのは、PHP 8.0から導入されたJITを試そうとして時のことです。どうもx86(32ビット)では、JITは使えないようなのです。下図はphpinfoからの該当部分です。</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-uMsbtPrhsx4/X92pbXRlUGI/AAAAAAAAWOg/YWiDppEPS2AHl9YochFptL28kg5TpqEeQCLcBGAsYHQ/s619/opcache32.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="179" data-original-width="619" src="https://1.bp.blogspot.com/-uMsbtPrhsx4/X92pbXRlUGI/AAAAAAAAWOg/YWiDppEPS2AHl9YochFptL28kg5TpqEeQCLcBGAsYHQ/s16000/opcache32.png" /></a></div><br /><div><a href="https://wiki.php.net/rfc/jit">JITのRFC</a>ではx86もサポートされていると書かれていましたが、本番投入は見送られたのでしょうか。私は、phpallを64ビット環境に移す決断をしました。以下はUbuntu20.04(64ビット)でのphpinfoで、JITが有効化できていることがわかります。</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-SV0h-8SIn-U/X92sMd7BGlI/AAAAAAAAWOs/1KHMTMa_7Zo6pDftbHBNkdMn2DnhFAhcgCLcBGAsYHQ/s619/opcache64.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="183" data-original-width="619" src="https://1.bp.blogspot.com/-SV0h-8SIn-U/X92sMd7BGlI/AAAAAAAAWOs/1KHMTMa_7Zo6pDftbHBNkdMn2DnhFAhcgCLcBGAsYHQ/s16000/opcache64.png" /></a></div><div><br /></div><h4 style="text-align: left;">残る課題</h4><div>楽しみとしてのPHPビルドについて紹介しましたが、現状課題が残っています。前述のように、64ビット環境でのPHP4.1と4.2のビルドには成功していないからです。ここは私の技術の至らなさではあるのですが、将来の楽しみにとっておこうと思います。やり方をご存知の方がいれば教えていただけるとありがたいです。</div>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-27878314716018683002020-11-14T21:18:00.001+09:002020-11-15T10:55:28.237+09:00auじぶん銀行のフィッシングSMSが届いた<p>3日前に、auじぶん銀行の巧妙な不正出金についてYouTube動画を公開しました。みんな見てねー。</p><p><a href="https://www.youtube.com/watch?v=Xa0K9eooaCw">auじぶん銀行アプリに対する不正出金の驚くべき手口</a></p><p>そうしたところ、先程私のiPhoneに以下のようなSMSが届きました。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-SIRAVOwjLYE/X6_CR8fV5CI/AAAAAAAAV-E/NMxU5GEds1U8lT4BKKqhGtOpxXQcoVADgCLcBGAsYHQ/s375/IMG_2680s.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="168" data-original-width="375" src="https://1.bp.blogspot.com/-SIRAVOwjLYE/X6_CR8fV5CI/AAAAAAAAV-E/NMxU5GEds1U8lT4BKKqhGtOpxXQcoVADgCLcBGAsYHQ/s16000/IMG_2680s.png" /></a></div>ふーん、au自分銀行のときは宅配事業者を装ったSMSということでしたが、これはどうなんでしょうね。開いてみると…<div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-VLp4PffsQnU/X6_CtIJqnzI/AAAAAAAAV-Q/XzZUW4MdcXos7Fso5hvTuh1k3k30NBvfACLcBGAsYHQ/s375/IMG_2682s.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="284" data-original-width="375" src="https://1.bp.blogspot.com/-VLp4PffsQnU/X6_CtIJqnzI/AAAAAAAAV-Q/XzZUW4MdcXos7Fso5hvTuh1k3k30NBvfACLcBGAsYHQ/s320/IMG_2682s.png" width="320" /></a></div><br /><div>詐欺サイトの警告が出ていますが、構わずに開いてみると…</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-1gyK_wxPOio/X6_KsEqOYLI/AAAAAAAAV_g/Cu1h8HkwMVIzSa4RiKBAmuueDxVPYoCbwCLcBGAsYHQ/s350/IMG_2673s.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="173" data-original-width="350" src="https://1.bp.blogspot.com/-1gyK_wxPOio/X6_KsEqOYLI/AAAAAAAAV_g/Cu1h8HkwMVIzSa4RiKBAmuueDxVPYoCbwCLcBGAsYHQ/s16000/IMG_2673s.png" /></a></div><br /><div>きたきたきたー。これですよ。auじぶん銀行のフィッシング(SMSの場合はスミッシングと言いますが)サイトのようですよー。「閉じる」をタップすると…</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-rEirG4m58rc/X6_EWx0wQOI/AAAAAAAAV-0/DLppSX46Dz4AnyxpvEjpYRlRBVfA8lZfQCLcBGAsYHQ/s602/IMG_2674s.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="602" data-original-width="375" src="https://1.bp.blogspot.com/-rEirG4m58rc/X6_EWx0wQOI/AAAAAAAAV-0/DLppSX46Dz4AnyxpvEjpYRlRBVfA8lZfQCLcBGAsYHQ/s16000/IMG_2674s.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>うーむ、これがauじぶん銀行の本物のフィッシングサイトですよ。これかー。僕が作った偽サイトよりもきちんと作ってありますねー(笑い)。</div><div>ちなみに、本物のauじぶん銀行サイトはこちら。</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ckQHUeSwRAE/X6_EnScv3-I/AAAAAAAAV_A/rJJIC8qwpaYm_bTFo8MlggzuuD--kk82gCLcBGAsYHQ/s604/IMG_2683s.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="604" data-original-width="375" src="https://1.bp.blogspot.com/-ckQHUeSwRAE/X6_EnScv3-I/AAAAAAAAV_A/rJJIC8qwpaYm_bTFo8MlggzuuD--kk82gCLcBGAsYHQ/s16000/IMG_2683s.png" /></a></div><br /><div>よく似ていますね。お客様番号とログインパスワードの欄は共通ですが、偽物の方は暗証番号と誕生日を要求しています。私がデモ用に作った偽サイトは、さらにカタカナの氏名を要求していますが、氏名はサイトにログインすると容易にわかるので省略しているのでしょうね。</div><div><br /></div><div>せっかくの「本物の偽サイト」ですので、少し実験してみましょう。お客様番号等をでたらめな文字列にして、ログインボタンをタップしてみます。</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-x88LFX2KIyE/X6_GTXEWw3I/AAAAAAAAV_U/18iI_iXx5OUGJ_oq9_5MmVS4Qx40DSQsACLcBGAsYHQ/s375/IMG_2679s.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="322" data-original-width="375" src="https://1.bp.blogspot.com/-x88LFX2KIyE/X6_GTXEWw3I/AAAAAAAAV_U/18iI_iXx5OUGJ_oq9_5MmVS4Qx40DSQsACLcBGAsYHQ/s16000/IMG_2679s.png" /></a></div><br /><div>「サーバーに接続中です。このまましばらくお待ち下さい」というダイアログが表示され、しばらくたった後、「入力に誤りがあります。」というダイアログが表示されます。</div><div>これは、攻撃者が、入力内容(お客様番号とログインパスワード)を用いて本物サイトにログインを試み失敗したためと考えられます。このように、裏で本物のサイトと中継する形式のフィッシングを中継型フィッシングと呼びます。</div><div><br /></div><div>ログインパスワードを得るためだけであれば、中継型にする必要はないのですが、オンラインバンキングの多くはパスワードだけでは送金や出金はできず、追加の認証を求めるわけで、その追加の認証を突破する目的で行われます。</div><div>ちなみに、au自分銀行の場合は、スマホアプリかカード番号裏の乱数表が用いられますが、<a href="https://www.jibunbank.co.jp/announcement/2020/1014_01.html">こちらのリリース</a>で公表されている手口の場合は、以下を突破していると考えられます。</div><div><ul style="text-align: left;"><li>キャッシュカード暗証番号</li><li>キャッシュカードの裏面にある確認番号表の数字</li><li>お客さま携帯電話宛てに発行されたワンタイムパスワード</li></ul></div><div>実際にどうなっているかは、本物の会員番号とパスワードを入力すればわかるのですが、さすがにそれはできないので、ここまででやめました。</div><div><br /></div><div>おそらくは、冒頭に紹介した動画の手口かと思いますので、興味のある方は動画の方を御覧ください。</div><div><p><a href="https://www.youtube.com/watch?v=Xa0K9eooaCw">auじぶん銀行アプリに対する不正出金の驚くべき手口</a></p><p>フィッシングは恐ろしい手口であり、<a href="http://web.archive.org/web/20201101131750/https://www.nishinippon.co.jp/item/n/654091/">こちらの報道(アーカイブ)</a>では、「警察庁によると、6、7月に同様の被害が全国で32件(約1060万円)確認されている」とあります。くれぐれもご注意下さい。また、中継型フィッシングを想定すると、二段階認証も突破される可能性が高いので、サイト運営者側もそれを想定した対策が求められます。</p><p><br /></p><h4 style="text-align: left;">追記(2020年11月15日11:00)</h4><p>今朝当該サイトにアクセスしてみたところ、DNSの設定が削除され、当該IPアドレスにもアクセスできない状態になっていました。関係者の皆様ありがとうございました。</p></div>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-40297934715325052542020-05-30T15:16:00.003+09:002020-05-30T15:40:49.449+09:00PHPの脆弱性CVE-2018-17082はApacheの脆弱性CVE-2015-3183を修正したら発現するようになったというお話最近自宅引きこもりで時間ができたので、YouTube動画を投稿するようになりました。みんな見てねー。<br />
<br />
<a href="https://www.youtube.com/user/hiroshitokumaru/featured">徳丸浩のウェブセキュリティ講座</a><br />
<br />
そんなことで、次の動画は、お気に入りのPHPの脆弱性 <a href="https://blog.tokumaru.org/2018/09/cve-2018-17082-cache-poisoning.html">CVE-2018-17082</a> を取り上げようと思ったんですよ。表向きXSSで出ているけど、<a href="https://www.scutum.jp/information/waf_tech_blog/2018/11/waf-blog-059.html">金床さんのツッコミ</a>にもあるように、実はHTTP Request Smuggling(HRS)だというやつです。でね、下準備であらためて調べていると、なんかよく分からない挙動がワラワラと出てくる。なんじゃ、こりゃ。CVE-2018-17082 全然分からない。僕は気分で CVE-2018-17082 を扱っている…<br />
<br />
で、雑に整理すると、以下のような感じなんです。<br />
<ul>
<li>古い環境だとCVE-2018-17082は発現しない(2015年以前)</li>
<li>少し古い環境だとCVE-2018-17082は発現する</li>
<li>新しい環境だとCVE-2018-17082は発現しない(2019年以降 …これは当たり前)</li>
</ul>
これ自体はよくあることです。古いバージョンには脆弱性がなく、途中のバージョンで作り込まれたってことね。でもね、CVE-2018-17082はPHP 5.0.0にもあるんですよ。PHP 5.0.0が出たのは2004年ですからね。これだと辻褄が合わない。<br />
<br />
試しに、自宅の環境の<a href="https://blog.tokumaru.org/2016/12/modphpall.html">modphpall</a>を使ってPHP 5.0.0でCVE-2018-17082を試すとちゃんと発現するわけですよ。PHPのバージョンは関係ない。<br />
<br />
で、次に、自宅の環境のCentOS/Debian/Ubuntuの各バージョン(<a href="https://ockeghem.pageful.app/post/item/8wASKvi0uUJ1GLN">パッチがまったくあたってないものとすべてのパッチがあたっているもの</a>…LinuxAllという大げさな名前にしているのはくれぐれも内緒だ)で試すと、先の現象になるわけです。あまり古いと脆弱性のあるPHPバージョンなのに発現しない。CentOS6と7なんかひどくて、パッチのあたってない環境だと発現しないのに、すべてのパッチがあたっていると逆に発現する。<br />
でも、ここにヒントがあると思ったわけですね。なぜって、<a href="https://access.redhat.com/security/cve/cve-2018-17082">CentOSはそもそもCVE-2018-17082のパッチは出してない</a>わけですよ。ならば、PHP以外に影響があるに違いない。ひょっとすると、Apacheのバージョンが影響しているのかも。考えてみれば、そうだよね。だってCVE-2018-17082は、PHPのApache2handlerの脆弱性だから、相方のApacheの実装に影響を受ける可能性は十分にある。<br />
<br />
なので、今度はApache 2.2と2.4のすべてのバージョンを導入した試験環境(1.3や2.0がないのにApacheAllという大げさな名前なのは内緒)で試したわけですよ。さいわい、リファレンス用としてPHP 5.3.3が入れてあるので、脆弱性が動くはずです。<br />
でためしたら、やはり古いApacheだとCVE-2018-17082は発現しなくて、以下のバージョンだと発現する。<br />
<ul>
<li>Apache 2.2.31以降</li>
<li>Apache 2.4.16以降</li>
</ul>
で、Apache 2.2.31で修正された脆弱性はなんだろう…と思って調べると、以下ですよ(これだけ)。<br />
<br />
<a href="https://httpd.apache.org/security/vulnerabilities_22.html">low: HTTP request smuggling attack against chunked request parser (CVE-2015-3183)</a><br />
<br />
思わず息を呑みました(誇張じゃないよ)。出た、HRS。これだ。これに違いない。<br />
ちなみに、<a href="https://github.com/apache/httpd/commit/e427c41257957b57036d5a549b260b6185d1dd73">CVE-2015-3183に対する修正はこちら</a>でして、コミットメッセージに「Limit accepted chunk-size to 2^63-1 and be strict about chunk-ext」とあるように、巨大なチャンクサイズへの対応でしょうね。<br />
でもね、皮肉な話じゃないですか。Apacheのあまり攻撃現実性がない脆弱性に対する修正の影響で、潜在的なPHPの脆弱性が顕在化して、こっちの方がはるかに攻撃の現実性があるわけですよ。別にApacheが悪い訳ではないですけどね。皮肉なもんだなーと思ったわけです。<br />
<br />
ということで、全てが綺麗に説明がついたので、安心して動画制作に戻ろうと思います。<br />
このように、私の動画は見えないところに手間をかけて制作していますんで、みんな見てくださいねー、よろしく!<br />
<br />
<a href="https://www.youtube.com/user/hiroshitokumaru/featured">徳丸浩のウェブセキュリティ講座</a>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-46340861172897031452020-03-27T14:24:00.000+09:002020-03-27T14:29:17.045+09:00PHP 7.2以降におけるPDO::PARAM_INTの仕様変更<h4>
サマリ</h4>
PHP 7.2以降、PDOの内部実装が変更された。動的プレースホルダ(エミュレーションOFF)にてバインド時にPDO::PARAM_INTを指定した場合、PHP 7.1までは文字列型としてバインドされていたが、PHP 7.2以降では整数型としてバインドされる。<br />
この変更により、従来PDOが内包していた「暗黙の型変換」は解消される一方、integerへの暗黙のキャストにより、整数の最大値を超えた場合に不具合が発生する可能性がある。<br />
<br />
<h4>
この記事を読むのに必要な前提知識</h4>
この記事は、以前の記事(下記)の続編のような形になっています。<br />
<br />
<a href="https://blog.tokumaru.org/2016/04/pdoint.html">PDOのサンプルで数値をバインドする際にintにキャストしている理由</a><br />
<br />
この記事では、PDOを用いたサンプルスクリプトでbindValue時にinteger型へのキャストを明示している理由を説明しています。パラメータを文字列として渡した場合、PDO::PARAM_INTにより整数型を指定しても、SQL文生成時に文字列リテラルとしてバインドされるため、「暗黙の型変換」により意図しない動作がおこり得ます。それを避けるために、値をintegerにキャストすることにより、SQL文には数値リテラルとして生成され、暗黙の型変換による不具合を避けることができます。一方、integer型へのキャストすると、整数の範囲を超えた場合にオーバーフローが起こる危険性があり、一長一短ではあります。<br />
<br />
<h4>
PHP 7.2での変更内容</h4>
PHP 7.2以降では、bindParamあるいはbindValue時にPDO::PARAM_INTを指定した場合、値はPHPのint型にキャストされます。ここで注意すべきことは、MySQLのINTではなく、PHPのinteger型にキャストされるため、32ビット環境では32ビット符号付き整数、64ビット環境では64ビット符号付き整数にキャストされ、その範囲を超えた場合はintegerの最小値あるいは最大値に変更されることです。<br />
<br />
<h4>
検証</h4>
検証のために、先のブログ記事と同じデータを用意します(再掲)。<br />
<blockquote class="tr_bq">
<pre>CREATE TABLE xdecimal (id DECIMAL(20)); -- DECIMAL(20)は10進20桁の数値型
INSERT INTO xdecimal VALUES (18015376320243459);
INSERT INTO xdecimal VALUES (18015376320243460);
INSERT INTO xdecimal VALUES (18015376320243461);
</pre>
</blockquote>
PHPサンプルは下記のとおりですが、暗黙の型変換を起こすために、WHERE句で + 0を追加しています。<br />
<blockquote class="tr_bq">
<pre><?php
$db = new PDO("mysql:host=127.0.0.1;dbname=test;charset=utf8", DBUSER, DBPASSWD);
$ps = $db->prepare("SELECT id FROM xdecimal WHERE id = :id + 0");
$id = '18015376320243461';
$ps->bindValue(':id', $id, PDO::PARAM_INT); // intへのキャストはしない
$ps->execute();
$row = $ps->fetch();
echo "$id->${row[0]}\n";
</pre>
</blockquote>
PHP 7.1.33で実行結果(32ビット環境、64ビット環境とも)<br />
<pre>【実行結果】18015376320243461 -> 18015376320243459
【実行されるSQL文】SELECT id FROM xdecimal WHERE id = <span style="color: red;">'18015376320243461'</span> + 0
</pre>
<br />
PHP 7.2.0での実行結果(64ビット環境)<br />
<pre>【実行結果】18015376320243461 -> 18015376320243461
【実行されるSQL文】SELECT id FROM xdecimal WHERE id = <span style="color: red;">18015376320243461</span> + 0
</pre>
<br />
PHP 7.2.0での実行結果(32ビット環境)<br />
<pre>【実行結果】18015376320243461 ->
【実行されるSQL文】SELECT id FROM xdecimal WHERE id = <span style="color: red;">2147483647</span> + 0
</pre>
<br />
端的な違いは、リテラルがシングルクォートで囲まれているか否か、すなわち、文字列リテラルか数値リテラルかです。PHP 7.2以降ではの変更、バインド時にPDO::PARAM_INTを指定した場合にバインド値が数値リテラルとして生成され、これはSQLの文法として本来の姿になったと考えます。これによる現実的なメリットは、文字列リテラルを数値に変換する際の「暗黙の型変換」が避けられ、正確な数値比較や演算が行えるようになったことです。<br />
<br />
<h4>
この変更による悪影響</h4>
PDO::PARAM_INT指定時に、値がいったんPHPのintegerにキャストされることから、integerの上下限を超えた値を指定した場合、強制的にPHP_INT_MAXまたはPHP_INT_MINに値が変更されます。具体例は、前述の「PHP 7.2.0での実行結果(32ビット環境)」を参照ください。integerの取りうる値の範囲は環境依存ですが、現在一般的な環境では以下のようになる場合が多いと思います。<br />
<br />
32ビット環境: -2147483648 ~ 2147483647<br />
64ビット環境: -9223372036854775808 ~ 9223372036854775807<br />
<br />
<h4>
PDOはどのように使えばよいか</h4>
今どき32ビット版のPHPを本番環境で使っている(かつPHP 7.2以降が動いている)環境はあまりないかもしれませんが、64ビット版であっても、数値の範囲について注意が必要です。PHP_INT_MAXを超える可能性がある値について、PDOはDecimal型のような型を指定する方法がないため、以下のように明示的なキャストを指定する方法などが考えられます。<br />
<blockquote class="tr_bq">
<pre>$ps = $db->prepare("SELECT id FROM xdecimal WHERE id = CAST(:id AS DECIMAL(20))"); // 明示的な型変換
$ps->bindValue(':id', $id, PDO::PARAM_STR); // いったん文字列型でバインドして、CASTで明示的に型変換
</pre>
</blockquote>
<br />
<h4>
まとめ</h4>
PHP 7.2におけるPDOの実装変更について紹介しました。この変更はドキュメント化されていないようで、かつ、PHP Internalsメーリングリストでも話題になっていないようです。もしドキュメント等を知っている方がおられたら教えてください。<br />
この変更は非互換かつドキュメント化されていないものであるので、ひょっとすると手違いなどで変更された可能性もあると思っています。ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-86828629771991931672019-12-09T11:06:00.002+09:002019-12-10T09:31:10.165+09:00SSRF対策としてAmazonから発表されたIMDSv2の効果と限界<h4>
サマリ</h4>
Capital OneからのSSRF攻撃による大規模な情報漏えい等をうけて、Amazonはインスタンスメタデータに対する保護策としてInstance Metadata Service (IMDSv2) を発表した。本稿では、IMDSv2が生まれた背景、使い方、効果、限界を説明した上で、SSRF対策におけるIMDSv2の位置づけについて説明する。<br />
<br />
<h4>
SSRFとは</h4>
SSRFは、下図のように「外部から直接アクセスできないエンドポイント」に対して、公開サーバーなどを踏み台としてアクセスする攻撃方法です。SSRF(Server Side Request Forgery)の詳細については過去記事「<a href="https://blog.tokumaru.org/2018/12/introduction-to-ssrf-server-side-request-forgery.html">SSRF(Server Side Request Forgery)徹底入門</a>」を参照ください。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-hQiG56xFklg/XexRRgD_UmI/AAAAAAAATLU/tj8bkKJsCNQkJOL02CniwFI0KRPcSwgNACLcBGAsYHQ/s1600/ssrf-imds.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="359" data-original-width="706" src="https://1.bp.blogspot.com/-hQiG56xFklg/XexRRgD_UmI/AAAAAAAATLU/tj8bkKJsCNQkJOL02CniwFI0KRPcSwgNACLcBGAsYHQ/s1600/ssrf-imds.png" /></a></div>
最終的な攻撃目標は多様ですが、近年問題になっているのが、クラウドサービスのインスタンス・メタデータを取得するAPIのエンドポイントです。有名なものがAmazon EC2の169.254.169.254(IMDS)ですが、類似の機能をクラウドサービス各社が提供しています。<br />
<ul>
<li><a href="https://gist.github.com/mrtc0/60ca6ba0fdfb4be0ba499c65932ab42e">cloud-service-metadata-api-list</a></li>
</ul>
先の記事でも紹介したSSRF脆弱なサンプルを以下に示します。これは、はてなブックマークのようなソーシャルブックマークアプリの「プレビュー機能」を想定しています。<br />
<blockquote>
<pre><?php
require_once('./htmlpurifier/library/HTMLPurifier.includes.php');
$purifier = new HTMLPurifier();
$ch = curl_init();
$url = $_GET['url'];
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$html = curl_exec($ch);
echo $purifier->purify($html);</pre>
</blockquote>
このスクリプトをAmazon EC2上においてSSRF攻撃すると、下図のようにIAMのクレデンシャルが表示されます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-OpaQkXUGrkA/XAXRMhbBhlI/AAAAAAAAQRk/3q4l-A594yoPL5-r9kEd9gcffQUnX-WswCPcBGAYYCw/s1600/ssrf-006x.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="289" data-original-width="628" src="https://1.bp.blogspot.com/-OpaQkXUGrkA/XAXRMhbBhlI/AAAAAAAAQRk/3q4l-A594yoPL5-r9kEd9gcffQUnX-WswCPcBGAYYCw/s1600/ssrf-006x.png" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
SSRF攻撃が一般の方にも話題になったのはCapital Oneからの1億人超の個人情報流出事件で、詳しくは以下の記事にまとめられています。<br />
<ul>
<li><a href="https://piyolog.hatenadiary.jp/entry/2019/08/06/062154">SSRF攻撃によるCapital Oneの個人情報流出についてまとめてみた - piyolog</a></li>
</ul>
また、SSRF攻撃の標準的な対策は、ネットワーク的な対策で、EC2の場合は以下のようなiptablesによる防御が従来から推奨されていました。<br />
<blockquote>
<pre class="src">sudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP</pre>
<br />
<a href="https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/instance_IAM_role.html">Amazon ECS コンテナインスタンスの IAM ロール - Amazon Elastic Container Service</a> より引用</blockquote>
ここまでが長い前置き(前提知識の確認)です。<br />
<br />
<h4>
EC2インスタンスメタデータサービスv2(IMDSv2)とは</h4>
この状況に対して、Amazonが批判されたり、Amazonの責任ではないという反論があったりしていましたが、Amazonは今年の11月20日オフィシャルブログにて、<a href="https://aws.amazon.com/jp/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/">Instance Metadata Service v2(IMDSv2) を発表</a>しました。以下は、クラスメソッドの<a href="https://dev.classmethod.jp/cloud/aws/ec2-imdsv2-release/">臼田さんのブログ記事</a>から要点の引用です。<br />
<ul>
<li>v2へのアクセスには事前に取得したTokenを必須とする</li>
<ul>
<li>TokenはPUTで取得する必要がある</li>
<li>Tokenリクエスト時に有効期限(秒)を設定できる</li>
<li>Tokenはヘッダに入れてリクエストする必要がある</li>
</ul>
<li>v1を無効化できる(デフォルトでは併用可能)</li>
<li><span style="background-color: #fcfcfc; color: #222222; font-family: "hiragino kaku gothic pro" , "meiryo" , "ms pgothic" , sans-serif; font-size: 16px;">メタデータサービス自体を無効化できる</span></li>
</ul>
IMDSv2によるメタデータ取得は以下のようになります。まずは、トークンの取得<br />
<blockquote class="tr_bq">
<pre>$ curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 60"
<span style="color: red;">AQAAAKunSEcqfWQgz1E-ryJ3fdWDoOkbn8Nn4h2C6qN6nP56npog8Q==</span>$</pre>
</blockquote>
赤字のBASE64っぽいものがトークンです。TTLを60秒としているので、この値は既に無効です。PUTメソッドとX-aws-ec2-metadata-token-ttl-secondsヘッダを要求することで、攻撃難易度を上げています。<br />
続いて、トークンを利用したメタデータの取得です。X-aws-ec2-metadata-tokenヘッダにより、先程取得したトークンを指定します。<br />
<blockquote class="tr_bq">
<pre>$ curl -H "X-aws-ec2-metadata-token: AQAAAKunSEcqfWQgz1E-ryJ3fdWDoOkbn8Nn4h2C6qN6nP56npog8Q==" http://169.254.169.254/latest/meta-data/iam/info/
{
"Code" : "Success",
"LastUpdated" : "2019-12-08T02:32:19Z",
"InstanceProfileArn" : "arn:aws:iam::999999999999:instance-profile/test-role",
"InstanceProfileId" : "ZZZZZZZZZZZZZZZZZZZZZ"
}$</pre>
</blockquote>
これだけだと、IMDSv1が有効になっているのでSSRF攻撃は緩和されません。IMDSv1を無効化するには、AWSCLIから以下のように --http-tokens を required に設定します。<br />
<blockquote class="tr_bq">
<pre>$ aws ec2 modify-instance-metadata-options --instance-id i-FFFFFFFFFFFFFFF <span style="color: red;">--http-tokens required</span> --http-endpoint enabled
{
"InstanceId": "i-FFFFFFFFFFFFFFFFF",
"InstanceMetadataOptions": {
"State": "pending",
"HttpTokens": "required",
"HttpPutResponseHopLimit": 1,
"HttpEndpoint": "enabled"
}
}
$</pre>
</blockquote>
この状態で先の攻撃をすると、以下のように攻撃は防御されます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-uQaZY2rDhL4/Xexoj85Rf5I/AAAAAAAATLg/5f73J3IND3Ej5e7bU5DRkgI870hA4i2_ACLcBGAsYHQ/s1600/ssrf-v1-blocked.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="187" data-original-width="548" src="https://1.bp.blogspot.com/-uQaZY2rDhL4/Xexoj85Rf5I/AAAAAAAATLg/5f73J3IND3Ej5e7bU5DRkgI870hA4i2_ACLcBGAsYHQ/s1600/ssrf-v1-blocked.png" /></a></div>
<br />
SSRF攻撃の文脈でPUTメソッドやカスタムHTTPリクエストヘッダを指定することは難しそうなので、「これだけでSSRF対策は十分ではないか」と思う人もいそうですが、実は攻撃は可能です。<br />
<br />
<h4>
Gopherプロトコルとは</h4>
以前からSSRF界隈ではGopherプロトコルの活用が話題となっていて、はせがわようすけさんが分かりやすいスライドで紹介されています。<br />
<ul>
<li><a href="https://speakerdeck.com/hasegawayosuke/ssrfji-chu">SSRF基礎 - Speaker Deck</a></li>
</ul>
このスライドの12ページからがGopherを用いた攻撃手法についての説明です…が、このスライドは今年の9月18日の講演のものですので、当時存在しなかったIMDSv2についての言及はありません。このため、はせがわさんのスライドを引き継ぐ形で、GopherによるIMDSv2への攻撃を紹介します。<br />
<br />
まず、Gopher自体の紹介は<a href="https://wiki.archlinux.jp/index.php/Gopher">こちらの記事</a>などを参照していただくとして、ここではcurlとnetcatによりgopherプロトコルを簡単に試してみます。<br />
<br />
まずはcurlコマンドにより以下のURLをアクセスしてみます。<br />
<blockquote class="tr_bq">
<pre>$ curl gopher://localhost:8888/_Hello%0d%0aHiroshi%20Tokumaru%0d%0a</pre>
</blockquote>
curlコマンド実行前にnetcatで8888ポートを待ち受けていると、以下のような表示になります。<br />
<blockquote class="tr_bq">
<pre>$ nc -l 8888
Hello
Hiroshi Tokumaru
<span style="color: red;">Response ← この行はResponse 改行 Ctrl-d を手入力したもの</span>
$</pre>
</blockquote>
この際の呼び出し側は下記となります。<br />
<blockquote class="tr_bq">
<pre>$ curl gopher://localhost:8888/_Hello%0d%0aHiroshi%20Tokumaru%0d%0a
<span style="color: red;">Response</span>
$</pre>
</blockquote>
このように、Gopherプロトコルを使うと、任意リクエストをURLで指定でき、そのレスポンスを受け取れることから、HTTPやSMTPその他のプロトコルをエミュレートできることになります。<br />
<br />
<h4>
Gopherプロトコルを用いたIMDSv2に対する攻撃</h4>
先程のサンプルプログラムをEC2のIMDSv1を無効化した環境に設置した状態で、Gopherプロトコルを用いて攻撃してみましょう。まずはPUTメソッドによるトークン取り出しです。表示が見やすいようにHTMLソースの形で表示しています。アドレスバーには gopher://169.254.169.254:80/_PUT というURLがちらっと見えていますね。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-v0SV03skJsw/Xex7lttQopI/AAAAAAAATME/POdfSK3zdxEAaFk2maJKmnPUOVBn9evBACLcBGAsYHQ/s1600/get-imdsv2-token.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="204" data-original-width="760" src="https://1.bp.blogspot.com/-v0SV03skJsw/Xex7lttQopI/AAAAAAAATME/POdfSK3zdxEAaFk2maJKmnPUOVBn9evBACLcBGAsYHQ/s1600/get-imdsv2-token.png" /></a></div>
<br />
このトークン(有効時間は60秒…もっと長くすることも可能)を用いて、メタデータを表示させた結果が下記です。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-0GtBNT5ie6c/Xex7lokkYfI/AAAAAAAATMA/4gHWh6tVSBsM-BQdswpuytP3ClrnnRpbgCLcBGAsYHQ/s1600/ssrf-thru-imdsv2-with-token.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="328" data-original-width="760" src="https://1.bp.blogspot.com/-0GtBNT5ie6c/Xex7lokkYfI/AAAAAAAATMA/4gHWh6tVSBsM-BQdswpuytP3ClrnnRpbgCLcBGAsYHQ/s1600/ssrf-thru-imdsv2-with-token.png" /></a></div>
<br />
このように、IMDSv1を無効化してIMDSv2のみ有効としても、Gopherプロトコルを用いてSSRF攻撃ができました。<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<h4>
リダイレクトを許可している場合</h4>
今までの「脆弱なスクリプト」は、与えられたURLに対してスキーム(プロトコル)もホストのIPアドレスもチェックしていなかったので、これらのチェックを追加してみましょう。これだけだと防御できて当然なので、cURLのオプションとしてCURLOPT_FOLLOWLOCATIONをtrueにします。これは、リダイレクトをcURL内部で自動的に追跡するという意味です。<br />
<blockquote>
<pre><?php
require_once('./htmlpurifier/library/HTMLPurifier.includes.php');
$purifier = new HTMLPurifier();
$ch = curl_init();
$url = $_GET['url'];
<span style="color: #cc0000;"> $urlinfo = parse_url($url); // URLのパース
$scheme = $urlinfo['scheme'];
$host = $urlinfo['host'];
$ip = gethostbyname($host);
if ($ip === "169.254.169.254") { // IPアドレスのチェック
die("Invalid host");
} elseif ($scheme !== 'http' && $scheme !== 'https') { // スキームのチェック
die("Invalid scheme");
}
</span> curl_setopt($ch, CURLOPT_URL, $url);
<span style="color: #cc0000;"> curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // リダイレクトを自動追跡
</span> curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$html = curl_exec($ch);
echo $purifier->purify($html);</pre>
</blockquote>
このスクリプトに対して、リダイレクトを用いた攻撃をします。具体的には下記のスクリプト(リダイレクタ)のURLをサンプルスクリプトに指定します。<br />
<blockquote>
<pre><?php
header('Location: gopher://169.254.169.254:80/_PUT%20/latest/api/token...以下悪用防止のため略</pre>
</blockquote>
実行結果は以下となり、トークンを取得できていることがわかります。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-xUGBQaJ4Uuk/Xex9iBju1WI/AAAAAAAATMU/QtD5GCI4czACc6O6bbKPx5NfLItv6subgCLcBGAsYHQ/s1600/get-imdsv2-token-with-redirect.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="209" data-original-width="760" src="https://1.bp.blogspot.com/-xUGBQaJ4Uuk/Xex9iBju1WI/AAAAAAAATMU/QtD5GCI4czACc6O6bbKPx5NfLItv6subgCLcBGAsYHQ/s1600/get-imdsv2-token-with-redirect.png" /></a></div>
<br />
同様に、このトークンを使用してEC2インスタンスのメタデータを取得することができます。<br />
<br />
※ はせがわさんのスライドでは、この攻撃にはスクリプト側で明示的に任意プロトコルへのリダイレクトが許可されている必要があるように読めます(P23)が、私が実験により確認した範囲では、任意プロトコルの明示的な許可は必要ないようです。<br />
<br />
<h4>
対策</h4>
上記攻撃には以下の対策候補が考えられます。<br />
<ul>
<li>curlで扱うプロトコルをHTTPおよびHTTPSに限定(常に指定を推奨)</li>
</ul>
<blockquote class="tr_bq">
<pre>curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
</pre>
</blockquote>
<ul>
<li>リダイレクトの追跡を禁止する(curlのデフォルトに戻す、あるいは以下を設定)</li>
</ul>
<blockquote class="tr_bq">
<pre>curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); // リダイレクト追跡しない
</pre>
</blockquote>
<ul>
<li>CURLINFO_PRIMARY_IPにより「実際にアクセスしたIPアドレス」を求め、169.254.169.254(等ブラックリストのIPアドレス)であれば表示をやめる</li>
</ul>
<blockquote class="tr_bq">
<pre>$primary_ip = curl_getinfo($ch, CURLINFO_PRIMARY_IP);</pre>
</blockquote>
<ul>
<li>URLからホスト名に対応するIPアドレスとスキームを確認する(先のスクリプトでは実施済み)</li>
</ul>
<br />
<h4>
結局どうすればよいか</h4>
IMDSv2はそもそもAmazonからもインスタンスメタデータに対する defense in depth (多層防御)と紹介されており、根本的な解決策ではありません。なので、他の根本的な解決策を実施した上で、予防的な対策(緩和策)として用いるべきです。<br />
では、根本的な解決策はなにかというと、インスタンスメタデータの保護という点では、先に紹介したiptables等を用いたネットワーク的な対策が確実です。あるいは、以下により、HTTPによるインスタンスメタデータ参照そのものを禁止することも有効です。<br />
<blockquote class="tr_bq">
<pre>$aws ec2 modify-instance-metadata-options --instance-id i-FFFFFFFFFFFFFFFF <span style="color: red;">--http-endpoint disabled</span>
{
"InstanceId": "i-FFFFFFFFFFFFFFFFF",
"InstanceMetadataOptions": {
"State": "pending",
"HttpTokens": "required",
"HttpPutResponseHopLimit": 1,
"<span style="color: red;">HttpEndpoint": "disabled</span>"
}
}</pre>
</blockquote>
ただし、上記はインスタンスメタデータに対する保護であり、SSRF攻撃全般を防御できるわけではないため、他に守るべきエンドポイントがある場合には他の対策を併用する必要があります。<br />
<br />
<h4>
まとめ</h4>
Instance Metadata Service v2 (IMDSv2) について紹介しました。IMDSv2を用いることにより、SSRF攻撃をかなり緩和されることが期待できるものの、根本的な解決策ではなく緩和策の一つとして用いるべきと考えます。これは、Amazon自体がIMDSv2をdefense in depth(多層防御)とうたっていることからも伺えます。<br />
また、SSRF攻撃の対策は難易度が高いため、可能であればSSRF攻撃の影響を受けない仕様(例えば外部から受け取ったURLにアクセスしない)の検討を推奨します。ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-28260026644285519512019-12-05T17:54:00.000+09:002019-12-05T18:10:38.477+09:00シェルを経由しないOSコマンド呼び出しがPHP7.4で実装されたこの記事は<a href="https://qiita.com/advent-calendar/2019/php">PHP Advent Calendar 2019</a>の5日目の記事です。<br />
<br />
<h4>
はじめに</h4>
私は6年前に、<a href="http://www.adventar.org/calendars/101">PHP Advent Calendar 2013</a>として「<a href="https://blog.tokumaru.org/2013/12/php_21.html">PHPだってシェル経由でないコマンド呼び出し機能が欲しい</a>」という記事を書きました。その中で、OSコマンドインジェクション対策の根本的かつ安全な対策は「シェルを経由しないコマンド呼び出し」であることを指摘した上で、末尾に以下のように書きました。<br />
<blockquote class="tr_bq">
PHPコミッタのみなさま、PHP5.6の新機能として、シェルを経由しないコマンド呼び出しの機能を追加できませんか?</blockquote>
現実には当時から<a href="https://www.php.net/manual/ja/ref.pcntl.php">PCNTL関数</a>にてシェルを経由しないコマンド呼び出しはできたのですが、当関数の使用が難しいことと、CLI版あるいはCGI版(FastCGIは可)のPHPでないとサポートされていないなどの制限があり、popenやproc_openなど使いやすいコマンド呼び出し関数において、シェル呼び出しのないコマンド実行機能が欲しいところでした。<br />
<br />
この「私の願い」はPHP 5.6では実現しませんでしたが、PHP 7.4においてproc_open関数の拡張として実現しました。実に6年越しの実現ということになります。<br />
<br />
<h4>
proc_openの従来の問題点</h4>
proc_openに限りませんが、PHPの従来のコマンド実行機能(PCNTL関数は例外)の問題として、「常にシェル経由でコマンドを呼び出す」ことがあります。これを確認するための簡単なサンプルを示します。以下は、ps -fコマンドをproc_open関数で呼び出しています。<br />
<blockquote>
<pre><?php
$cmd = "ps -f";
$process = proc_open($cmd, [], $pipes);
if (is_resource($process)) {
$return_value = proc_close($process);
echo "command returned $return_value\n";
}
</pre>
</blockquote>
呼び出し例は下記となります。赤字で示しているように、シェル(/bin/sh)経由でpsコマンドが実行されています。<br />
<blockquote>
<pre>UID PID PPID C STIME TTY TIME CMD
ockeghem 16921 16920 0 16:31 pts/0 00:00:00 -bash
ockeghem 18858 16921 0 17:18 pts/0 00:00:00 php-7.4.0 proc_open4.php
ockeghem 18859 18858 0 17:18 pts/0 00:00:00 <span style="color: red;">sh -c ps -f</span>
ockeghem 18860 18859 0 17:18 pts/0 00:00:00 ps -f
command returned 0
</pre>
</blockquote>
このため、コマンドラインにセミコロンなどにより追加のコマンドを実行できる可能性があり、OSコマンドインジェクション脆弱性の原因になっていました。ここで、psのオプションとして、-fの代わりに、「-f; echo Hello」を指定してみましょう。<br />
<blockquote>
<pre><?php
$cmd = "ps -f; echo Hello";
$process = proc_open($cmd, [], $pipes);
// 以下省略
</pre>
</blockquote>
呼び出し例は下記となります。sh -c のパラメータとして; echo Helloが追加されていることと、echoコマンドの実行結果としてHelloが表示されていることがわかります。これがOSコマンドインジェクションの原理です。<br />
<blockquote>
<pre>UID PID PPID C STIME TTY TIME CMD
ockeghem 16921 16920 0 16:31 pts/0 00:00:00 -bash
ockeghem 18932 16921 0 17:30 pts/0 00:00:00 php-7.4.0 proc_open4.php
ockeghem 18933 18932 0 17:30 pts/0 00:00:00 sh -c ps -f<span style="color: red;">; echo Hello</span>
ockeghem 18934 18933 0 17:30 pts/0 00:00:00 ps -f
<span style="color: red;">Hello</span>
command returned 0
</pre>
</blockquote>
この対策として、コマンドラインのパラメータをエスケープ処理する方法もありますが、エスケープ処理自体が複雑になる可能性があり、実際にPHPの<a href="https://www.php.net/manual/ja/function.escapeshellcmd.php">escapeshellcmd関数</a>には脆弱性(<a href="https://blog.tokumaru.org/2011/01/php-escapeshellcmd-is-dangerous.html">こちら</a>を参照)があるため使用を避けるべき状態でした。<br />
<br />
<h4>
proc_openのPHP 7.4での新しい呼び出し方</h4>
この状況に対して、PHP 7.4では、proc_openの第1引数を配列として指定することにより、コマンドとパラメータを明確に分離するとともに、シェルを経由しないコマンド実行ができるようになりました(パチパチパチ)。<br />
先のスクリプトをこの形式で書き換えてみましょう。<br />
<blockquote>
<pre><?php
<span style="color: red;">$cmd = ["ps", "-f"];</span>
$process = proc_open($cmd, [], $pipes);
if (is_resource($process)) {
$return_value = proc_close($process);
echo "command returned $return_value\n";
}
</pre>
</blockquote>
実行例は以下となります。シェルを経由せずに直接コマンドが実行されていることがわかります。<br />
<blockquote>
<pre>UID PID PPID C STIME TTY TIME CMD
ockeghem 16921 16920 0 16:31 pts/0 00:00:00 -bash
ockeghem 18895 16921 0 17:27 pts/0 00:00:00 php-7.4.0 proc_open4.php
ockeghem 18896 18895 0 17:27 pts/0 00:00:00 ps -f
command returned 0
</pre>
</blockquote>
続いて、先程同様に、-f オプションの代わりに -f; echo Hello を指定してみましょう。<br />
<blockquote>
<pre><?php
<span style="color: red;">$cmd = ["ps", "-f; echo Hello"];</span>
$process = proc_open($cmd, [], $pipes);
// 以下略
</pre>
</blockquote>
実行結果は以下のとおりです。psコマンドにオプションとして「-f; echo Hello」が渡されたため、「unsupported SysV option」というエラーになっていますが、OSコマンドインジェクションにはならないことがわかります。<br />
<blockquote>
<pre>error: unsupported SysV option
Usage:
ps [options]
Try 'ps --help <simple all="" list="" misc="" output="" threads="">'
or 'ps --help <s a="" l="" m="" o="" t="">'
for additional help text.
For more details see ps(1).
command returned 1</pre>
</blockquote>
この呼出方法(proc_openの第一引数を配列で指定)の場合、シェルを経由しないでコマンドを呼び出すことから、原理的にOSコマンドインジェクション脆弱性を避けることができます。今後PHP 7.4以降にて外部コマンドを呼び出す場合は常に、proc_open関数にて第1引数を配列で指定し、かつ配列の先頭要素(コマンド名)は固定とすることで、OSコマンドインジェクションを避けつつ簡便かつ安全な実装が可能になります。<br />
<br />
<h4>
まとめ</h4>
PHP 7.4にて新たに追加されたproc_openの新しい呼び出し方を紹介しました。私個人としても、6年越しの要望がかなえられた結果となり、よいクリスマスを迎えられそうです。ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-22630188068456904752019-07-29T12:51:00.000+09:002019-07-29T20:37:06.718+09:00PHPサーバーサイドプログラミングパーフェクトマスターのCSRF対策に脆弱性<h4 class="tr_bq">
サマリ</h4>
<a href="https://www.amazon.co.jp/gp/product/4798044865/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4798044865&linkCode=as2&tag=jisakucompile-22&linkId=5f550903672c34fd6993244b5f473dac" target="_blank">PHPサーバーサイドプログラミングパーフェクトマスター</a><img alt="" border="0" height="1" src="//ir-jp.amazon-adsystem.com/e/ir?t=jisakucompile-22&l=am2&o=9&a=4798044865" style="border: none !important; margin: 0px !important;" width="1" />には、PHP入門書としては珍しくクロスサイト・リクエストフォージェリ(CSRF)対策についての説明があるが、その方法には問題がある。アルゴリズムとして問題があることに加えて、実装上の問題があり、そのままコピペして用いると脆弱性となる。<br />
<br />
<h4>
はじめに</h4>
<a href="https://twitter.com/gallu">古庄親方</a>の以下のツイートを見て驚きました。<br />
<blockquote class="twitter-tweet">
<div dir="ltr" lang="ja">
CSRF用のトークンの作成<br />
$token = password_hash(mt_rand(), PASSWORD_DEFAULT);<br />
ってのを書籍で見た………もンのすンげぇなぁ(苦笑<br />
書籍名でググって調べる……評判が悪いので、まぁ、納得っちゃぁ納得。</div>
— がる (@gallu) <a href="https://twitter.com/gallu/status/1151512955332128768?ref_src=twsrc%5Etfw">July 17, 2019</a></blockquote>
<script async="" charset="utf-8" src="https://platform.twitter.com/widgets.js"></script>
<br />
CSRFトークンの生成に、password_hash関数を使うですと?<br />
親方に書籍名を教えていただき、購入したのが、この記事で紹介する「PHPサーバーサイドプログラミングパーフェクトマスター」です。同書では、CSRF対策にpassword_hashを2種類の方法で使っています(!)が、本稿では、セクション10.3 (P726~) にて説明されている方法を取り上げます。<br />
<br />
<h4>
当該コード</h4>
当該コードを示します。同書では、認証・入力フォーム・登録の3機能が1つのPHPファイルにまとめられていますが、そこから抜き出す形で、以下は入力フォームです。<br />
<blockquote class="tr_bq">
<pre><?php
function getToken($rand='') {
$_SESSION['rand'] = $rand;
$token = password_hash($rand, PASSWORD_DEFAULT);
return $token;
}
// セッションスタート
session_start();
$rand = mt_rand();
$token = getToken($rand);
print <<<EOD
<form action="" method="post">
<input type="hidden" name="request" value="reg" />
<input type="hidden" name="token" value="$token" />
<input type="submit" value="登録">
</form>
EOD;</pre>
</blockquote>
トークンの「素(もと)」としてmt_rand()関数が呼ばれ、その値をセッション変数$_SESSION['rand']に保存しています。その乱数値をpassword_hash関数で処理することで、トークンを生成しています。入力フォームの生成例を以下に示します。<br />
<blockquote class="tr_bq">
<pre><form action="" method="post">
<input type="hidden" name="request" value="reg" />
<input type="hidden" name="token"
value="<span style="color: red;">$2y$10$alqDLwZSizuBYpwmBR6pj.WzxI7UeW9YutuxHjb.r2qw9QQOBqbw6</span>" />
<input type="submit" value="登録">
</form></pre>
</blockquote>
赤字に示した部分($2y$...)がトークンですが、これを見ると、「なんでパスワードのハッシュ値をCSRFトークンにしているわけ?」と勘違いする人が続出しそうですね。<br />
そして、このトークンを受け取り、処理するプログラムが下記の部分です。<br />
<blockquote>
<pre><?php
function check ($token = '') {
return (password_verify($_SESSION['rand'], $token));
}
session_start();
if (isset($_POST['request']) === true && $_POST['request'] === 'reg' && isUser() == true) {
if(isset($_POST['token']) === false || check($_POST['token']) === false) {
print '不正なアクセスが行われました。';
} else {
print '処理が完了しました!';
unset($_SESSION['rand']);
$_SESSION = array();
}</pre>
}</blockquote>
$_POST['token']がNULLでないことを確認した上で、check関数で、セッション変数$_SESSION['rand']をパスワードに見立て、パスワードのハッシュ値に相当する$_POST['token']にてpassword_verify関数で照合する形で、トークンを検証しています。<br />
<br />
<h4>
何が問題か</h4>
このプログラムには以下の問題があります。<br />
<ol>
<li>設計上の問題</li>
<li>実装上の問題</li>
</ol>
以下、順に説明します。<br />
<br />
<h4>
設計上の問題</h4>
そもそもpassword_hash関数は、パスワードを安全に保存するための関数なので、CSRFトークンの処理のことはまったく考慮されておらず、一言で言えば「使う場所を間違えている」ことになります。通常トークンの生成には、暗号論的に安全な疑似乱数生成器(CSPRNG)を用います。PHP 7以降ではrandom_bytes関数があるので(*1)、それを素直に使うだけです。さらにハッシュ関数を通すようなことは必要ありません。余計なことをやればやるほど、バグの原因になり、ひいては脆弱性の原因になります。<br />
<br />
(追記 2019/7/29 20:36)<br />
*1 当書籍はXAMPP上のPHP5.5を前提としているので、random_bytes関数(PHP 7.0以降)は使えませんが、代わりにopenssl_random_pseudo_bytes関数(PHP 5.3以降)が使えます。当関数はopensslの導入が前提ですが、XAMPPはデフォルトでopenssl関数が使用できます。<br />
(追記終わり)<br />
<br />
加えて、mt_rand()の使用も問題です。この関数は、マニュアル(下記引用)にある通り、セキュリティ目的で使うことは適切でありません。<br />
<blockquote class="tr_bq">
<b>警告</b> この関数が生成する値は、暗号学的に安全ではありません。そのため、これを暗号として使ってはいけません。暗号学的に安全な値が必要な場合は、random_int() か random_bytes() あるいは openssl_random_pseudo_bytes() を使いましょう。<br />
<br />
<a href="https://php.net/manual/ja/function.mt-rand.php">https://php.net/manual/ja/function.mt-rand.php</a> より引用</blockquote>
また、mt_rand関数を引きなしで呼び出すと、0からmt_getrandmax()の戻り値までの値を返しますが、mt_getrandmax()は64ビット環境でも2147483647なので、31ビットの範囲になります。これはトークンに用いる乱数のエントロピーとしては不足しています。<br />
さらに、password_hash関数はパスワードの保護を強化するためにストレッチング(ハッシュ計算を繰り返すこと)を施していますが、そのために、password_hashおよびpassword_verify関数は非常に低速です。CSRF対策のために、わざわざ処理を遅くする必要はありません。<br />
<br />
<h4>
実装上の問題</h4>
紹介したプログラムには実装上の問題もあります。使われているトークンはワンタイムのものであり、使用済みになるとunsetされます。すなわち、トークンがNULLとなる時期が存在します。そのタイミングを狙った攻撃ができるのです。<br />
すなわち、以下の関数呼び出しがtrueになるような$tokenを渡せばよいことになりますが…<br />
<blockquote>
<pre>password_verify(NULL, $token)</pre>
</blockquote>
実は、password_verifyの第一引数にNULLを渡すと、空文字列を渡したのと同じ結果になります。なので、空文字列に対するpassword_hash関数の結果を使って、攻撃が可能です。空文字列に対するハッシュ値はソルトにより無数に存在しますが、たとえば以下で攻撃ができます。<br />
<blockquote>
<pre>$2y$10$V3V9iX2iR6ZqaNuFAyUlPeiIvmQrGKDbrJQWdkXWVECGUodZON0Iu</pre>
</blockquote>
検証例を示します。<br />
<blockquote>
<pre><?php
var_dump(password_verify(<span style="color: red;">null</span>, '$2y$10$V3V9iX2iR6ZqaNuFAyUlPeiIvmQrGKDbrJQWdkXWVECGUodZON0Iu'));
// bool(true) が表示される</pre>
</blockquote>
これは、前述した「余計なことをやればやるほど、バグの原因になり、ひいては脆弱性の原因にな」った例といえます。<br />
<br />
<h4>
まとめ</h4>
PHPサーバーサイドプログラミングパーフェクトマスターにおけるCSRF対策の問題を報告しました。実務上でCSRF対策をする場合は、アプリケーションフレームワークの機能を使うか、よく検証されたライブラリを使うべきですが、特別な事情があって独自実装する場合には、定石的な手法を用い、かつできるだけ簡素な、検証のしやすい実装にすべきと考えます。<br />
また、password_hash関数をトークン生成に用いる例が、同書に限らず散見されますが、これは百害あって一利なしですので、トークン生成には単にCSPRNGを使うと覚えておきましょう。<br />
<br />
<br />
<br />
<iframe frameborder="0" marginheight="0" marginwidth="0" scrolling="no" src="https://rcm-fe.amazon-adsystem.com/e/cm?ref=qf_sp_asin_til&t=jisakucompile-22&m=amazon&o=9&p=8&l=as1&IS2=1&detail=1&asins=4798044865&linkId=f0b38898105465fe13e3b4b5d051e4c2&bc1=000000&lt1=_blank&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr" style="height: 240px; width: 120px;">
</iframe><iframe frameborder="0" marginheight="0" marginwidth="0" scrolling="no" src="https://rcm-fe.amazon-adsystem.com/e/cm?ref=qf_sp_asin_til&t=jisakucompile-22&m=amazon&o=9&p=8&l=as1&IS2=1&detail=1&asins=4797393165&linkId=be9e91dadeb36d47b9e23802b91e43a8&bc1=000000&lt1=_blank&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr" style="height: 240px; width: 120px;">
</iframe>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com3tag:blogger.com,1999:blog-3689516124953269065.post-23162980143105497612019-07-01T10:31:00.002+09:002019-07-01T10:31:58.172+09:00PHPカンファレンス福岡2019のSST社ブースにてPHPクイズ出題を担当しました<a href="https://phpcon.fukuoka.jp/2019/">PHPカンファレンス福岡2019</a>のセキュアスカイテクノロジー(SST)社ブースにて、PHPクイズの出題を担当しました。以下は、そのパネルのイメージです。SST社は弊社<a href="https://www.eg-secure.co.jp/">EGセキュアソリューションズ株式会社</a>のパートナー企業で、私はSST社のEラーラニングコンテンツ(PHP編)の<a href="https://www.securesky-tech.com/service/e_learning.html">監修を担当</a>しています。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Nv_rVlwOYOc/XRiryp9fjkI/AAAAAAAAR5U/GryvfZdTvz4Bc0ePmSh6FJ3PZklZKkbNACLcBGAs/s1600/sst-tokumaru-quiz01.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="358" data-original-width="360" src="https://1.bp.blogspot.com/-Nv_rVlwOYOc/XRiryp9fjkI/AAAAAAAAR5U/GryvfZdTvz4Bc0ePmSh6FJ3PZklZKkbNACLcBGAs/s1600/sst-tokumaru-quiz01.jpg" /></a></div>
<br />
問題はすべて2択で、回答は下の写真のように、赤または青のシールをボードに貼り付ける形になっています。回答がわりとバラけていて、出題者的にはいい感じですね。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Bmnb8cwrvuw/XRiry6UEPAI/AAAAAAAAR5Y/yS2uYWjGRAcwS3X3TM17BeKqjjLei9t0ACLcBGAs/s1600/sst-tokumaru-quiz02.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="681" data-original-width="1388" height="314" src="https://1.bp.blogspot.com/-Bmnb8cwrvuw/XRiry6UEPAI/AAAAAAAAR5Y/yS2uYWjGRAcwS3X3TM17BeKqjjLei9t0ACLcBGAs/s640/sst-tokumaru-quiz02.jpg" width="640" /></a></div>
<br />
以下、出題と解答、解説を記載します。<br />
<br />
<h4>
問題1</h4>
以下のPHPスクリプトで、クッキーPHPSESSIDの削除として機能するのはどちら?<br />
PHPのバージョンは7.3.6とする。<br />
<br />
A) (正解)31人が選択=正答率 83.8%<br />
<pre><?php
setcookie('PHPSESSID');
</pre>
<br />
B) 6人が選択<br />
<pre><?php
header('Set-Cookie: PHPSESSID=');
</pre>
<br />
<h4>
解説</h4>
setcookie関数の第2引数の省略時の値は '' (空文字列)ですので、A)は setcookie('PHPSESSID', ''); としたのと同じはずです。そして、第2引数が空文字列の場合は、実際にはクッキーの値としてdeletedが指定され、expires属性が過去日時(PHP-7.3.6の場合は、Thu, 01-Jan-1970 00:00:01 GMT)になります。すなわち、setcookie関数の第2引数を省略すると、クッキーの削除として機能します。<br />
B) header関数の方は、クッキーの値が空文字列になりますが、クッキーそのものは削除されません。
<br />
<br />
参考記事: <a href="https://blog.tokumaru.org/2013/10/phpsetcookie.html">PHPのsetcookie関数で空文字列を設定しようとするとクッキーが削除される</a>
<br />
<br />
ところが、出題の確認時に<a href="https://blog.tokumaru.org/2016/12/modphpall.html">modphpall</a>で確認したところ、古いPHPでは上記の挙動にはならず、以下のレスポンスヘッダが送信されることがわかりました。この動作は、setcookie関数の第2引数を省略した場合のみで、空文字列またはnullを指定した場合はマニュアルどおりの動作となります。<br />
<pre>Set-Cookie: PHPSESSID=
</pre>
正確に言えば、PHP 4.1.0~PHP 5.6.13までが上記挙動になります。すべてのPHP 4.0と PHP 5.6.14以降、PHP 7以降では、マニュアルどおりの動作となります。この変更は、恐らく以下のバグレポートに対応したものと思われます。<br />
<br />
<a href="https://bugs.php.net/bug.php?id=67131">Bug #67131<span style="white-space: pre;"> </span>setcookie() conditional for empty values not met</a>
<br />
<br />
プログラミング言語のエッジケースの出題をする場合は特に、実環境での動作確認と、処理系の想定バージョンの明記が重要だなとあらためて感じました。
<br />
<br />
<h4>
問題2</h4>
以下のPHPスクリプトはどちらが表示される? PHPのバージョンは7.3.6とする。<br />
<pre><?php
$mail = "a@b@example.jp";
var_dump(filter_var($mail, FILTER_SANITIZE_EMAIL));
</pre>
<br />
A) 31人が選択<br />
bool(false)<br />
<br />
B) (正解)6人が選択=正答率 16.2%<br />
string(14) "a@b@example.jp"<br />
<br />
<h4>
解説</h4>
この問題は「ひっかけ」でして、実際に多くの方がひっかかりました。<br />
<a href="https://php.net/manual/ja/filter.filters.sanitize.php">マニュアル</a>にあるように、filter_var関数に FILTER_<span style="color: red;">SANITIZE</span>_EMAIL を指定した場合は、英字、数字および !#$%&'*+-=?^_`{|}~@.[] 以外のすべての文字を取り除きます。出題の場合は、除去対象の文字がないので、入力値がそのまま出力されます。<br />
メールアドレスの形式を検証するためには、FILTER_<span style="color: red;">SANITIZE</span>_EMAIL ではなく、FILTER_<span style="color: red;">VALIDATE</span>_EMAIL を使用します。<br />
<br />
率直に言って、FILTER_SANITIZE_EMAILのユースケースは思いつきません。要らんでしょ、こんなもの。<br />
<br />
<h4>
問題3(PHP考古学)</h4>
PHP-5.2.17にてregister_globals=onの環境で、あらかじめ以下のようにセッション変数が設定されている。<br />
<pre>$_SESSION['user'] = 'yamada';</pre>
クエリ文字列 user=tanaka<br />
を指定して以下のスクリプトを実行した場合の表示はどちら?<br />
<pre><?php
session_start();
echo $user;
</pre>
A)yamada (正解) 23人が選択=正答率 62.2%<br />
B)tanaka 14人が選択<br />
<br />
<h4>
解説</h4>
register_globals=on の環境では、session_start()を実行時にセッション変数の値がグローバル変数として初期設定されます。したがって、$_SESSION['user'] = 'yamada'; が設定されている場合、$user の初期値は 'yamada' になります。<br />
仮に、このセッション変数がセットされていない場合、クエリ文字列 user=tanaka の方が使われ、$user の初期値は 'tanaka' になります。セッション変数とクエリ文字列(やPOST変数など)の両方に同じパラメータ名がある場合、セッション変数の方が優先されます。その理由は、セッション変数のグローバル変数へのセットは、起動時ではなく、session_start()の実行時に行われるからです。すなわち、同名のパラメータがあった場合は、セッション変数が上書きします。<br />
したがって、register_globals=on の使用は脆弱性の原因になりやすいことはよく知られていますが、セッション変数をグローバル変数の初期値として用いると、特に脆弱性が混入しやすくなります。<br />
<br />
例えば、以下のログインチェックのプログラムについて<br />
<pre>session_start();
if (! isset($_SESSION['user']) {
die('ログインしてください');
}
$user = $_SESSION['user'];
</pre>
これを register_globals=on を想定して「直訳」すると以下のようになります。<br />
<pre>session_start();
if (! isset($user)) {
die('ログインしてください');
}
// $user = $_SESSION['user']; // これは不要となる
</pre>
register_globals=on の場合、session_start()を実行した時点で、セッション変数 $_SESSION['user'] の値はグローバル変数 $user にセットされるため、「これは不要となる」とコメントした行は不要となります。便利ですね!<br />
しかし、$_SESSION['user'] がない場合、$user=nullが実行されるわけではないので、クエリ文字列等にuser=tanakaがあった場合、$userには 'tanaka' がセットされます。すなわち、パスワードを知らなくても誰にでもなりすましできます。これは重大な問題ですね。<br />
register_globals=on を用いて、上記の脆弱性を修正するには以下のように書くべきですが…<br />
<pre>$user = null; // register_globals=onによる初期設定を無効化する
session_start();
// $_SESSION['user'] がある場合、$userにその値がセットされる
if (! isset($user)) {
die('ログインしてください');
}
</pre>
上記は著しく直感に反するプログラムですし、それゆえに初期化漏れが盛大に発生しそうです。なので、register_globalsがPHP 5.4で削除されたのは、必然のことと言えましょう。<br />
<br />
<br>
[PR]<br />
徳丸が代表を務める<a href="https://www.eg-secure.co.jp/">EGセキュアソリューションズ株式会社</a>では、<a href="https://www.eg-secure.co.jp/service/">ウェブサイトを堅牢にするための各種セキュリティサービス</a>を提供しています。<br>
<br>
<iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="https://rcm-fe.amazon-adsystem.com/e/cm?ref=tf_til&t=jisakucompile-22&m=amazon&o=9&p=8&l=as1&IS2=1&detail=1&asins=4797393165&linkId=9c2d9c41a5411bc409144bc8d758c109&bc1=000000<1=_blank&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr">
</iframe>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-59229673237688050232019-05-30T13:37:00.000+09:002019-05-30T13:57:09.436+09:002019年1月から5月に公表されたウェブサイトからのクレジットカード情報漏えい事件まとめ株式会社ヤマダ電機の運営するECサイトから、最大37,832件のクレジットカード情報が漏洩したと昨日発表されました。ヤマダ電機のように日本を代表する家電量販店のサイトからクレジットカード情報が漏洩したことに私自身驚きました。<br />
<br />
<a href="https://www.yamada-denki.jp/information/190529/">弊社が運営する「ヤマダウエブコム・ヤマダモール」への不正アクセスによる個人情報流出に関するお詫びとお知らせ</a><br />
<br />
漏洩した情報は以下のように発表されています。<br />
<ul>
<li>クレジットカード番号</li>
<li>有効期限</li>
<li>セキュリティコード</li>
</ul>
はてなブックマークやtwitterのコメントを見ていると、「セキュリティコードを保存していたのか」という意見が見えますが、おそらくセキュリティコードは保存されていなかったと推測します。<br />
本稿では、この件を含め、本年の現時点までのウェブサイトからのクレジットカード情報漏えい事件についてまとめました。<br />
<br />
<h3>
事件の一覧</h3>
下表に本年(2019年)の現時点までに公表されたウェブサイトからのクレジットカード情報漏洩事件をまとめました。サイト名、漏洩期間、漏洩件数(最大)、セキュリティコードの漏洩有無、漏洩の手口(後述)を記載しています。<br /><br>
<style type="text/css">
table.sample{
border-top:1px solid #000066;
border-left:1px solid #000066;
border-collapse:collapse;
border-spacing:0;
background-color:#ffffff;
empty-cells:show;
}
.sample th{
border-right:1px solid #000066;
border-bottom:1px solid #000066;
color:#330000;
background-color:#CCCCFF;
background-position:left top;
padding:0.1em 0.1em;
font-weight: normal;
text-align: center;
}
.sample td{
border-right:1px solid #000066;
border-bottom:1px solid #000066;
padding:0.1em 0.4em;
text-align: left;
}
td.tdr { text-align: right; }
</style>
<table class="sample">
<tbody>
<tr><th>サイト名</th><th width="35%">漏洩期間</th><th width="10%">漏洩件数</th><th width="10%">セキュリティコード</th><th width="10%">漏洩手口</th></tr>
<tr><td><a href="https://www.bunnyfa-yokohama.com/shop/user_data/20190201-0.php">バニーファミリー横浜ネットショップ</a></td><td>2018年6月28日~同年10月25日</td><td>241件</td><td>漏洩</td><td>Type5</td></tr>
<tr><td><a href="http://www.hasepro.com/corp/manager/wp-content/uploads/hasepro_release0226.pdf">オンライン通販サイト(ハセ・プロ)</a></td><td>2018年10月1日~2019年1月24日</td><td>1,311件</td><td>漏洩</td><td>Type5</td></tr>
<tr><td><a href="https://www.quint-j.co.jp/information/">歯学書ドットコム</a></td><td>2012年11月11日~2018年12月28日</td><td>5,689件</td><td>漏洩</td><td>不明</td></tr>
<tr><td><a href="https://honmi.asia/20190401-0eABCAb5pmT34f92.html">本味主義</a></td><td>2017年5月22日~2018年10月14日</td><td>2,926件</td><td>漏洩</td><td>Type4?</td></tr>
<tr><td><a href="https://www.circus-circus.jp/free/info-201903.html">子供服サーカス</a></td><td>2018年10月1日~2019年1月18日</td><td>2,200件</td><td>漏洩</td><td>Type4</td></tr>
<tr><td><a href="https://www.regina-ecore.com/news_caution/">エコレオンラインショップ</a></td><td>2018年5月16日~2018年12月11日</td><td>247件</td><td>漏洩</td><td>Type4</td></tr>
<tr><td><a href="http://www.jrkyushu.co.jp/news/__icsFiles/afieldfile/2019/04/12/nanatsuboshi_gallery_4.pdf">ななつ星 Gallery</a></td><td>2013年10月5日(サイト開設日)~<br />
2019年3月11日(サイト閉鎖日)</td><td>3,086件</td><td>漏洩</td><td>不明</td></tr>
<tr><td><a href="https://www.j-works-net.co.jp/information/2959/">「ショコラ ベルアメール」オンラインショップ</a></td><td>2018年8月6日~2019年1月21日</td><td>1,045件</td><td>漏洩</td><td>Type5</td></tr>
<tr><td><a href="https://edelwein.co.jp/1079">エーデルワイン オンラインショップ</a></td><td>2015年7月8日~2018年8月5日</td><td>1,140件</td><td>漏洩</td><td>不明</td></tr>
<tr><td><a href="https://www.odagaki.co.jp/news/pdf/20190515.pdf">小田垣商店オンラインショップ</a></td><td>2018年4月3日~2018年5月16日及び<br />
2018年9月3日~2019年2月28日</td><td>2,415件</td><td>漏洩</td><td>不明</td></tr>
<tr><td><a href="https://www.fujiiya.co.jp/topics.html">藤い屋オンラインショップ</a></td><td>2018年10月15日~2019年1月28日</td><td>477件</td><td></td><td>Type5</td></tr>
<tr><td><a href="https://jungle-scs.co.jp/?news=%E6%9C%AA%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB/">エンターテインメントホビーショップ ジャングル</a></td><td>2017年5月2日~2018年11月6日</td><td>2,507件</td><td>漏洩</td><td>Type2</td></tr>
<tr><td><a href="https://www.yamada-denki.jp/information/190529/">ヤマダウエブコム・ヤマダモール</a></td><td>2019年3月18日~2019年4月26日</td><td>37,832件</td><td>漏洩</td><td>Type4</td></tr>
</tbody></table>
<br />
<h3>
大半の事件でセキュリティコードが漏洩している</h3>
上表からわかるように、藤い屋オンラインショップを除いたすべての事件でセキュリティコードが漏洩しています。<a href="https://blog.tokumaru.org/2018/12/2018.html">昨年のまとめ</a>も見ていただくとわかりますが、最近のウェブサイトからのクレジットカード情報漏洩では、セキュリティコードが漏洩する方がむしろ普通です。<br />
昨年10月のブログ記事「<a href="https://blog.tokumaru.org/2018/10/methods-of-stealing-credit-card-information.html">クレジットカード情報盗み出しの手口をまとめた</a>」では、クレジットカード情報を窃取する手口をType1~Type5にまとめましたが、この中で、Type4とType5はセキュリティコードを容易に盗むことができます。<br />
<br />
<h4>
Type4の手口とは</h4>
ここでType4の手口について説明します。下図のように、カード情報入力画面に細工を施すことがType4の特徴です。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-6tYGAbtLgY4/W8fRCf5ErlI/AAAAAAAAQGA/dr_qDbYdBkUieKLy4kW2A9UWUndQmxUQQCPcBGAYYCw/s1600/js-1-credit-card.png" imageanchor="1" style="clear: left; float: none; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="313" data-original-width="658" src="https://1.bp.blogspot.com/-6tYGAbtLgY4/W8fRCf5ErlI/AAAAAAAAQGA/dr_qDbYdBkUieKLy4kW2A9UWUndQmxUQQCPcBGAYYCw/s1600/js-1-credit-card.png" /></a></div>
下図はサイト利用者がクレジットカード情報を入力している様子です。入力フォームに仕掛けられたJavaScriptにより、カード情報は攻撃者の管理するサーバーに送信されています。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-g1sgQzhcXqM/W8fRCW6QqxI/AAAAAAAAQGA/r82asZTddwghX_DU9m36nGpdDWpAkwxegCPcBGAYYCw/s1600/js-2-credit-card.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="303" data-original-width="747" src="https://1.bp.blogspot.com/-g1sgQzhcXqM/W8fRCW6QqxI/AAAAAAAAQGA/r82asZTddwghX_DU9m36nGpdDWpAkwxegCPcBGAYYCw/s1600/js-2-credit-card.png" /></a></div>
ヤマダ電機のサイトからの情報漏えいがこのパターンと推測する理由は、リリースの以下の記述からです。<br />
<blockquote class="tr_bq">
(1)原因<br />
第三者によって「ヤマダウエブコム・ヤマダモール」に不正アクセスされ、ペイメントアプリケーションの改ざんが行われたため<br />
<a href="https://www.yamada-denki.jp/information/190529/">弊社が運営する「ヤマダウエブコム・ヤマダモール」への不正アクセスによる個人情報流出に関するお詫びとお知らせ</a>より引用</blockquote>
そして、同じくType4と推測されるサイトとしては下記があります。<br />
<blockquote class="tr_bq">
Q.子供服サーカス、および子供服ミリバールからクレジットカード情報が流出したのですか?<br />
弊社ではカード情報を保有しておりません。今回は、注文情報入力画面 が 不正アクセスにより 改竄され、カード入力画面で入力されたお客様のカード情報が、流出したと思われます。<br />
【<a href="https://www.circus-circus.jp/free/info-201903/faq.html">Q&A】クレジットカード情報流出に関するご質問と回答</a>(株式会社サーカス)より引用</blockquote>
<blockquote class="tr_bq">
2018 年5 月16 日に攻撃者が、データベースへ不正な仕掛けをページ内に埋め込んだとみられます。<br />
その仕掛けは、ページ内の入力フォームに入力されたカード会員情報を正規の処理とは別に外部サイトへ転送する機能を持っていた為、2018年5月16日以降当該サイトのカード利用者のカード会員情報が搾取されていたと考えられる、との事でした。<br />
<a href="https://www.regina-ecore.com/news_caution/">エコレ通販サイトにおける不正アクセスによるお客さま情報の流出懸念に関するお知らせ</a> より引用</blockquote>
<br />
<h4>
Type5の手口も依然活発</h4>
一方、昨年から使われた始めたType5も依然として活発です。下図は、バニーファミリー横浜のFAQからの引用です。<br />
<div class="separator" style="clear: both; float: none; text-align: center;">
<a href="https://1.bp.blogspot.com/-rlMeGl_rzHM/XO87RWAnZ-I/AAAAAAAARxg/mesmVA-V7dEcamzFI7IdFns-VPkz34bxwCLcBGAs/s1600/bunny-family-before.png" imageanchor="1" style="clear: left; float: none; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="313" data-original-width="548" src="https://1.bp.blogspot.com/-rlMeGl_rzHM/XO87RWAnZ-I/AAAAAAAARxg/mesmVA-V7dEcamzFI7IdFns-VPkz34bxwCLcBGAs/s1600/bunny-family-before.png" /></a><a href="https://1.bp.blogspot.com/-rlMeGl_rzHM/XO87RWAnZ-I/AAAAAAAARxg/mesmVA-V7dEcamzFI7IdFns-VPkz34bxwCLcBGAs/s1600/bunny-family-before.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><br /></a><a href="https://1.bp.blogspot.com/-rlMeGl_rzHM/XO87RWAnZ-I/AAAAAAAARxg/mesmVA-V7dEcamzFI7IdFns-VPkz34bxwCLcBGAs/s1600/bunny-family-before.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><br /></a>
</div>
<a href="https://www.bunnyfa-yokohama.com/shop/user_data/20190201-1-qa.php">【重要】カード情報流出についての、ご質問とご回答 バニーファミリー横浜 公式オンラインショップ</a> より引用
<br />
<br />
そして、下図が攻撃後の画面遷移で、「偽のカード入力画面」が画面遷移に挿入されています。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-nzyom1G8Kds/XO87RWglC0I/AAAAAAAARxc/_jxuFi6t11YP-udIWEcrxfMVgeEDINh8QCLcBGAs/s1600/bunny-family-after.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><br /></a><a href="https://1.bp.blogspot.com/-nzyom1G8Kds/XO87RWglC0I/AAAAAAAARxc/_jxuFi6t11YP-udIWEcrxfMVgeEDINh8QCLcBGAs/s1600/bunny-family-after.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><br /></a><a href="https://1.bp.blogspot.com/-nzyom1G8Kds/XO87RWglC0I/AAAAAAAARxc/_jxuFi6t11YP-udIWEcrxfMVgeEDINh8QCLcBGAs/s1600/bunny-family-after.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="332" data-original-width="720" src="https://1.bp.blogspot.com/-nzyom1G8Kds/XO87RWglC0I/AAAAAAAARxc/_jxuFi6t11YP-udIWEcrxfMVgeEDINh8QCLcBGAs/s1600/bunny-family-after.png" /></a></div>
<a href="https://www.bunnyfa-yokohama.com/shop/user_data/20190201-1-qa.php">【重要】カード情報流出についての、ご質問とご回答 バニーファミリー横浜 公式オンラインショップ</a> より引用<br />
<br />
この遷移は、「<a href="https://blog.tokumaru.org/2018/12/2018.html">2018年に公表されたウェブサイトからのクレジットカード情報漏えい事件まとめ</a>」で紹介した伊織の事例と酷似しており、偽画面のドメイン名まで同じです。同一犯人、あるいは同一犯行グループではないでしょうか。<br />
<br />
また、以下のように、ハセ・プロ、ショコラ ベルアメール、藤い屋についてもこの手口であることがリリースからわかります。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-kVkS4RgaROU/XO9NdhIDuFI/AAAAAAAARxw/ryEp-TjLpW0iyig3Z747TbGezMawXxFLQCLcBGAs/s1600/hase-pro.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="239" data-original-width="674" src="https://1.bp.blogspot.com/-kVkS4RgaROU/XO9NdhIDuFI/AAAAAAAARxw/ryEp-TjLpW0iyig3Z747TbGezMawXxFLQCLcBGAs/s1600/hase-pro.png" /></a></div>
<a href="http://www.hasepro.com/corp/manager/wp-content/uploads/hasepro_release0226.pdf">フィッシングサイトによるクレジットカード情報不正取得についてのお詫びと注意喚起のお知らせ</a> (株式会社ハセ・プロ)より引用<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-p97r2ErLYd4/XO9OBP-dq-I/AAAAAAAARx4/jlBkhFDKBFA1ESfjXZ1E5UjEviBTm6oPQCLcBGAs/s1600/j-works.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="414" data-original-width="720" src="https://1.bp.blogspot.com/-p97r2ErLYd4/XO9OBP-dq-I/AAAAAAAARx4/jlBkhFDKBFA1ESfjXZ1E5UjEviBTm6oPQCLcBGAs/s1600/j-works.png" /></a></div>
<a href="https://www.j-works-net.co.jp/information/2966/">「ショコラ ベルアメール」のオンラインショップへの不正アクセスに関するご質問と回答</a> より引用<br />
<blockquote class="tr_bq">
2018年10月15日から2019年1月28日までの期間においてシステムが改ざんされた痕跡があり、 クレジットカード決済を選択されたお客様が偽の決済画面へ誘導され、そこで入力されたクレジットカード情報が流出し、 一部のお客様のクレジットカード情報が不正利用された可能性があることを確認いたしました。<br />
<a href="https://www.fujiiya.co.jp/topics.html">弊社が運営する「藤い屋オンラインショップ」への不正アクセスによる個人情報流出に関するお詫びとお知らせ</a>より引用</blockquote>
<br />
<h3>
なぜ入力フォームからクレジットカード情報を盗むのか</h3>
ブログ記事「<a href="https://blog.tokumaru.org/2018/10/ec.html">ECサイトからクレジットカード情報を盗み出す新たな手口</a>」にて紹介したように、昨年の6月1日に改正割賦販売法が施行され、クレジットカード情報を扱うECサイト事業者にもカード情報保護が求められるようになりました。そして、この保護についてのガイドラインになるのが、『クレジット取引セキュリティ協議会の「実行計画」』であり、中身はクレジットカード情報番号の非保持化が柱になっています。<br />
具体的には、以下に示す「JavaScript(トークン)型」と「リダイレクト(リンク)型」による決済システムが推進されています。<br />
以下は、JavaScript型の決済の様子です。入力フォームは加盟店(ECサイト)側にありますが、カード情報はECサイトのサーバーを通過せず、JavaScriptにより直接決済代行事業者にカード情報を送信する方式です。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-K1zVfru_Wf0/XO9T3FNs9pI/AAAAAAAARyI/QpK_LmEHZPw_hPeRlG47SPjAmqRYA9b1wCLcBGAs/s1600/javascript-type.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="355" data-original-width="730" src="https://1.bp.blogspot.com/-K1zVfru_Wf0/XO9T3FNs9pI/AAAAAAAARyI/QpK_LmEHZPw_hPeRlG47SPjAmqRYA9b1wCLcBGAs/s1600/javascript-type.png" /></a></div>
<a href="https://www.j-credit.or.jp/security/pdf/plan_2019.pdf">実行計画2019</a> より引用<br />
<br />
JavaScript型の場合、先に説明したType4およびType5でカード情報を盗むことができます。<br />
一方下図はリダイレクト型決済の概念図です。カード情報を入力する際には、決済代行事業者の画面に遷移するため、ECサイト側では一切生のカード情報を扱わず、安全性が高い方法と考えられていました。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Wbu4CL3KEwc/XO9T3IWtf2I/AAAAAAAARyE/s4NsRAwiqBYxBD7ZEZvApECDJRlpgNInQCLcBGAs/s1600/link-type.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="351" data-original-width="727" src="https://1.bp.blogspot.com/-Wbu4CL3KEwc/XO9T3IWtf2I/AAAAAAAARyE/s4NsRAwiqBYxBD7ZEZvApECDJRlpgNInQCLcBGAs/s1600/link-type.png" /></a></div>
<a href="https://www.j-credit.or.jp/security/pdf/plan_2019.pdf">実行計画2019</a> より引用<br />
<br />
しかし、前記Type5の攻撃ではリダイレクト型決済でもカード情報を盗むことができます。今年のカード情報窃取事件ではType4とType5が使い分けられていますが、これは決済方式に合わせて適した方法が採用されたものと考えられます。
<br />
<br />
<h3>
カード情報非保持化で満足せず基本的な対策を</h3>
以上で説明してきたように、経産省およびクレジット取引セキュリティ協議会の進める「カード情報非保持化」では十分にカード情報を保護できる状況ではありません。基本に立ち返って、以下の対策を推奨します(2018年末の記事の再掲)。<br />
<ul>
<li>ウェブアプリケーションやソフトウェアライブラリ、プラットフォームの脆弱性対策(パッチ適用など)</li>
<li>管理者等のパスワードを強固にする(可能ならばインターネットからは管理者ログインできないよう設定する)</li>
</ul>
加えて、以下の対策を推奨します。<br />
<ul>
<li>Web Application Firewall(WAF)の導入</li>
<li>ファイルパーミッションとファイルオーナーの適切な設定</li>
<li>改ざん検知システムの導入</li>
</ul>
<br>
[PR]<br />
徳丸が代表を務める<a href="https://www.eg-secure.co.jp/">EGセキュアソリューションズ株式会社</a>では、<a href="https://www.eg-secure.co.jp/service/">ECサイトを堅牢にするための各種セキュリティサービス</a>を提供しています。<br />
<br />
<br />
<iframe frameborder="0" marginheight="0" marginwidth="0" scrolling="no" src="https://rcm-fe.amazon-adsystem.com/e/cm?ref=tf_til&t=jisakucompile-22&m=amazon&o=9&p=8&l=as1&IS2=1&detail=1&asins=4797393165&linkId=ea98a5af29162c3f6f66b51bf75e9bea&bc1=000000&lt1=_blank&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr" style="height: 240px; width: 120px;"></iframe>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-55763868579614732692019-05-07T12:15:00.000+09:002019-05-07T12:17:36.892+09:00[書評]噂の学園一美少女な先輩がモブの俺に惚れてるって、これなんのバグですか?瓜生聖(うりゅうせい)の近著「<a href="https://www.amazon.co.jp/gp/product/4041082536/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4041082536&linkCode=as2&tag=jisakucompile-22&linkId=6c89db52fa7109c76ef02fd33a8a4d9d">噂の学園一美少女な先輩がモブの俺に惚れてるって、これなんのバグですか?</a> 」を読んだので紹介したい。本書は、JNSA(特定非営利活動法人 日本ネットワークセキュリティ協会)が主催した<a href="https://www.jnsa.org/novel_contest/">サイバーセキュリティ小説コンテスト</a>にて大賞を受賞した作品「目つきの悪い女が眼鏡をかけたら美少女だった件」を大幅に加筆したのち、角川スニーカー文庫から出版された。<br />
<br />
<h3>
重要事項説明</h3>
<ul>
<li>著者と評者は知人関係にあり公私共に交流がある</li>
<li>評者が読んだ書籍はご恵贈いただいたものである</li>
<li>この記事のリンクにはアフィリエイトが含まれる</li>
</ul>
<br />
<h3>
はじめに</h3>
著者の<a href="https://twitter.com/uryusei">瓜生聖</a>は、twitterのプロフィールには「ITmediaで記事を書いてる兼業ライター」とあるが、本業はITエンジニアである。つまり、現役のITエンジニア兼テクニカルライターである人物が、サイバーセキュリティ小説を書いたのが本書ということになる。瓜生聖はライターとしても一流なので技術面と文章力は申し分ないはずだが、小説となるとどうだろうか。評者の興味は、まずそこにあった。<br />
<br />
<h3>
あらすじ</h3>
本書は以下の三部構成となっている。<br />
<br />
<h4>
第一話 試験問題漏洩事件</h4>
本書の準ヒロイン格で、主人公鷹野祐(たかのたすく)の幼なじみの西村理乃(にしむらりの)が、数学の試験問題漏洩事件の犯人だという噂が流れ、事件が提示される。祐は、美少女衣川マト(ころもがわまと)の力を借りて事件の真相を解明し、理乃を救う。祐は小学生時代に探偵を志していたが、とある事件がトラウマになり、探偵の夢をあきらめていたが、この事件をきっかけに、自分が進むべき道がおぼろげながら見えてくる。<br />
<br />
<h4>
第二話 夏の嵐</h4>
祐は区民プールで子供たちと遊ぶマトを見かけ、そこからの流れでマトが幼少期を過ごした施設を訪問、マトの過去を知ることになる。その後マトの自宅を訪問しドキドキの一時を過ごす。<br />
台風の日、買い物を言いつけられた祐は自宅に帰ることができなくなり、愛帝学園を訪れる。そこで第二の事件を解決する。過程で、サイバーセキュリティに詳しい青年鴻上(こうがみ)が登場する。<br />
<br />
<h4>
第三話 最強の武器</h4>
マトが高校に来なくなってしまった。鴻上がマトをスカウトして米国に呼んだらしい。マトが遠くに行ってしまう。しかも、米国でのマトの仕事は…<br />
マトを救うべく、祐は第三の事件を三人の少女の力を借りて解決する。そして、はたして祐とマトは結ばれるのか。<br />
<br />
<h3>
重層的な構造</h3>
本書はラブコメラノベという分類になっているが、その実複雑な構成になっている。評者の見るところ、以下の要素を併せ持っている。<br />
<ul>
<li>ラブコメ</li>
<li>サイバーセキュリティ小説</li>
<li>成長譚(さまざまな事件を少女たちの力を借りて解決する過程で主人公は成長する)</li>
<li>自分探し(無目的に生きてきた主人公が自分のやりたいことを見出す)</li>
<li>探偵小説(第一話は探偵小説でもある)</li>
<li>家庭内暴力や児童ポルノ問題</li>
</ul>
まだ他にもあるかもしれない。<br />
これだけの要素を文庫本300ページに突っ込むとごちゃごちゃしてしまいそうだが、どうか。率直なところ、児童ポルノ問題を扱い探偵小説仕立てにもなっている第一話は、話が少し重くなり、また、筋書きを追うのに苦労する箇所がある。著者の初めての小説ということで張り切ってネタをぶち込みすぎたのかもしれない。その反省からか、第二話と第三話では、材料を少し減らして、著者はすっきりとまとめてみせる。全体としては、重層的な構造にも関わらずラノベらしくすらすら楽しく読むことができる。<br />
<br />
<h3>
エピソード紹介</h3>
次に、本書の冒頭からいくつかのエピソードを引用で紹介しよう。<br />
<br />
物語は、本書の主人公である愛帝学園の1年生の鷹野祐(たかのたすく)が、ヒロイン衣川マト(ころまがわまと)と邂逅するところから始まる。<br />
<blockquote class="tr_bq">
教室に入ろうとした瞬間、突然引き戸が勢いよく開いた。<br />
突然のことに足が絡まり、思わず尻餅をつく。<br />
「いてて……す、すいません」<br />
見上げた先にいたのは妖精だつた。<br />
陽の光をまとい、細く艷やかな銀髪がふわりと舞う。シルバーアッシュの柳眉の下はぱっちりとした大きな翠眼(すいがん)で、真っ白な肌は白磁のようにつるつるだ。<br />
学年の違う俺でも知っている銀髪の美少女ーー衣川先輩だった。<br />
遠くから見るのとは全然違う美の暴力をまともに受け、俺はただただ、呆然と見つめるだけだった。目を離すこともできない。もしかしたら呼吸も忘れていたかもしれない。</blockquote>
この後、いったんは「氷のように冷たい目で俺を見下ろしていた」マトは、あるきっかけの後主人公を抱きしめるに至る。その後マトは顔を赤らめて走り去るが、主人公は小さなUSBメモリが落ちていることに気づく。<br />
<br />
その後、準ヒロイン格でアイドルを目指している西村理乃(にしむらりの)や、主人公が入り浸っているコンピュータ部(主人公は部員ではない)の部員である嵯峨野亜弥(さがのあや)が次々に登場し、わずか10ページほどの間で主要人物が出揃うことになる。スピーディな展開で手際が良い。<br />
<br />
主人公は亜弥にUSBメモリの調査を依頼する。亜弥はアーミーナイフを取り出し、慣れた手つきでガワを開くと、USBメモリのチップがPhison2307であり、ファームウェア改造ツールPsychsonにより改造が可能であると指摘する。<br />
<blockquote class="tr_bq">
「ちょ、ちょっと待ってくれ。つまりその、ファームウェア? ってのを書き換えるとどうなるんだ?」<br />
「デバイスクラスを偽装できるー」<br />
「俺にもわかるように言ってくれ」<br />
「USBデバイスの挙動をエミュレートできるー」<br />
「俺にもわかるように言ってくれ」<br />
亜弥は話し足りなそうにしながらも、言葉を選ぶように言った。<br />
「PCを乗っ取れるー」<br />
「まじか」</blockquote>
BadUSBが早くも登場である。このやり取りでもわかるように、主人公はコンピュータには詳しくない。本書は主人公の一人称視点で書かれているので、難しい用語が交わる技術的な説明について、主人公に「わからない、もっと易しく説明してくれ」と言わせることによって、わかりやすく解説したり、詳細を省略して要点のみを説明できることになる。これは、著者の工夫だろう。<br />
<br />
この後、なぜか主人公と連れ歩いているマトと、アイドルを目指す理乃が、主人公を巡って張り合う場面がある。そう、なんせラブコメなので主人公はモテモテなのだ。理乃は言う。<br />
<blockquote class="tr_bq">
「好みは人それぞれだと思いますけどね、衣川先輩。幼なじみでずっと昔から知っていて、お互いに祐、理乃と呼び合う関係のあたしから言わせてもらえれば、今の祐はつまんないやつですよ。普通の人に分かる良さなんて、皆無ですからね」</blockquote>
幼なじみの理乃は、祐は昔と違ってつまらないやつになってしまったと言う。祐は、小学生の頃まではシャーロック・ホームズにあこがれ、将来を探偵になることを目指していたが、ある事件に関わったことがきっかけとなり、探偵を志すことをやめ、できるだけ目立たないように過ごしている。<br />
<br />
あらすじで紹介したように、この物語は、愛帝学園に起こるさまざまな事件を主人公が周囲の力を解決することにより成長する、いわばロールプレイングゲーム(成長譚)の形態をとっている。主人公は、過去のトラウマから探偵になる夢を放棄してしまったが、友人のトラブルを解決すべく再び探偵として行動する。その過程で、マトからOSINT(オシント)という方法論を教えてもらい、これが自分の進むべき道だと気づく。<br />
<br />
問題を抱えているのは主人公だけではない。マトと理乃は、家族に関するトラブルを抱えていたが、それぞれの方法で過去のトラブルを乗り越えようとしている。それが物語に厚みを加えている。<br />
<br />
次に、本書の二大要素であるサイバーセキュリティ小説として、およびラブコメラノベとしての側面を紹介する。<br />
<br />
<h3>
サイバーセキュリティ小説として</h3>
本書の「事件」に登場するセキリティ要素を紹介しよう。<br />
<br />
第一話: BadUSB、MaaS(Malware-as-a-Service)<br />
第二話: ウェブサイト侵入(クレジットカード情報非保持サイト?)<br />
<div>
第三話: 暗号通貨</div>
<div>
<br /></div>
上記のように、イマドキのセキュリティ要素がふんだんに盛り込まれている。しかも、現役のITエンジニアが書いただけあって、セキュリティ部分のクォリティは非常に高い。先に引用したBadUSBもそうだが、ここでは、評者が思わずニヤリとした箇所を引用しよう。以下は、第二話中に出てくる、侵入されたウェブサイトを巡る会話である。<br />
<blockquote class="tr_bq">
「なるほど…じゃあコレはニセモノってことなんですか?」<br />
僕は入力途中になっていた通販サイトのページを示す。アドレスバーには緑色で企業名まで表示されている。これがニセモノならどうやって見分ければいいんだろうか。<br />
「ああ、フィッシングという言い方は正確じゃないか。改ざん、と言った方が正しいのかな。通常だと通信が保護されていなかったり、URLが微妙に違ったりすることが多いけど、これは本物のサーバーに何かあったときの予備サーバだから、それ自体は本物よ。ただ、ファイルが書き換えられているだけ」</blockquote>
これはクレジットカード情報窃取の最新の手口である。評者が以前ブログ記事「<a href="https://blog.tokumaru.org/2018/10/methods-of-stealing-credit-card-information.html">クレジットカード情報盗み出しの手口をまとめた</a>」に書いたタイプ4かタイプ5に相当する。おそらく「フィッシング」という言葉があることから、タイプ5、すなわち最新の手口であろう。<br />
このように、著者は細かいクラッキングの手口にまで目配りして、最新の手法が紹介されている。まるで、評者がふだん「これをフィッシングと言うな」と言っているのを意識しているかのような会話だw<br />
細かいようだが、セキュリティはディテールが重要であるし、あまりに荒唐無稽な手口だと(荒唐無稽さを突っ込んで楽しむなら別だが)読者は白けてしまう。その点、本書は、セキュリティに精通した読者でも、存分に楽しむことができる。<br />
また、本書のヒロイン衣川マトは、実在の高名なバグハンターがモデルになっている。評者は予めそれを知っていたが、それを知らない読者でも、途中で実在のモデルを暗示する重要なエピソードが出てくるので、セキュリティに関心のある読者なら「ははーん」と分かるだろう。ここは引用したいところではあるが、ネタバレになるので控えておく。<br />
<br />
<h3>
ラブコメとして</h3>
ラブコメラノベなのだから、主人公が「冴えないやつ」であるにも関わらずがモテモテなのは、いくら非現実的でも大目に見てあげるのが大人のたしなみというものだ…そんなふうに考えていた時期が私にもありました。<br />
実際冒頭のくだりでは、「おいおい、それはないだろう」と思いながら読んでいたのであるが、読み進めていくうちに、主人公のモテモテぶりが不自然ではなくなってくる。当初は、「主人公がモテモテなのは在原業平や光源氏以来の我が国の伝統だからな」と思っていたが、そうではなかった。鷹野祐は冴えないように見えて実は魅力的な少年なのだ。そのことを、周囲も、当人も、そして読者も気づいていないが、三人の少女は気づいていた。それは、先に引用した「普通の人に分かる良さなんて、皆無ですからね」という理乃の言葉からも伺える。読者は、物語の進展とともに主人公の魅力がわかってくるわけで、それで「モテモテぶりが自然に思えてくる」のだろう。<br />
評者は、主人公の艶福ぶりをうらやんだり、時には「おいおい、昔から『据え膳食わぬは男の恥』と言ってな…なぜ据え膳を食わぬ」と主人公をけしかたり、さらには、「うーん、この調子でいくと祐はマトとくっつくのではなく、理乃とくっついてしまう、なんてどんでん返しもなくないか?」などと要らぬ心配をしたりした。つまり、評者はラブコメとしての本書も存分に堪能した。<br />
<br />
<h3>
おわりに</h3>
瓜生聖のサイバーセキュリティ小説「噂の学園一美少女な先輩がモブの俺に惚れてるって、これなんのバグですか? 」を紹介した。本書は、ラブコメ仕立てのサイバーセキュリティ小説であるのみならず、重層的な構造を持つ意欲的な内容であり、それでいて、ラノベとしてすらすらと読める一流のエンタテインメントである。<br />
著者は「あとがき」の末尾で「本書を読んだラノベ好きの方が『サイバーセキュリティって面白そう』と思っていだたければ、サイバーセキュリティ関係者が『ラノベもなかなか面白いな』と思っていただければ著者冥利につきます」と記している。本書は評者が初めて読んだラノベだが、本当に面白かった。可能ならば、本書の続編で、祐とマトが恋人らしくいちゃついているシーンも読んでみたい。<br />
<br />
<br />
<iframe frameborder="0" marginheight="0" marginwidth="0" scrolling="no" src="https://rcm-fe.amazon-adsystem.com/e/cm?ref=qf_sp_asin_til&t=jisakucompile-22&m=amazon&o=9&p=8&l=as1&IS2=1&detail=1&asins=4041082536&linkId=6c44065c94dda17c26aa3fdfc79613d6&bc1=000000&lt1=_blank&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr" style="height: 240px; width: 120px;">
</iframe>ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-10235590696950713772019-04-29T18:10:00.000+09:002019-04-29T19:16:47.250+09:00鈴木常彦先生の「共用レンタルサーバにおけるメールの窃盗」の話を聴講した4月23日(火)に開催された 「<a href="https://ssm.pkan.org/ssmjp%E9%96%8B%E5%82%AC/2019%E5%B9%B404%E6%9C%88%E3%81%AEssmjp%EF%BD%9Edns%E3%81%AE%E8%A9%B1%E3%82%92%E8%81%9E%E3%81%8F%E4%BC%9A%EF%BD%9E%E3%81%AE%E3%81%8A%E7%9F%A5%E3%82%89%E3%81%9B/">#ssmjp 2019/04 ~DNSの話を聞く会~</a>」に「Outputなら任せてください枠」で参加しましたので、講演内容からとくにやばい(?)内容と思われる@tss_ontap(鈴木常彦=浸透言うな先生)の「黒塗りの DNS (萎縮編)」から、「共用レンタルサーバにおけるメールの窃盗」について紹介します。<a href="http://www.e-ontap.com/dns/ssmjp/#(1)">スライドは公開されています</a>。<br />
<br />
<h4>サマリ</h4>
レンタルサーバーからメールを送信する場合、悪意の第三者に、特定のドメインに対するメールを横取りされるリスクがある<br />
<br />
<h4>攻撃手法</h4>
<br />
<ul>
<li>攻撃者は、レンタルサーバーを契約(お試しなどでも可能)して、攻撃対象のドメイン名(ここではchukyo-u.ac.jp…中京大学のドメイン名を用いる)を登録する</li>
<li>その際に、当該ドメイン名の権利を有している必要はない(権利があれば正当にメールを受信できるので攻撃の必要がない)</li>
<li>これだけ</li>
</ul>
<br />
<h4>なぜメールが横取りされるか?</h4>
一般にメール送信する場合、サーバー内に送信先ドメインがある場合、そちらを優先して配送する。レンタルサーバー内に攻撃対象のドメイン名(ここではchukyo-u.ac.jp)がメール設定されていると、メール配送の際にDNSを参照することなく、無条件に(ローカル内で)当該のメールボックスにメールを配送してしまう。このchukyo-u.ac.jpのメールボックスは、実際には正規のドメイン名登録者ではなく、悪意の第三者が登録したものなので、メールは悪意の第三者が横取りできることになる。<br />
<br />
<h4>FAQ</h4>
Q1: レンタルサーバー契約時にドメイン名の権利は確認されないのか?<br />
A1: 多くの場合権利確認はないそうですし、まぁ難しそうですね<br />
<br />
Q2: レンサバ側でDNS権威サーバーやwhoisとか参照しないのか?<br />
A2: 前述の通り、ローカル配送の際にはDNSやwhoisは参照しません<br />
<br />
Q3: ドメイン名の権利者ができる対策はあるか?<br />
A3: すべてのレンタルサーバーに契約すれば防げますが現実的ではありせん<br />
<br />
Q4: メール送信者(レンタルサーバー利用者)ができる対策はあるか?<br />
A4: メール以外の方法で「メール送ったよ、届いていないときは連絡して!」とメッセージするとか…というのは冗談で、メール送信者側にとれる対策はなさそうです<br />
<br />
Q5: DNS関係ないじゃん<br />
A5: はい。DNSとは無関係の問題ですが、近隣のテーマだとは思います<br />
ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-6208417537283413592019-04-17T08:52:00.001+09:002019-04-17T08:52:32.271+09:00WordPressのプラグインVisual CSS Style Editorに権限昇格の脆弱性最近WordPressのプラグインのアップデート状況を監視しているのですが、<a href="https://wordpress.org/plugins/yellow-pencil-visual-theme-customizer/">Visual CSS Style Editor(別名Yellow pencil visual theme customizer)</a>が公開停止になり、しばらくたって公開が再開されていました。これは、掲題のように権限昇格の脆弱性があったので公開停止になり、修正版が出たことにより公開再開したものです。WordPressのプラグインではよく見る光景です。<br />
<br />
注: 公開停止になったまま再開しないプラグインも珍しくありません。最近では、<a href="https://wordpress.org/plugins/types/">types</a>、<a href="https://wordpress.org/plugins/google-maps-builder/">google-maps-builder</a>、<a href="https://wordpress.org/plugins/simple-share-buttons-adder/">simple-share-buttons-adder</a>、<a href="https://wordpress.org/plugins/yuzo-related-post/">yuzo-related-post</a>等の人気プラグインが公開停止になりましたが、本稿執筆時点で再開していません。<br />
<br />
<h4>
脆弱性の概要</h4>
脆弱性のあるバージョン: 7.1.9以前<br />
脆弱性の種類: 権限昇格<br />
脆弱性による直接の影響: 外部からWordPressの設定をログインなしに変更できる<br />
典型的な攻撃手法: 外部から勝手に管理者ユーザーを登録することで管理者になれる<br />
対策: 最新版(本稿執筆時点では7.2.1)にアップデートする<br />
<br />
上記は、以前紹介した<a href="https://blog.tokumaru.org/2018/12/wp-gdpr-compliance-cve-2018-19207.html">WP GDPR Complianceの脆弱性CVE-2018-19207</a>と攻撃手法や影響は似ています。PoC(概念実証コード)は公開されていますが、影響が大きいので紹介はしません。私が逮捕されても困りますしね…<br />
<br />
<h4>
原因</h4>
複数の問題が組み合わさって攻撃が可能になっていますが、とくに、yp_remote_get_first()関数内で、特定のGETパラメータを指定すると一時的に管理者になる(厳密にはid=1のユーザになる)機能が実装されていることが原因のようです。この機能の意図はよくわかりませんが、表面上は開発者が仕込んだバックドアのように見えます。以下は<a href="https://plugins.trac.wordpress.org/changeset/2067583/yellow-pencil-visual-theme-customizer/trunk/yellow-pencil.php?old=2059309&old_path=yellow-pencil-visual-theme-customizer%2Ftrunk%2Fyellow-pencil.php">7.1.9から7.2.0への差分</a>です。当該箇所は、修正版では単に削除されています。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-kCRwjjnysG4/XLZpHx714FI/AAAAAAAARn0/exyJXnnvbnwk-_oSz-4udf7JARobKCfXACLcBGAs/s1600/yellow-css.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="546" data-original-width="568" src="https://3.bp.blogspot.com/-kCRwjjnysG4/XLZpHx714FI/AAAAAAAARn0/exyJXnnvbnwk-_oSz-4udf7JARobKCfXACLcBGAs/s1600/yellow-css.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
<h4>
対策</h4>
Visual CSS Style Editorプラグインの7.2.0で改修されています。本稿執筆時点の最新版は7.2.1です。最新版の導入を推奨します。<br />
当脆弱性の攻撃のうち、管理者ユーザーを勝手に作成される経路については、WordPressのログイン機能 wp-login.php のURLを変更し、隠すことで緩和策になります。<br />
<br />
<div>
<h4>
宣伝</h4>
EGセキュアソリューションズ株式会社では、WordPress利用サイトのセキュリティ強化施策を支援しています。サービス案内は<a href="https://www.eg-secure.co.jp/service/wordpress_security/">こちら</a>。詳細は<a href="https://www.eg-secure.co.jp/contact/">お問い合わせください</a>。</div>
<br />ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-8060153309019260952019-03-04T10:48:00.000+09:002019-03-04T10:49:41.207+09:00EC2上でDNS RebindingによるSSRF攻撃可能性を検証したAWS EC2環境でのDNS Rebindingについて検証したので紹介します。<br />
まずは、「前回までのおさらい」です。先日以下の記事でSSRF攻撃およびSSRF脆弱性について紹介しました。<br />
<ul>
<li><a href="https://blog.tokumaru.org/2018/12/introduction-to-ssrf-server-side-request-forgery.html">SSRF(Server Side Request Forgery)徹底入門</a></li>
</ul>
この記事の中で、以下のように紹介しました。<br />
<blockquote class="tr_bq">
ホスト名からIPアドレスを求める際にも以下の問題が発生します。<br />
<ul>
<li>DNSサーバーが複数のIPアドレスを返す場合の処理の漏れ</li>
<li>IPアドレスの表記の多様性(参考記事)</li>
<li><span style="color: red;">IPアドレスチェックとHTTPリクエストのタイミングの差を悪用した攻撃(TOCTOU脆弱性)</span></li>
<li>リクエスト先のWebサーバーが、攻撃対象サーバーにリダイレクトする</li>
</ul>
上記のTOCTOU(Time of check to time of use)問題は、DNSの名前解決の文脈では<span style="color: red;">DNS Rebinding</span>とも呼ばれます。</blockquote>
DNS Rebinding攻撃についてはインフラ側で対策が取られている場合もあります。そこで、EC2上のウェブサイトにて、DNS RebindingによるSSRF攻撃が可能か検証してみました。<br />
攻撃対象のスクリプトを以下に示します。これは先日の記事のスクリプトを改修したもので、クエリ文字列urlで指定したURLのコンテンツを読み出し、表示するものです。IPアドレスのチェックを追加し、表示系を簡略化しています。<br />
<blockquote>
<pre class="src"><?php
header('Content-Type: text/plain');
$url = $_GET['url'];
$urlinfo = parse_url($url);
$host = $urlinfo['host']; // URLからホスト名を取り出し
$ip = gethostbyname($host); // 接続先IPアドレスを取得
echo "Target IP : $ip\n"; // 接続先のIPアドレスを表示
if ($ip == "169.254.169.254") {
die("Invalid host $host"); // IPアドレスが169.254.169.254ならエラー
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$body = curl_exec($ch);
echo $body;
$prime_ip = curl_getinfo($ch, CURLINFO_PRIMARY_IP);
echo "\nActual IP : $prime_ip\n"; // 実際のIPアドレスを表示 </pre>
</blockquote>
このスクリプトは、DNS Rebinding以外にもいろいろ駄目ですが、とりあえず接続先のIPアドレスが169.254.169.254でないことは確認しています。これを以下のIPアドレスで実行すると<br />
<br />
http://ホスト名/ssrf.php?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/test-role<br />
<br />
以下のようにエラーになります。<br />
<blockquote>
<pre class="src">Target IP : 169.254.169.254
Invalid host 169.254.169.254</pre>
</blockquote>
<h4>
DNS Rebinding</h4>
ようやくDNS Rebindingの説明です。攻撃対象が下図のWeb Applicationだとします。図の右側のDNS Contents Server(DNS権威サーバー)は、接続先のドメイン名を管理しているDNSサーバーで、実は攻撃者が管理している想定です(example.jpは例示用のドメイン名です)。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-uSFcA472rTM/XHteTIL7txI/AAAAAAAARhk/y01KAXTg50kJsVKd7RYRei3RObKOF2bhACLcBGAs/s1600/aws-dns-rebinding.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="253" data-original-width="652" src="https://3.bp.blogspot.com/-uSFcA472rTM/XHteTIL7txI/AAAAAAAARhk/y01KAXTg50kJsVKd7RYRei3RObKOF2bhACLcBGAs/s1600/aws-dns-rebinding.png" /></a></div>
<br />
この状態で、先のサンプルスクリプトでurl=http://evil.example.jp/... を指定した場合、通常ケースでは以下のように名前解決されます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-JU8RvQsiylE/XHteTBvFyaI/AAAAAAAARhc/dZQW1tFXAc0rJI7uKym35KOdAV9-nKOOQCEwYBhgL/s1600/aws-dns-rebinding-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="337" data-original-width="765" src="https://4.bp.blogspot.com/-JU8RvQsiylE/XHteTBvFyaI/AAAAAAAARhc/dZQW1tFXAc0rJI7uKym35KOdAV9-nKOOQCEwYBhgL/s1600/aws-dns-rebinding-2.png" /></a></div>
上図のように、gethostbynameとcurl_execの双方で名前解決が発生しますが、DNSキャッシュサーバーのキャッシュが残存していれば、2回目の名前解決の際には、DNSコンテンツサーバーには問い合わせずにDNSキャッシュサーバーがキャッシュの値を返します。<br />
そこで、DNSコンテンツサーバー(権威サーバー)側がTTL=0としてAレコードを返し、Aレコードの要求毎に異なるIPアドレスを返す攻撃がDNS Rebinding攻撃です。その様子を下図に示します。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-LBIuiRHGRNU/XHteTKC2LFI/AAAAAAAARhg/TLKUQA-0FkQowhjubqxhXIasn7E7TonqACEwYBhgL/s1600/aws-dns-rebinding-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="350" data-original-width="771" src="https://2.bp.blogspot.com/-LBIuiRHGRNU/XHteTKC2LFI/AAAAAAAARhg/TLKUQA-0FkQowhjubqxhXIasn7E7TonqACEwYBhgL/s1600/aws-dns-rebinding-3.png" /></a></div>
DNSコンテンツサーバーがTTL=0でAレコードを返すので、DNSキャッシュサーバーは問い合わせがある度にDNSコンテンツサーバーにAレコードを問い合わせます。そして、最初の問い合わせでは無害なIPアドレス(203.0.113.5)を返し、二度目の問い合わせでは169.254.169.254(EC2インスタンスの設定を返すIPアドレス)を返しています。このようにして、IPアドレスのチェックをすり抜ける攻撃手法がDNS Rebinding攻撃です。<br />
上図を見る限りいかにも成功しそうですが、実際にはキャッシュサーバーの仕様などに依存します。DNS Rebinding対策その他を目的として、キャッシュサーバー側でTTLを超えてキャッシュを保持するDNS Pinningを実施している可能性があるからです。<br />
<br />
<h4>
試してみる</h4>
簡易的なDNSコンテンツサーバーを書いて、上記攻撃を試してみました。このDNSサーバーは、上図の通り、1回目の応答ではAレコードとして 203.0.113.5 を返し、2回目以降は 169.254.169.254 を返します。その結果、以下のようにSSRF攻撃に成功しました。<br />
<blockquote>
<pre class="src">Target IP : 203.0.113.5
{
"Code" : "Success",
"LastUpdated" : "2019-03-03T08:00:51Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIAxxxxxxxxxxxxxxxx",
"SecretAccessKey" : "zCc1sxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Token" : "FQoGZXIvYXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
"Expiration" : "2019-03-03T14:30:31Z"
}
Actual IP : 169.254.169.254 </pre>
</blockquote>
gethostbynameの時点では、アクセス対象のIPアドレスは 203.0.113.5 (Taget IPとして表示)でしたので、事前のIPアドレスチェックはかいくぐっていることがわかります。<br />
Actual IPは、curlが実際にアクセスしたIPアドレスであり、169.254.169.254となっています。<br />
<br />
<h4>
対策</h4>
DNS Rebinding攻撃を前提としない条件でも、接続先のURLについて以下のチェックが必要です。<br />
<ul>
<li>プロトコル(スキーム)が http か https のいずれかである</li>
<li>接続先ホストに紐づくIPアドレスが禁止されたものでない</li>
</ul>
そして、IPアドレスのチェックがそもそも容易でないことは<a href="https://blog.tokumaru.org/2018/12/introduction-to-ssrf-server-side-request-forgery.html">SSRF徹底入門</a>にも書きました。加えて、DNS Rebinding攻撃の対策もやっかいです。gethostbyname関数の呼び出しに代えてdns_get_record関数等によりDNSクエリを実行して、TTLを確認することは可能ですが、正常系でもTTLが 0 になることはあり得るので、リトライなどの複雑な処理が必要になります。処理が複雑になると、<a href="https://blog.tokumaru.org/2019/02/caution-bcrypt-with-sha512.html">それ自体が脆弱性の原因になる</a>ので、ちょっとやりたくない実装です。<br />
<br />
先のスクリプトでは、PHPのcurl実装で使用できるCURLINFO_PRIMARY_IPを利用して、「実際に接続したホストのIPアドレス」を表示しています。攻撃によるリスクが情報漏えいのみである場合は、このIPアドレスを検証して、許可されていない場合はエラーにすることで漏洩を回避できます。ただし、これで完全な対策になるかは、徳丸自身は検証していません。<br />
<br />
いっそのこと…ということで、外部コンテンツの取得をPROXY経由にするという対策があります。PROXYなら、接続先のホワイトリストやブラックリストによる制限は容易だからです。これだと、DNS Rebinding以外の対策も同時に行えます。<br />
<br />
PROXYサーバーを用意するほどでもない…というケースでは、Amazonが推奨しているiptablesによる対策が考えられます。先の記事でも引用しましたが、再掲します。<br />
<blockquote>
<pre class="src">sudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP</pre>
<a href="https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/instance_IAM_role.html">Amazon ECS コンテナインスタンスの IAM ロール - Amazon Elastic Container Service</a> より引用</blockquote>
詳しくは、上記ドキュメントを参照ください。<br />
<br />
<h4>
補足</h4>
DNS Rebindingの一般的な対策(アクセスされる側)は以下のいずれかを実装することです(両方でもよい)。<br />
<ul>
<li>Hostヘッダの検証(<a href="https://www.atmarkit.co.jp/ait/articles/1103/01/news125_3.html">参考</a>)</li>
<li>適切な認証とアクセス制御</li>
</ul>
AWSの 169.254.169.254 は両者のどちらもやっていないことはイケテナイと個人的には思います。仮に Host ヘッダがIPアドレス以外のケースをエラーにしておけば、DNS Rebinding攻撃でここから情報を盗むことはできません。今更変更は難しいかもしれませんが、ここはチェックしていただきたかったですね。<br />
<br />
<h4>
まとめ</h4>
DNS RebindingによるSSRF対策回避について説明しました。要約すると、「やってみたらできました」ということです。SSRF脆弱性の対策は難しいので、任意URLを受け取る仕様そのものを見直すか、本稿で紹介したPROXYあるいはiptablesによる対策を推奨します。ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-82309318426705621602019-02-25T08:31:00.000+09:002019-02-25T19:49:54.418+09:00bcryptの72文字制限をSHA-512ハッシュで回避する方式の注意点宅ふぁいる便から平文パスワードが漏洩した件を受けて、あらためてパスワードの安全な保存方法が関心を集めています。現在のパスワード保存のベストプラクティスは、パスワード保存に特化したハッシュ関数(ソルトやストレッチングも用いる)であるbcryptやArgon2などを用いることです。PHPの場合は、PHP5.5以降で使用できるpassword_hash関数が非常に便利ですし、他の言語やアプリケーションフレームワークでも、それぞれ用意されているパスワード保護の機能を使うことはパスワード保護の第一選択肢となります。<br />
<br />
なかでもbcryptは、PHPのpassword_hash関数のデフォルトアルゴリズムである他、他の言語でも安全なハッシュ保存機能として広く利用されていますが、パスワードが最大72文字で切り詰められるという実装上の特性があり、その点が気になる人もいるようです(この制限はDoS脆弱性回避が目的です)。<br />
72文字による切り詰めを回避するためのアイデアとして、bcryptに与えるパスワードを前処理としてSHA-512ハッシュを求めてbcryptの入力とする方法を見かける場合があります。疑似コードで書くと以下となります。<br />
<style type="text/css">
.src { font-family: Consolas, Menlo, 'Liberation Mono', Courier, monospace; }
</style>
<br />
<blockquote>
<pre class="src">$hash = bcrypt(sha512($password)); // パスワードをSHA-512ハッシュを求めた後bcryptで処理する</pre>
</blockquote>
今までは個人ブログ(<a href="https://blog.ohgaki.net/password_hash-important-limitation">これ</a>など)で見かける程度でしたが、弊社のお客様から、Dropboxのパスワード保存方式がこれだと教えていただきました。<br />
<ul>
<li><a href="https://blogs.dropbox.com/tech/2016/09/how-dropbox-securely-stores-your-passwords/">How Dropbox securely stores your passwords | Dropbox Tech Blog</a></li>
</ul>
以下はDropboxで採用している方式の模式図です(上記ブログ記事より引用)。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-Q9pcAjA3FUU/XHJND7cxC7I/AAAAAAAARbA/r24hxMJoULI-4BElsEFSD214Fn-v6snXgCLcBGAs/s1600/dropbox-password-hash.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="439" data-original-width="436" src="https://4.bp.blogspot.com/-Q9pcAjA3FUU/XHJND7cxC7I/AAAAAAAARbA/r24hxMJoULI-4BElsEFSD214Fn-v6snXgCLcBGAs/s1600/dropbox-password-hash.png" /></a></div>
<br />
この図から分かるように、SHA-512→bcrypt→AES256(暗号化)と3段階の処理が入っていることになります。過剰なまでにパスワード保護が入っている背景には、Dropboxは以前パスワード情報を漏洩している「前科」があるからかもしれません。<br />
<ul>
<li><a href="https://news.mynavi.jp/article/20160901-a240/">Dropbox、6800万のアカウントデータを漏洩 - パスワードの変更を</a>(2016年9月1日の記事) </li>
</ul>
Dropboxが採用している方式ということで、自分(自社)でもこの方式を採用しようと思う人がいるかもしれませんが、このSHA-512とbcryptを二重に適用するという方法は重大な落とし穴があるため、筆者としては、素直にbcryptのみを使うことを推奨します。以下、その落とし穴について説明します。<br />
<br />
<h4>
SHA-512→bcrypt方式の詳細検討</h4>
SHA-512ハッシュは、その名前の通り512ビットすなわち64バイトのハッシュ値が生成されますが、多くの場合16進数文字列の形で用いられます。その場合は128バイト長となり、bcryptにより72文字に切り詰めされます。これだとかなりの情報が失われますし、そもそも切り詰めが嫌で始めたことなのに、半分近くの情報が失われるのでは何をやっているのかという気分になるでしょう。同様に、base64エンコードしても88バイト長なので、切り詰めが発生することには変わりません。<br />
このような事情から、先に紹介した<a href="https://blog.ohgaki.net/password_hash-important-limitation">個人ブログ記事</a>では、SHA-512のバイナリ形式をbcryptにパスワードとして与える実装になっています。バイナリ形式だと64バイトですから、「切り詰め」の対象にはならないはずです。<br />
<br />
すなわちPHPの場合、以下のような実装です。<br />
<blockquote class="tr_bq">
<pre class="src">// ハッシュ値の生成
$sha512 = hash('sha512', $password, true); // true はバイナリの指定
$hash = password_hash($sha512, PASSWORD_DEFAULT);
// パスワードの照合
$sha512 = hash('sha512', $password, true); // true はバイナリの指定
$result = password_verify($sha512, $hash);</pre>
</blockquote>
<br />
<h4>
PHPのbcrypt実装はバイナリセーフでない</h4>
ここで問題が生じます。PHPのbcrypt実装はオペレーティングシステムのcrypt(3)ライブラリに依存しており、NULLターミネート形式の文字列でパスワードを受け取ります。すなわち、bcryptは72文字の切り詰め問題に加えて、「バイナリセーフでない」という仕様上の制約があります。このため、SHA-512ハッシュ(バイナリ)中にNULLバイトがあると、そこから先を「切り詰め」してしまうという問題があります。最悪ケースとしては、SHA-512ハッシュの先頭がNULLバイトになる場合で、crypt関数にはゼロ文字のパスワードが指定されることになります。<br />
この性質を持つパスワードの例として下記(8文字、99文字)があります。<br />
<blockquote class="tr_bq">
<pre class="src">Aaaaaa3@
A very loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong password
</pre>
</blockquote>
以下のように、SHA-512ハッシュの先頭はNULLバイトになります。<br />
<blockquote class="tr_bq">
<pre class="src">$ echo -n Aaaaaa3@ | sha512sum
<span style="color: red;">00</span>11780c00726845802482273be4b2e9329a5d403276b5088fc3e49ca866262632108b32dd2950b680a32eb3808ec9e5710af59c6f6f60f6bbcc9e17098f8685 -
$ echo -n A very loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong password | sha512sum
<span style="color: red;">00</span>895faa444575854626475e154dcb8407670af59d9be977a0aa855a49702e45b9c2aad59befe77241bf48f870b08c06bcf80726a6887f41689dc1f0ed977506 -</pre>
</blockquote>
これらは、SHA-512ハッシュの先頭バイトが共にNULLになるので、これらのハッシュ値によるログインでは、相互にパスワードを入れ替えてもログインできるはずです。試してみましょう。<br />
<blockquote class="tr_bq">
<pre class="src">// 長いパスワードでハッシュ値を求める
$password = 'A very loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong password';
$sha512 = hash('sha512', $password, true);
$hash = password_hash($sha512, PASSWORD_DEFAULT);
// 短いパスワード Aaaaaa3@ で照合する
$password = 'Aaaaaa3@';
$sha512 = hash('sha512', $password, true);
$result = password_verify($sha512, $hash);
var_dump($result);</pre>
</blockquote>
このスクリプトは true を表示します。すなわち、ログイン成功となります。<br />
ここでは2例のみ示しましたが、SHA-512ハッシュの先頭バイトがNULLとなる確率は1/256ですから、上記方式で実装したログインプログラムでは、任意ユーザーが1/256の確率でパスワード「Aaaaaa3@」で不正ログインできることになります。これはとんでもない大穴ですよね。<br />
<br />
<h4>
ではどうすればよいか</h4>
NULLバイトを防ぐ目的であれば、バイナリハッシュ値を255進数に変換し、1~255のバイト値にマップする(base255とでも言いますか)ことで防げます。しかし、そのような複雑な処理を追加することは、バグの原因になり、ひいては脆弱性の要因になります。<br />
なので、bcryptを使う場合は、72文字制限やバイナリセーフでない問題は仕様として受け入れ、そのままで使うことをお勧めします。<br />
先に紹介したDropboxのブログでは、「For ease of elucidation, in the figure and below we omit any mention of binary encoding (base64) (私訳: 説明を簡単にするために、下図および以下の説明では、バイナリエンコーディング(base64)については言及しません)」とあり、base64エンコーディングを採用しているようでもありますが、詳細は不明です。<br />
<br />
どうしても、これら制限が受容できない場合は、Argon2などのアルゴリズムを使うことで回避できます。PHPの場合は、以下で実現可能です(PHP 7.2以降)。<br />
<blockquote class="tr_bq">
<pre class="src">$hash = password_hash($sha512, PASSWORD_ARGON2I); // PHP 7.2以降
$hash = password_hash($sha512, PASSWORD_ARGON2ID); // PHP 7.3以降</pre>
</blockquote>
PHPのArgon2実装はcrypt(3)に依存しておらず、72文字制限はなくバイナリセーフであるようです。PHP 7.3以降で使用できるオプションPASSWORD_ARGON2IDは、サイドチャネル攻撃とGPUによるクラッキングの両方の耐性を備えたアルゴリズムです。<br />
<br />
<h4>
まとめ</h4>
bcryptとSHA-512ハッシュを組み合わせて使う方式の注意点を説明しました。<br />
私は、拙著「<a href="https://www.amazon.co.jp/dp/B07DVY4H3M/">安全なWebアプリケーションの作り方 第2版</a>」で以下のように説明しました。<br />
<blockquote class="tr_bq">
PHPには、これらの施策をまとめて使いやすくしたpassword_hashという安全で便利な関数があります(PHP5.5.0以降)。極力password_hashを使うことを推奨します。<br />
また、PHPに限らず、パスワード保存機能は独自実装せずに、安全なライブラリやフレームワークの機能を用いることを推奨します。</blockquote>
「独自実装せずに、安全なライブラリやフレームワークの機能を用いる」とは、ライブラリ等を使う際に余計なことをしないということでもあり、本稿で紹介した例は「余計なこと」をやってしまった副作用の例であると考えます。<br />
<br />
一般論としても、プログラムは複雑になればなるほどバグが混入しやすくなり、ひいては脆弱性の原因にもなります。プログラムを簡明に保つことは、安全なプログラムを開発する上でも重要だと考えます。ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0tag:blogger.com,1999:blog-3689516124953269065.post-32875011201374327302019-01-15T10:21:00.000+09:002019-01-15T11:15:22.947+09:00CWE-20入門<h4>
サマリ</h4>
この記事の想定読者は、企業等の脆弱性ハンドリング担当者です。脆弱性情報にしばしば出てくるCWE-20の読み解き方を説明します。<br />
<h4>
CWEとは</h4>
CWEはCommon Weakness Enumerationの略で、日本語では「共通脆弱性タイプ一覧」と訳されています。この訳からも分かるように、脆弱性をタイプ毎に分類しているものです。つまり、SQLインジェクション、XSS、バッファオーバーフロー等に、CWE-XXXという「分類番号」をふったものと考えるとわかりやすいでしょう。以下は、IPAが公表しているCWEの解説記事からの引用です。<br />
<blockquote class="tr_bq">
1999年頃から米国政府の支援を受けた非営利団体のMITRE(*2)が中心となり仕様策定が行われ、2006年3月に最初の原案が公開されました。その後、40を超えるベンダーや研究機関が協力して仕様改善や内容拡充が行われ、2008年9月9日にCWEバージョン1.0が公開されました。<br />
<a href="https://www.ipa.go.jp/security/vuln/CWE.html">共通脆弱性タイプ一覧CWE概説:IPA 独立行政法人 情報処理推進機構</a> から引用</blockquote>
代表的なCWEは、この記事中の以下の図を見るとわかりやすいと思います。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-3jKXPqpfQcg/XDyMnXj_MtI/AAAAAAAAQWw/QQWjUZ4CY50rpyD_BVKA1xJbW3qz3XqoQCLcBGAs/s1600/cwex.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="576" data-original-width="768" src="https://4.bp.blogspot.com/-3jKXPqpfQcg/XDyMnXj_MtI/AAAAAAAAQWw/QQWjUZ4CY50rpyD_BVKA1xJbW3qz3XqoQCLcBGAs/s1600/cwex.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://www.ipa.go.jp/security/vuln/CWE.html">共通脆弱性タイプ一覧CWE概説:IPA 独立行政法人 情報処理推進機構</a> より引用</div>
<br />
CWEの特徴の一つは、上図から明らかなように、階層的な分類になっていることです。たとえば、インジェクションCWE-74の下の階層には、XSS、SQLインジェクション、OSコマンドインジェクションなどの著名な脆弱性がぶら下がっていて、これらがインジェクションという共通の脆弱性のタイプに属していることがわかります。<br />
<br />
<h4>
CWE-20とは</h4>
本稿のテーマCWE-20は、インジェクションも包含する大分類になっています。先程引用した図からCWE-20関連のみを抽出したものを以下に示します<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-MJFc9WVwxEE/XDwMw_CclLI/AAAAAAAAQWY/m5hwOwf5zvMlKZ0MrbxU5Qzu8QYryZBZACLcBGAs/s1600/cwe-20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="352" data-original-width="684" src="https://4.bp.blogspot.com/-MJFc9WVwxEE/XDwMw_CclLI/AAAAAAAAQWY/m5hwOwf5zvMlKZ0MrbxU5Qzu8QYryZBZACLcBGAs/s1600/cwe-20.png" /></a></div>
<br />
前述のインジェクションに加えてパストラバーサルなども含まれていますね。すなわち、外部からの入力値に起因する脆弱性がCWE-20としてまとめられています。<br />
それにしても、CWE-20は「不適切な入力確認」(Improper Input Validation)と説明されていますが、これらはバリデーションの問題でしょうか。<br />
そうではありません。パストラバーサルはバリデーションで対策可能ですが、インジェクション系のXSSやSQLインジェクションがバリデーションにより対策できない(完全な対策にならない)ことは明らかです。なのに、どうして、CWE-20は「不適切な入力確認」なのでしょうか。その答えは、本家のCWE-20の解説中にあります。<br />
<blockquote class="tr_bq">
Some people use "input validation" as a general term that covers many different neutralization techniques for ensuring that input is appropriate, such as filtering, canonicalization, and escaping. Others use the term in a more narrow context to simply mean "checking if an input conforms to expectations without changing it."<br />
<br />
<a href="https://cwe.mitre.org/data/definitions/20.html">CWE - CWE-20: Improper Input Validation (3.2)</a>より引用</blockquote>
私訳を以下に示します。<br />
<blockquote class="tr_bq">
フィルタリングや正規化、エスケープなど、入力が適切であることを確実にするためのさまざまな無効化手法をカバーする包括的な用語として「入力検証(Input Validation)」を使用する人もいます。他の人達は、より狭い文脈、すなわち、単に「入力を変更することなく、期待される値であることを確認する」という意味でこの用語を用います。</blockquote>
ということで、Input Validationはエスケープやフィルタリングを含む包括的な用語として使われる場合があることがわかります。CWE-20のInput Validationはこちらの意味でしょう。そうでないと、SQLインジェクションやXSS等をカバーする理由を説明できません。<br />
<br />
<h4>
CWE-20は大分類のはずなのに、個別脆弱性がしばしばCWE-20とされる</h4>
ここまで、CWE-20は脆弱性の大分類と説明しましたが、現実には個別の脆弱性がCWE-20と分類されるケースが多くあります。以下は、JVN iPediaで、CWE-20かつCVSSv2で7.0以上の脆弱性を検索した結果です。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-bMUM4XPQwNA/XDyNxhjSF9I/AAAAAAAAQW8/Ts3Teln30F4LGvXZEFOrdrzIfK0LepDBwCLcBGAs/s1600/cwe-20-vuls.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="524" data-original-width="830" src="https://4.bp.blogspot.com/-bMUM4XPQwNA/XDyNxhjSF9I/AAAAAAAAQW8/Ts3Teln30F4LGvXZEFOrdrzIfK0LepDBwCLcBGAs/s1600/cwe-20-vuls.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://jvndb.jvn.jp/search/index.php?mode=_vulnerability_search_IA_VulnSearch&lang=ja&keyword=&useSynonym=1&vendor=&product=&datePublicFromYear=&datePublicFromMonth=&datePublicToYear=&datePublicToMonth=&dateLastPublishedFromYear=&dateLastPublishedFromMonth=&dateLastPublishedToYear=&dateLastPublishedToMonth=&severity%5B%5D=01&cwe=20&searchProductId=">JVN iPediaでCWE-20でかつCVSS v2 で7以上を検索する</a></div>
<br />
CWE-20が大分類だとすると、個別脆弱性がCWE-20に分類されるのはおかしいですね。上記はどのようなものなのでしょうか。そこで、以下、この日記で過去に紹介した脆弱性の中からCWE-20とされているものを2件紹介して、CWE-20の実態に迫ります。<br />
<br />
<h4>
CWE-20の例(1)CGI版PHPのRCE脆弱性CVE-2012-1823</h4>
以下の記事で紹介した脆弱性です。<br />
<br />
<a href="https://blog.tokumaru.org/2012/05/php-cgi-remote-scripting-cve-2012-1823.html">CGI版PHPにリモートからスクリプト実行を許す脆弱性(CVE-2012-1823)</a><br />
<br />
PHPがCGIモードで動いている場合のみですが、外部から任意スクリプト実行可能な凶悪な脆弱性でした。この脆弱性はCWE-20に分類されています。<br />
この脆弱性の原因は、CGIモードの場合クエリ文字列にスペース区切りで指定した文字列がCGIプログラムのコマンドライン引数として指定される仕様を悪用して、PHPに -s や -d 等のオプションを指定できてしまうところにありました。<br />
この脆弱性のPHPソースコード上の問題は、以下の記事が詳しいです。<br />
<br />
<a href="https://sect.iij.ad.jp/d/2012/05/087662.html">CVE-2012-1823 CVE-2012-2311 PHPのCGIモードにおける脆弱性について</a><br />
<br />
この記事にもあるように、脆弱性の根本原因は、CGIモードの場合不要なオプションパラメータの処理が存在すること自体にあります。したがって、脆弱性の対策は、不要なオプションパラメータの処理を除去することであり、入力バリデーションを追加することではありません。<br />
<br />
<h4>
CWE-20の例(2)Joomla!のRCE脆弱性CVE-2015-8562</h4>
以下の記事やスライドで紹介した脆弱性です。やはり、CWE-20と分類されています。<br />
<br />
<a href="https://blog.tokumaru.org/2015/12/joomla-zero-day-attack-caused-by-php.html">Joomla!の「ゼロデイコード実行脆弱性」はPHPの既知の脆弱性が原因</a><br />
<a href="https://www.slideshare.net/ockeghem/joomlacve20158562">脆弱性は誰のせい? PHP、MySQL、Joomla! の責任やいかに | slideshare</a><br />
<br />
この脆弱性に対する攻撃は、もはや芸術的と表現しても良いくらいの高度なものです。以下の3つのソフトウェアの脆弱性および好ましくない仕様を組み合わせています。詳しくは上記の記事を参照ください。<br />
<br />
Joomla! : CVE-2015-8562<br />
PHP : CVE-2015-6835<br />
MySQL : utf8_general_ci 指定した列にutf8の4バイト形式の文字を追加すると、その文字以降が切り詰められる仕様<br />
<br />
そして、私自身は、この攻撃の根本原因はCVE-2015-6835(CWE-416: Use After Freeに分類)と考えています。攻撃全体としては、<a href="https://blog.tokumaru.org/2017/09/introduction-to-object-injection.html">安全でないデシリアライゼーション</a>(CWE-502)に該当します。<br />
Joomla! のCVE-2015-8562は何がいけなかったかというと、Joomla!はセッションストレージとしてMySQLを利用していて、その結果、PHPのシリアライズ後のセッション情報をMySQLに格納しているのに、シリアライズ結果がバイナリとなる点を十分に考慮しておらず、utf8_general_ciという型を使用していたところにあります。<br />
Joomla!側の対処としては、緊急対処として攻撃経路となるUser-Agentその他のHTTPヘッダをセッションに格納することをやめ、根本対策としてシリアライズ後のセッション情報をBase64エンコードしてから格納するようにしています。やはり、入力バリデーションを追加したわけではありません。<br />
<br />
<h4>
CWE-20の現実の使われ方</h4>
上記で紹介した2つの脆弱性は、いずれも狭義のバリデーションが主原因ではないのに、CWE-20が割り当てられていました。これらは、適当なCWEがないので、「とりあえず外部入力が経路なのでCWE-20にしておこう」という感じでCWE-20があてられているものと推測します。脆弱性情報を見ていると、そのようなケースは多く見受けられます。<br />
また、商用製品の脆弱性情報に多い例として、脆弱性があることは公表するが、その詳細は公表したくないケースなどで、脆弱性分類としてCWE-20がわりあてられるケースがあります。先に、JVN iPediaの検索結果を示しましたが、高危険度のCWE-20脆弱性の例の大半が商用製品であることからも、これは伺えます。<br />
現に、CWE-20の解説には以下のように記載されています。<br />
<blockquote class="tr_bq">
The "input validation" term is extremely common, but it is used in many different ways. In some cases its usage can obscure the real underlying weakness or otherwise hide chaining and composite relationships.<br />
<a href="https://cwe.mitre.org/data/definitions/20.html">CWE - CWE-20: Improper Input Validation (3.2)</a>より引用</blockquote>
日本語訳としてJVNの訳を以下に引用します。<br />
<blockquote class="tr_bq">
「入力の妥当性チェック」という用語は極めて一般的ですが、用語の使い方は様々です。いくつかのケースでは、根本的な脆弱性を曖昧にするためや、関連した複雑な事象を隠すことを目的として使われます。<br />
<a href="https://jvndb.jvn.jp/ja/cwe/CWE-20.html">CWE-20</a> より引用</blockquote>
ということで、CWE-20が使われるケースの多くは、脆弱性公表側の怠慢や隠蔽意図によるケースが多く、悪意がないケースとしては「分類の難しいその他のケース」ということになるかと思います。<br />
<br />
<h4>
まとめ</h4>
CWE-20について解説しました。本来、CWE-20は脆弱性パターンの大分類として取り扱うべきと考えますが、現実にはCWE-20とすべきでない局面でもCWE-20が濫用される傾向があります。私は、CWE-20の濫用は正確な脆弱性情報流通の上では有害だと考えています。<br />
もし読者が、脆弱性情報中にCWE-20と記載されているのを見かけたら、「CWE-20では何もわからないではないか」と舌打ちしつつも、そうせざるを得なかった背景についても想いを馳せると、その脆弱性に対する関心が一層高まるのではないでしょうか。ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com1tag:blogger.com,1999:blog-3689516124953269065.post-68330090066773227692018-12-31T15:58:00.000+09:002018-12-31T15:58:48.816+09:002018年に公表されたウェブサイトからのクレジットカード情報漏えい事件まとめ<h4>
エグゼクティブサマリ</h4>
2018年に公表されたウェブサイトからのクレジットカード情報漏えい事件をまとめた。同年6月1日に改正割賦販売法が施行後も継続してカード情報漏えい事件は発生しており、カード情報「非保持」に対応した攻撃が目立つ結果となった。<br />
<br />
<h4>
事件の一覧</h4>
下表に、本年(2018年)に公表されたウェブサイトからのクレジットカード情報漏えい事件をまとめました。サイト名、漏洩の期間、漏洩件数(最大)、セキュリティコード漏洩の有無、偽決済画面への誘導があったかを記載しています。<br />
<br />
<style type="text/css">
table.sample{
border-top:1px solid #000066;
border-left:1px solid #000066;
border-collapse:collapse;
border-spacing:0;
background-color:#ffffff;
empty-cells:show;
}
.sample th{
border-right:1px solid #000066;
border-bottom:1px solid #000066;
color:#330000;
background-color:#CCCCFF;
background-position:left top;
padding:0.1em 0.1em;
font-weight: normal;
text-align: center;
}
.sample td{
border-right:1px solid #000066;
border-bottom:1px solid #000066;
padding:0.1em 0.4em;
text-align: left;
}
td.tdr { text-align: right; }
</style>
<br />
<table class="sample">
<tbody>
<tr><th>サイト名</th><th>漏洩期間</th><th>漏洩件数</th><th>セキュリティコード</th><th>偽決済画面誘導</th></tr>
<tr><td><a href="http://cou-shop.jugem.jp/?eid=1629">C.O.U.オンラインショップ</a></td><td>2017/8/5~2017/9/15</td><td class="tdr">335</td><td>漏洩</td><td></td></tr>
<tr><td><a href="https://www.paletas.jp/user_data/pdf/20180201.pdf">パレタス オンラインショップ</a></td><td>2017/4/27~2017/7/18</td><td class="tdr">821</td><td>漏洩</td><td></td></tr>
<tr><td><a href="http://www.morinagamilk.co.jp/information2/newsentry-2900.html">健康食品通販サイト<br />(森永乳業株式会社)</a></td><td>2015/1/7~2017/10/16</td><td class="tdr">29,773</td><td></td><td><br /></td></tr>
<tr><td><a href="https://goace.jp/files/user/pdf/180517.pdf">A-Web 倶楽部「宅配サービス」</a></td><td>2017/12/17~2018/3/27</td><td class="tdr">3,412</td><td></td><td></td></tr>
<tr><td><a href="https://www.princehotels.co.jp/file.jsp?id=117608">プリンスホテル予約サイト</a></td><td>~2017/8</td><td class="tdr">66,960</td><td></td><td></td></tr>
<tr><td><a href="https://kensayaku.net/user_data/info20180523">こうのとり検査薬.NET</a></td><td>2017/8/7~2018/1/18</td><td class="tdr">11,314</td><td>漏洩</td><td><br /></td></tr>
<tr><td><a href="http://www.evameva.com/diary/?year=2018&page=10">evameva Online Shop</a></td><td>2018/3/7~2018/6/19</td><td class="tdr">358</td><td>漏洩</td><td><br /></td></tr>
<tr><td><a href="http://www.asahikei.co.jp/oshirase_o.html">アサヒ軽金属工業株式会社<br />「Webショッピングサイト」</a></td><td>2017/1/14~2018/5/25</td><td class="tdr">77,198</td><td></td><td><br /></td></tr>
<tr><td><a href="https://www.trans-cosmos.co.jp/company/news/pdf/2018/181009.pdf">SOKAオンラインストア」</a></td><td>2018/7/30~2018/8/24</td><td class="tdr">2,481</td><td>漏洩</td><td>あり</td></tr>
<tr><td><a href="https://www.i-ori.jp/wordpress/iori-press-release/20181024-0-37803.html">伊織ネットショップ</a></td><td>2018/5/8~2018/8/22</td><td class="tdr">2,145</td><td>漏洩</td><td>あり</td></tr>
<tr><td><a href="https://www.zowhow.com/media/wysiwyg/recipe/zow/20181127_zowhow-pil/20181127_zowhow-pil.pdf">ZOWHOW</a></td><td>2018/4/25~2018/7/18</td><td class="tdr">397</td><td>漏洩</td><td><br /></td></tr>
<tr><td><a href="https://editmode.shop-pro.jp/?mode=f1">EDITMODE</a></td><td>2018/3/7~2018/7/11</td><td class="tdr">14,679</td><td>漏洩</td><td><br /></td></tr>
<tr><td><a href="https://www.ginza-west.co.jp/info_2018/">銀座ウエストオンライン通販サイト</a></td><td>2018/9/12~2018/11/2</td><td class="tdr">668</td><td>漏洩</td><td>あり</td></tr>
<tr><td><a href="https://www.dlmarket.jp/">DLmarket</a></td><td>2018/10/17~2018/11/12</td><td class="tdr">7,741</td><td>漏洩</td><td>あり</td></tr>
</tbody></table>
<br />
<h4>
セキュリティコードが漏洩する事例が多い</h4>
上表からわかるように、セキュリティコードまで漏洩する例が14件中10件と多くなっています。昨年以前でもセキュリティコードが漏洩する事件は継続的に発生しており、以下の記事にまとめたことがあります。<br />
<br />
<a href="https://blog.tokumaru.org/2016/03/blog-post.html">決済代行を使っていてもクレジットカード情報が漏洩するフォーム改ざんに注意</a><br />
<br />
ここでいう「フォーム改ざん」の手口は、以下の記事の「タイプ4」に相当するものです。<br />
<br />
<a href="https://blog.tokumaru.org/2018/10/methods-of-stealing-credit-card-information.html">クレジットカード情報盗み出しの手口をまとめた</a><br />
<br />
この「タイプ4」が日本で初めて使われたのは、上記記事からもわかるように、2013年3月のJINSオンラインショップ事件です。上表の事件にもタイプ4のものが多いのではないかと予想しますが、はっきりタイプ4と思われるのは、パレタスオンラインショップの事件です。同社のリリースには以下のように記されています。<br />
<blockquote class="tr_bq">
弊社公式通販サイトのウェブサーバーに外部からの不正アクセスがあり、不正プログラムが仕組まれたためカード会員データが流出したことが判明しております。</blockquote>
不正プログラムがどのようなものか不詳ですが、有力な仮説としては、下図のように入力フォームにJavaScriptが組み込まれ、フォームに入力したカード情報が攻撃者のサイトに転送されていたのではないでしょうか。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-g1sgQzhcXqM/W8fRCW6QqxI/AAAAAAAAQGA/r82asZTddwghX_DU9m36nGpdDWpAkwxegCPcBGAYYCw/s1600/js-2-credit-card.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="303" data-original-width="747" src="https://1.bp.blogspot.com/-g1sgQzhcXqM/W8fRCW6QqxI/AAAAAAAAQGA/r82asZTddwghX_DU9m36nGpdDWpAkwxegCPcBGAYYCw/s1600/js-2-credit-card.png" /></a></div>
また、同社は対策として下記のように書いています。<br />
<blockquote class="tr_bq">
弊社では、通販サイトにおいて、より信頼性の高い、カード決済代行会社の提供するリンク型システムへの移行を決定しております。また、WEB改ざん検知サービスを導入いたしました。</blockquote>
これら対策は、Type4のカード情報窃取に対して有力な防御策になるものです。<br />
<br />
<h4>
偽決済画面への誘導手口の増加</h4>
SOKAオンラインストアの手口は「偽決済画面」に誘導するというものであり、新しい手口だったので以下の記事で詳しく報告しました。<br />
<br />
<a href="https://blog.tokumaru.org/2018/10/ec.html">ECサイトからクレジットカード情報を盗み出す新たな手口</a><br />
<br />
さらに、この事件以降、偽決済画面への誘導手口が使用された事件が合計4件発生しています(SOKAオンラインストア事件を含む)。それらのうち2件について以下に説明します。<br />
<br />
<h4>
伊織ネットショップの事例</h4>
今治タオルの販売サイト伊織ネットショップの事例は、公式リリースで手口の詳細が公開されていてとても興味深いものです。以下は、正常系の画面遷移です。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-grEsC3rZFDo/XCmf2JBljVI/AAAAAAAAQUI/WRkTG7ih8Nwj6hae8bJbKju0CguXj0eVwCLcBGAs/s1600/iori-before.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="362" data-original-width="550" src="https://4.bp.blogspot.com/-grEsC3rZFDo/XCmf2JBljVI/AAAAAAAAQUI/WRkTG7ih8Nwj6hae8bJbKju0CguXj0eVwCLcBGAs/s1600/iori-before.png" /></a></div>
<br clear="all" />
<a href="https://www.i-ori.jp/wordpress/iori-press-release/20181024-1-37812.html">【重要】カード情報流出についての、ご質問とご回答 - タオル専門店「伊織」</a> より引用<br />
<br />
そして、以下が攻撃後の画面遷移で、「偽のカード入力画面」が画面遷移に挿入されています。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-VPwDQxcc-q0/XCmf2KlSHmI/AAAAAAAAQUE/DIxGbAMAcrUYsZe-57qbftE7Yq0Gmw__wCEwYBhgL/s1600/iori-after.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="377" data-original-width="736" src="https://3.bp.blogspot.com/-VPwDQxcc-q0/XCmf2KlSHmI/AAAAAAAAQUE/DIxGbAMAcrUYsZe-57qbftE7Yq0Gmw__wCEwYBhgL/s1600/iori-after.png" /></a></div>
<br clear="all" />
<a href="https://www.i-ori.jp/wordpress/iori-press-release/20181024-1-37812.html">【重要】カード情報流出についての、ご質問とご回答 - タオル専門店「伊織」</a> より引用<br />
<br />
この画面遷移は、私の目にはいささか「雑」に見えます。カード情報の入力画面は本来「ご注文内容確認」の後に出てくるものですし、オリジナルの画面遷移もそうなっていますが、改ざん後の遷移では、カード入力(偽)→注文内容確認→カード入力(正)と不自然な遷移になっています。ITに詳しい人が使えば不自然さに気づきそうなものですが、しばらくは不自然さに気づかれなかった模様です。<br />
伊織ネットショップの事件では、事件発覚のきっかけは、「2018年8月22日夕方、弊社サイトにおいてフィッシングサイト(偽のクレジットカード番号の入力画面)へジャンプする事象が確認され」(<a href="https://www.i-ori.jp/wordpress/iori-press-release/20181024-0-37803.html">同社リリース</a>より引用)とあるので、前記不自然さにより異常が発覚した可能性はあります。<br />
<br />
<h4>
銀座ウエストの事例</h4>
一方、銀座ウエストの事例はさらに巧妙になっています。偽の決済サイトに進むことは同じですが、偽の決済サイトは、「決済代行会社の類似したドメインURL」であったと報告されています。下図は銀座ウェストのリリースからの引用です。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-iN7eKtd8QkQ/XCmf2KMTsUI/AAAAAAAAQUA/8kwuulWHI94iEax2eEsurBkEPk6HuH71gCEwYBhgL/s1600/ginza-west.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="363" data-original-width="560" src="https://1.bp.blogspot.com/-iN7eKtd8QkQ/XCmf2KMTsUI/AAAAAAAAQUA/8kwuulWHI94iEax2eEsurBkEPk6HuH71gCEwYBhgL/s1600/ginza-west.png" /></a></div>
<br clear="all" />
<a href="https://www.ginza-west.co.jp/info_2018/faq.html">洋菓子舗ウエスト|よくあるご質問と回答</a> より引用(*1)<br />
<br />
通常、偽のサイトは本物そっくり(本物と同一)の画面デザインにしますが、この例ではドメイン名まで本物に似せていたことがわかります。伊織の事件よりも巧妙化しています。<br />
<br />
*1 この図は、<a href="https://blog.tokumaru.org/2018/10/ec.html">私のブログ記事</a>の図を参考に、図を現実の事件に合わせて変更したものです。そのため元のブログ記事と図の意匠が類似していますが、私は図版利用の許可をしておりますので権利上の問題はありません。<br />
<br />
<h4>
まとめ・対策</h4>
2018年に公表されたウェブサイトからのクレジットカード情報漏えい事件をまとめました。本年後半には、「偽決済画面」への誘導手口が目立つ結果となり、以前<a href="https://blog.tokumaru.org/2018/10/ec.html">SOKAオンラインショップの事件を紹介</a>した際に『紹介した手口は、クレジットカード情報「非保持化」では対策できないものであり、今後のクレジットカード情報漏洩事件の主流となる可能性があります』と書いた通りになりつつあります。<br />
対策としては、先に書いたように、まずは基本的な以下の施策が必須となります。<br />
<ul>
<li>ウェブアプリケーションやソフトウェアライブラリ、プラットフォームの脆弱性対策(パッチ適用など)</li>
<li>管理者等のパスワードを強固にする(可能ならばインターネットからは管理者ログインできないよう設定する)</li>
</ul>
加えて、以下の対策を推奨します。<br />
<ul>
<li>Web Application Firewall(WAF)の導入</li>
<li>ファイルパーミッションとファイルオーナーの適切な設定</li>
<li>改ざん検知システムの導入</li>
</ul>
<br />
<br />ockeghemhttp://www.blogger.com/profile/13465836435601518769noreply@blogger.com0