Qiitaを見ると使われるTreasure Dataの追跡から逃げてみる

# 背景
Qiitaのページを見るとTrasure DataのAPIが実行されてページの閲覧とかが記録されているようです。
QiitaのページのHTMLをソースコードを確認すると以下のような実装がされていることが確認できます。

```javascript
// Configure an instance for your database
var td = new Treasure({
  host: 'in.treasuredata.com',
  writeKey: '10614/f5a4453704ad12facc47fe5281fd57526a6119e9',
  database: 'qiita_public',
  startInSignedMode: true
});

// Enable cross-domain tracking
td.set('$global', 'td_global_id', 'td_global_id');
// Track pageview information to 'pageviews' table
td.set('pageviews_all', {"query_parameters":{},"path_parameters":{"controller":"public/items","action":"show","user_id":"mima_ita","type":"items","id":"9a002f772e5789bd682f"},"user_id":47856})
td.trackPageview('pageviews_all');
// 略
td.trackEvent('pageviews_article',{
    user_id: "47856",
    article_id: "973108",
    article_uuid: "9a002f772e5789bd682f",
    td_description: "&quot;# まえがき\nむかーし、グーグルで適当な個人サイト探すより、なるべく公式ドキュメントを参照してねという動画を作ったことがあります。\n\n\u0026amp;lt;blockquote class=\u0026amp;quot;twitter-tweet\u0026amp;quot; data-lang=\u0026amp;quot;ja\u0026amp;quot;\u0026amp;gt;\u0026amp;lt;p lang=\u0026amp;quot;ja\u0026amp;quot; dir=\u0026amp;quot;ltr\u0026amp;quot;\u0026amp;gt;ExcelVBA　一口講座 資料の調べ方 \u0026amp;lt;a href=\u0026amp;quot;https://t.co/loD36SEETu\u0026amp;quot;\u0026amp;gt;https://t.co/loD36SEETu\u0026amp;lt;/a\u0026amp;gt; \u0026amp;lt;a href=\u0026amp;quot;https://twitter.com/hashtag/sm21809660?src=hash\u0026amp;amp;amp;ref_src=twsrc%5Etfw\u0026amp;quot;\u0026amp;gt;#sm21809660\u0026amp;lt;/a\u0026amp;gt; \u0026amp;lt;a href=\u0026amp;quot;https://twitter.com/hashtag/%E3%83%8B%E3%82%B3%E3%83%8B%E3%82%B3%E5%8B%95%E7%94%BB?src=hash\u0026amp;amp;amp;ref_src=twsrc%5Etfw\u0026amp;quot;\u0026amp;gt;#ニコニコ動画\u0026amp;lt;/a\u0026amp;gt;\u0026amp;lt;/p\u0026amp;gt;\u0026amp;amp;mdash; m.ita (@mima_ita) \u0026amp;lt;a href=\u0026amp;quot;https://twitter.com/mima_ita/status/1152339345652629504?ref_src=twsrc%5Etfw\u0026amp;quot;\u0026amp;gt;2019年7月19日\u0026amp;lt;/a\u0026amp;gt;\u0026amp;lt;/blockquote\u0026amp;gt;\n\u0026amp;lt;script async src=\u0026amp;quot;https://platform.twitter.com/widgets.js\u0026amp;quot; charset=\u0026amp;quot;utf-8\u0026amp;quot;\u0026amp;gt;\u0026amp;lt;/script\u0026amp;gt;\n\n残念ながら、「動けばいいんだよ」勢には効果がなかったようなので、最近、見つけた一見動くが、公式ドキュメントで認められていないことやってバグが出る例を出してリベンジしようと思います。\n\n# 問題\nExcel VBAで動的配列の作成されているか否かを判断するにはどうしたらいいでしょうか？\nたとえば以下のコードでエラーになる場合と、ならない場合を判別する方法を考えてみてください。\n\n```vb\nPublic Sub test()\n     Dim v() As Variant\n     \u0026amp;#39; Debug.Print UBound(v) \u0026amp;#39; エラーになる\n     \n     ReDim v(1) As Variant\n     Debug.Print UBound(v)\n     \n     Erase v\n     \u0026amp;#39; Debug.Print UBound(v) \u0026amp;#39; エラーになる\nEnd Sub\n```\n\n\n# 想定される回答\nおそらく、googleとかで検索した場合に、この問題で想定される回答は次の４つになります。\n\n##OnErrorを使用する方法\n動的配列が作成されていなかったり、削除されている場合でUBoundやLBoundでエラーが発生するなら、素直にエラーハンドリングをして動的配列の作成の有無をチェックします。\n\n```vb\nPublic Sub testArraySample()\n    Dim v() As Variant\n    Debug.Assert isEmptyArray(v) = True\n    \n    ReDim v(1) As Variant\n    Debug.Assert isEmptyArray(v) = False\n    \n    Erase v\n    Debug.Assert isEmptyArray(v) = True\nEnd Sub\n\nPrivate Function isEmptyArray(v() As Variant) As Boolean\nOn Error GoTo ErrUbound:\n    Dim tmp As Long\n    tmp = UBound(v)\n    isEmptyArray = False\n    Exit Function\nErrUbound:\n    If Err.Number \u0026amp;lt;\u0026amp;gt; 9 Then\n        Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext\n    End If\n    isEmptyArray = True\nEnd Function\n```\n\n##SafeArrayGetDimを利用する方法\nVBAの配列は動的であれ、固定であれ、SafeArrayになっています。\nhttp://msdn.microsoft.com/en-us/library/windows/desktop/ms221482%28v=vs.85%29.aspx\n\nこのSafeArrayの次元数はSafeArrayGetDimで取得できます。次元数が０ならまだ配列が作られていないと判断できます。\nhttp://msdn.microsoft.com/en-us/library/windows/desktop/ms221539%28v=vs.85%29.aspx\n\n\n```vb\nPrivate Declare PtrSafe Function SafeArrayGetDim Lib \u0026amp;quot;oleaut32\u0026amp;quot; (ByRef psa() As Any) As Long\n\nPrivate Function isEmptyArray(v() As Variant) As Boolean\n    If SafeArrayGetDim(v) \u0026amp;lt;\u0026amp;gt; 0 Then\n        isEmptyArray = False\n    Else\n        isEmptyArray = True\n    End If\nEnd Function\n```\n\n##Sgnを使う方法\nSgnを利用することで判定できます。\nなんで動くかはよくわかりません（ﾄﾞﾔｧ！)\n\n```vb\nPrivate Function isEmptyArray(v() As Variant) As Boolean\n    If Sgn(v) Then\n        isEmptyArray = False\n    Else\n        isEmptyArray = True\n    End If\nEnd Function\n```\n\n\n## Not Notを利用する場合\nNot Notのテクニックを使うことで判定できます。\nなんで動くかはよくわかりません（ﾄﾞﾔｧ！)\n\n```vb\nPrivate Function isEmptyArray(v() As Variant) As Boolean\n    If Not Not v Then\n        isEmptyArray = False\n    Else\n        isEmptyArray = True\n    End If\nEnd Function\n```\n\n# 各実装の評価\nまず、簡単な動作確認だと、この４つのソースコードは、それっぽく動作します。\nその上で、レビューで、このコードに遭遇した場合どう判断するか考えてみてください。\n\n仮に私がレビュワーだった場合は以下のような判定をします。\n\n##OnErrorを使用する方法\nVBAの言語仕様通りの実装なのでレビューを通します。\n\n##SafeArrayGetDimを利用する方法\nWinAPIを使っているので32bitと64bitの両方のExcelで動くか微妙な宣言ですが、少なくともMSDNの根拠があるので**32bit/64bit両方のExcelで動作確認しておくこと**という条件でレビューを通します。\n\nまた、レビュー時に、SafeArrayのあたりの根拠の説明ができない場合は、仮に動くコードでも却下します。\n\n\n##Sgnを使う方法\nこれは却下します。\nこの実装の動かないケースについては以下のページに詳しく書いてあります。\n\n**VBAで配列のNull判定にSgn関数を使ってはいけない**\nhttps://qiita.com/satoko138/items/7e06dda56683065968f7\n\n簡単にいうと、配列の割り当て前はアドレスが0になる。\nIF分岐でそのままだと判定できないので、Sgn関数を通してからIF分岐に渡している。\n\nしかし、[Sgn関数の仕様](https://docs.microsoft.com/en-us/openspecs/microsoft_general_purpose_programming_languages/ms-vbal/b7bbdcd3-c8fc-4f42-b362-18ace8f2be55)として引数には「Double containing any valid numeric expression.」とあるので、配列などを渡すと、一見動く場合もあるが、予期せぬ動作をしてしまう。\n\n\n## Not Notを使う方法\nこれも却下です。\nSgn関数の代わりにNot論理演算子をつかって回避していますが、[VBAの論理演算子](https://docs.microsoft.com/en-us/openspecs/microsoft_general_purpose_programming_languages/ms-vbal/2d70780d-6e99-47d6-9759-2b3a46b8e862\n)の説明に「§ A logical operator is invalid if the declared type of any operand is an array or a UDT.」、つまり論理演算子に配列渡しても無効と明記してあります。\n\n「Sgnを使う方法」とことなり、予期せぬ結果になるパターンを見つけられていませんが、「見つからない＝不具合がない」ということではないので、先の公式ページより信憑性の高い資料を提出しない限り却下です。\n\n# いいたいこと\n・とりあえず公式のページを参考に論理をたてられるようにしましょう\n　→**Qiita含めて個人サイト**より優先的に参考にしましょう。\n\n・もし、公式のページが読みづらくて、個人サイトとかを参考にするとしても、その個人サイトが公式を参考にしているかで信憑性を判断しましょう。\n\n・動けばいいってのはだめです。\n　→テストの原則で「欠陥がない」ことは示せないから、よくわからんけど動いたなんてのはレビューで通せるわけないです。\n\n・とりあえず公式ページ参照していれば、結果が間違っても責任回避できます。\n\n（補足）\n・Googleで調べて何故か公式より信憑性のないサイトが上にくる場合はGoogleの設定を英語設定にするとマシになるかもしれません\n　https://qiita.com/mima_ita/items/29e02c755062921e5e87\n\n　\n&quot;",
    td_title: "&quot;公式ドキュメントが読まれない こんな世の中じゃ ポイズン&quot;"
  })
  

```

Treasureはtd.min.jsにて実装されており、trackEventを実行することで、ユーザ名と表示している記事の情報を「[https://in.treasuredata.com/js/v3/event/qiita_public/pageviews_article](https://in.treasuredata.com/js/v3/event/qiita_public/pageviews_article)」に送信しているようです。

おそらく、これを基に特定のユーザが閲覧した記事の情報を集計していたと推測できます。


# 追跡から逃げてみる（Windows版）
1. メモ帳を管理者権限で開きます。
2. 「C:\Windows\System32\drivers\etc\hosts」を開きます。
3. ファイルの末尾に下記を追加します。

```
0.0.0.0 in.treasuredata.com
```

この作業を行ったあと、Chromeの開発者ツールで確認するとin.treasuredata.comへのアクセスが失敗していることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47856/0d274c61-0a72-2ae8-615e-c0b3cd2bedd2.png)

このURLを禁止にしてもとりあえず、Qiitaで投稿や閲覧は可能になっているようです。
しかし、当該APIを使用しているQiita以外のサイトで悪影響が出る可能性はあります。

なお、今後の閲覧した情報については防止できそうですが、当然、今までの情報については消えるわけではないので、無駄な努力かもしれません。

# 参考
https://github.com/treasure-data/td-js-sdk