火曜日, 3月 20, 2018

SNS時代のブログとは

SNS時代のブログとはみたいなクサい記事を書いてみたくなったんだよ。
beepcapなんだよ。

水曜日, 12月 20, 2017

邪悪なxonsh(の予定だったんだけどなぁ・・・)

Xonshを邪悪に使おうとしたが失敗した話から

beepcap

2017/12/20

はじめに

10日ぶりである。
この記事はXonsh Advent Calendar 2017の20日目だ。


Xonshに対する解説は他の人がたくさん書いているので、 この記事では邪悪な使い方を書こうと思っていた。 まず僕の用途だとXonshはとてもいいものだけど、 ちょっとまだ実用に至ってない。 zshのほうがちょっとだけ便利だ。 補間の学習具合や、historyに溜め込んだコマンドの記録が僕を拘束する。 historyを移植してやろうと思ったが、Xonshの履歴はちょっとばかし 扱いがめんどくさかったんでやめた。 そんなわけで気合が入らないなか、試してみたことの 記録を展開していこうと思う。 なお、この記事はXonsh 0.5.12をベースに書いている。 もしも改善されていたり、間違っていたらどんどん指摘してくれ。 Twitterの@beepcapで待ってるぞ。

最初の不満は

最初の不満はリダイレクトにあった。 Xonshはその性質上Pythonで書いた関数をシェル上で利用できるが、 これをシェルの中の人に渡すにはaliasを使わねばならず、 そうすると折角関数で作ったのに引数が使えずという苦しみが出てくるのだ。 もちろん、そこはこう・・・パラメータを与えてやればいい

$ echo 'hogehoge' | fuga > out.txt

・・・・・・ いやいや正気か? どう考えてもこうやりたい[*]

$ fuga('hogehoge') > out.txt

そこで考えた。 「aliasを使うから引数を付けられないのである」 「!そうだaliasなしでリダイレクト制御すればよくね!?」 Pythonのコードをこうすれば、>を再現することが出来る。

class fuga:
  gtxt = ""
  def __init__(self, in_t):
    fuga.gtxt = in_t
  def __gt__(self, other):
    f = open(other, "w")
    f.write(fuga.gtxt)
    f.close()

$ fuga("hogehoge") > "out.txt"

Oh!Yes!!!
ほんのちょっとばかし"が増えてしまったが、 おおよそ思い通りではないか?!・・・・・・ ・・・うん。そうなんだ。 Pythonは文字列を扱うときには""が必須で、これを外せなかったんだ・・・ 悲しい。 挫折した。 [*]aliasを使う方法に対するメリットがあるとすれば、渡されるオブジェクトが ファイル名でなくても良いという点だろうか。 例えば、


$ fuga("hogehoge") > "twitter"

こういう使い方も考えられるわけだ。 ツイートするのが簡単になる。 [*]
がまぁ、テキストの指定がダサいので失敗とする。

xonshの秘密をあかしてやろうとしたんだが・・・

xonshを今回いじるにあたって、自分に一つのルールを課した。 それは、「ソースコードを見ない」ということである。 我々は開発者であり、しかもxonshを喜んで扱うような人間であるからして Pythonには詳しい[*]。 その我々がソースコードを見ながらhackすれば、そりゃ何でも出来てしまうはずだ。 あくまでも、プロプライエタリ・ソフトウェアに対するように、 ユーザに開示されている情報のみで解析しなければ、面白くないだろう。

そんなわけでxonshをポチポチ遊んでいたのだが、 Twitterでばんくし(@vaaaaanquish)と会話している時に こんな発見があった。

Xonshは色々と強力だが、長くつけっぱなしで使用していると ゴミが多くなってくる。 これはzshなども同じはずなのだが、あいつらはそれが見えないので なんとなく気にされてない。 しかしXonshはPythonなので、dir()を呼べば一覧が出てくる。 もちろんdelで解放することも出来る・・・はずだった。


$ for i in dir():
    del i

このコードを実行してみてほしい。 バージョンによってコードは予想外の動きをする。 例えば私の手元の0.5.12では、「何も起こらない」。 意味が分かるだろうか? dir()内の要素が消せないのだ。 ところがどっこい、例えば


$ del __name__

では、ちゃんとname要素が消えてなくなるのである。 どうもbuiltinsを消そうとするとなにか強制力が働いて、 処理が中断されるようなのだ。 実際、dir()を使った解放コードはある時まで [*]Xonshの本体を巻き込んで死亡していた。

このbuiltins、何か秘密が隠れているに違いない。

調査のために、以下のように確認してみる。 [*]


$ dir(__builtins__)

ふむふむ、ちょっと中身は違うが標準的なbuiltinsの要素を含んでいそうだ・・・ オブジェクトの名前でも確認してやろうかな・・・


$ print(__builtins__.__name__)

AttributeError: 'dict' object has no attribute '__name__'

は?

え?いやいや、オブジェクトならそれが引けないって致命的でしょ・・・

ちょっとまて、あ、手が滑った


$ print(__builtins__)

'__name__': 'builtins', '__doc__': "Built-in functions,
 exceptions, and other objects.\n\nNoteworthy: None is the `nil' object;
 Ellipsis represents `...' in slices.", '__package__': ”, '__loader__':
 <class '_frozen_importlib.BuiltinImpor
...略
 

お、おい・・・お前これ・・・ dictじゃねーか!!!!!!!!!!!!!!!!!!!

そう、そういうことなのだ。 Xonshはbuiltinsの中身をまるごと置き換えていたのだ。 しかも、built-inモジュールではなく、辞書型のオブジェクトとして。 この中身は大変面白いので、みんなもぜひいじってみてほしい。 例えば、


$ __builtins__['testtxt'] = "test text."
$ testtxt
'test text.'

といった感じである。 aliasなどの定義もこの中にある。

が、時間不足でこの遊びも失敗した。 subprocessを利用している処理を一部置き換えることには成功したのだが、 まだ、根幹の定義済み命令を置き換えると、xonshがハングアップしてしまう。 [*]これでは大して悪いことに使えない。

ということで、失敗である。

イカロスの翼

神話の時代にイカロスは翼を作って飛び立ったが、 太陽の熱に焼かれて翼が壊れ、落下して死んだそうだ。 あれから得られる教訓は、「パワーが足りない」である。 なぜロケット推進にしなかったのか。

そんなわけで、xonshでも処理するにパワーが足りない時がある。 パワーといえばマルチプロセスだ!!! しかし、bashやzshのように、pipeでつなげばプロセスが起きたり、 xargsを使った今流行りのhackも使えない。 Pythonにはmultiprocessing()があるが、 CPythonがベースのxonshではGILがある。 性能への影響がどの程度とかそういうの関係なく嫌だ。 Cとの結合をしようとしたらGILが働く設計にしとけばよかったじゃんか [*]

ということで、os.fork()を使うことにした。

fork()を使用したマルチプロセス化の問題点として fork()は子と親でメモリ以外の資源を共有する、という点が挙げられる。 例えばxonshで


$ import os
$ os.fork()

とやると、とんでもないことになる。 入出力が多重化し、まともに入力を受け付けなくなるだけではなく、 表示も二重に出て、有り体に言えばバグる。 [*]そして、親プロセスを終わらせたが最後、ゾンビと化した子供は エラーを吐き続けターミナルを埋め尽くすのである。

何が悪いのか。 xonshはターミナルの上で動くCUIプログラムなので、 ターミナルソフトとの通信には当然、stdin, stdoutを利用している。 fork()するということは、これらを複数のプロセスで共有してしまうことになる。

ここで、経験の多い人はこう思うだろう。

「stdinとstdoutを閉じればいいんだよ馬鹿だなぁ」

やってみよう。


$ import sys
$ sys.stdin.close()

どうなっただろうか。 おそらく例外が発生して/bin/shへfallbackされたのではないだろうか? コードを見ないルールなので、これは予測だが、 トレースバックを見るにどうやらstdinに対してxonshはポーリングを掛けており、 これが閉じられてしまうケースを想定していないようなのだ。 Oh...じゃあfork()しても、stdinをclose()した段階で、 プロセス落ちちゃうじゃん。

ところが、ここに光明が見える。


$ import sys
$ sys.stdout.close()

どうなっただろうか? 何も起こらない? ではこうして見てほしい。


$ import sys
$ sys.stdout

お分かりだろうか。 そう、このstdoutは_ioに属していないのだ。 stdoutが別のclassに差し替えられているということは、 同じことをstdinにも施してやればいい。 無害なインタフェースクラスを作り、stdoutとstdinを差し替えてやればオッ!

・・・

ところが、そうは行かなかったのである・・・。

解析中にトレースバックで見たのだが・・・


$ import os
$ os.close(1)

これ[*]をやって出てくる トレースバックに、 あろうことか


  File "/usr/lib/python3/dist-packages/xonsh/__amalgam__.py",
  line 15626, in settitle
      with open(1, 'wb', closefd=False) as f:

fuxx!!! オイオイオイオイ、その使い方は無いだろオイオイ!

stdoutをですね!ダイレクトにですね! 叩いてるコードがいるんですね!!!

俺の言えた義理じゃないが邪悪すぎやしませんかね?

ということで、いまだに俺はXonshを使ってfork()をうまく使うことが出来ない。 だれか助けてくれ。

最後に

新たなおもちゃを手に入れたと思って遊んでいたが、成果だけが出ていなかった。 皆さまはこのような悪い大人になってはいけませんよ?

それではまた。

About this document ...

Xonshを邪悪に使おうとしたが失敗した話から

This document was generated using the LaTeX2HTML translator Version 2017.2 (Released Jan 23, 2017)

Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The command line arguments were:
latex2html -split 0 xonsh.tex

The translation was initiated on 2017-12-20


Footnotes

... どう考えてもこうやりたい[*]
これ実は出来るよって情報求む
... 挫折した。[*]
これも出来るなら教えてほしいと思うが・・・いやこれは無理やろ
... ツイートするのが簡単になる。[*]
ほんとか?tweet()みたいな関数用意しとけばいいだけじゃね?
... Pythonには詳しい[*]
そんな気がする
... 実際、dir()を使った解放コードはある時まで[*]
バージョン控えておかなかったんだよなぁ
...調査のために、以下のように確認してみる。 [*]
Pythonにおいては未知のオブジェクトを調べる標準的な手法だ
... まだ、根幹の定義済み命令を置き換えると、xonshがハングアップしてしまう。[*]
なにやら新しいバージョンで試したら、今度はハングアップしなくなっているのかな?これは期待が持てる
... Cとの結合をしようとしたらGILが働く設計にしとけばよかったじゃんか[*]
愚痴
... 表示も二重に出て、有り体に言えばバグる。[*]
xonshのバグではないと思うけど
...これ[*]
stdoutを無理やり止めようとしたのだ

日曜日, 12月 10, 2017

web スクレイピング隠蔽技術

web スクレイピング隠蔽技術

beepcap

2017/12/10

はじめに

なんか気づいたらアドベントカレンダーに登録してた。 webスクレイピングの10日目だ。 beepcapである。


webスクレイピング... 最近はwebスクレイピングなどとかっこいい名前が付いているが、 昔はwebクローラといった気がする。厳密な定義がどうかは分からんのだが、 軽く調べてみても両者を明確に区別する文言は見つからない。 本記事ではwebクローラと言うことにする。[*]

さて、古くからwebを扱っている人間にとって、 webクローラというのは親の敵のような存在だ。 ネットワークが従量課金性であった時代では、 アップロードとダウンロード双方に回線料が発生するのだ。 クローラにアクセスされた分の料金を支払わねばならない個人サイトの運営者は とにかくクローラを排除する必要に迫られた。

その検出手法と攻防の推移を紹介しながら話を進めよう。

基本編

初歩的なクロール対策

「初歩的な」と書いたが、一般的なwebクローラの攻防は大体これだ。 [*]まずさくっとwebクロールしてみよう。 きっと今回のACの他の記事にwgetによるクロールの話が出てくると思うので 仔細は語らないが、


$ wget -r -l inf -k -nc -E -np http://targ.et

といったところだろうか。 上記は最速で取得しようとするので、 httpdのアクセスログは単一のIPアドレスでうめつくされる。 管理者側からはバレバレだ。

この行為への対策はIPアドレスフィルタリングとなる。 自動化して一定時間のアクセスを禁止するものから、[*]


$ iptables -A INPUT -p tcp -m hashlimit -dport 80
  -hashlimit-name no_robot -hashlimit 5/s -hashimit-burst 3
  -hashlimit-mode srcip -j ACCEPT
$ iptables -A INPUT -dport 80 -j DROP

やらかしたら二度とアクセスを許さないものまで多種多様だが、


$ iptables -A INPUT -s xx.xx.xx.xx/32 -j DROP

対webクローラの手法としてIPアドレスフィルタリングは良く使われていた。 90年代〜2000年代初頭にwebページを運営していた仲間内では 「クロール元のIPリスト」なるものがやりとりされていたりもしたのだ。 こうなってしまっては満足に収集も出来ない。 [*]そこで、次のような対策が打たれた。

初期の対策の対策

これもwgetの例でいいだろう。


$ wget -r -l inf -k -nc -E -np -w 1 http://targ.et

このコードは1秒に1回のアクセスに取得を制限するものだ。 一般的なクローラの動作といっていい。 現代の普通のサイトではクローラを拒否する理由なんて 負荷くらいのものだから、この対策で十分に効果があるし、 目を付けられてブロックされる恐れも無い。
世の中の大半が幸せなら、それ以上の技術はいらない。


などという甘い考え方が通用しない人たちもいる。

例えばセキュリティ技術者のようにログを監視している人々にとっては、 こんなふうにこれらのアクセスが見える。


[水 12月  6 08:30:03 2017] Inbound_tcp_s SRC=xxx.xxx.xxx.xxx DST= LEN=40
 TOS=0x00 PREC=0x00 TTL=238 ID=45566 PROTO=TCP SPT=43146 DPT=80
 WINDOW=1024 RES=0x00 SYN URGP=0
[水 12月  6 08:30:04 2017] Inbound_tcp_s SRC=xxx.xxx.xxx.xxx DST= LEN=40
 TOS=0x00 PREC=0x00 TTL=238 ID=45567 PROTO=TCP SPT=6666 DPT=80
 WINDOW=1024 RES=0x00 SYN URGP=0
[水 12月  6 08:30:05 2017] Inbound_tcp_s SRC=xxx.xxx.xxx.xxx DST= LEN=40
 TOS=0x00 PREC=0x00 TTL=238 ID=45568 PROTO=TCP SPT=22012 DPT=80
 WINDOW=1024 RES=0x00 SYN URGP=0
[水 12月  6 08:30:06 2017] Inbound_tcp_s SRC=xxx.xxx.xxx.xxx DST= LEN=40
 TOS=0x00 PREC=0x00 TTL=238 ID=45569 PROTO=TCP SPT=30153 DPT=80
 WINDOW=1024 RES=0x00 SYN URGP=0
.
.
.

webクローラであることがバレバレだ。 webクローリングを阻止したい人間がいれば、排除されてそれで終わりとなる。

そこで、ここからが本題だ。 如何にしてwebクロールを隠蔽するのか。

アクセスのタイミングの隠蔽

人間が操作しているかのようなログを残せば、機械だと見破られなければ 如何に優秀な管理者が相手であっても、これをブロックして仕舞うことは難しい。

例えばこれをランダム化によって回避しようとする手法がある。 wgetで言えば-random-waitという手法である。 これを行うとアクセスの待ち時間がランダム化し、 パッと見では分からなく・・・ならない。普通に怪しい。 ただし、自動解析ツールを使って監視している場合は検出されなくなることがある。 周期性をキーに監視しているツールが存在するからだ。 それでも、まぁアクセスが多くなるので、 人間に監視を促すよう警告が出る場合がある。そうしたらお陀仏だ。

ランダムではなく、人のような動きをクローラにはさせてやらねばならない。

とここまで書いたが、残り締め切りまで一時間しか無いので、 巻いていこう。タイミング隠蔽を含めて、クロールが発覚しにくい手法の肝要な部分は3つある。

人の動きを模倣するには2つの点で気をつけねばならない。 一つはアクセスする頻度だ。 先程までの話から展開させると、つまり人は文書を読んでから次のリンクをクリックするので、文書上の距離を計測すれば適切な待ち時間を算出できる。 この方法は、レンダリング後の画像を使う確実な方法と、マークアップと文を含めた文字数で算出する方法があるが、 評価し確認してみた所だと、文字数だけでもかなり精度が高い。 これはマークアップという書式の特徴だと思われる。 ただし、画像の中に文書があるような形態の場合はやはり画面上の距離を推定する必要がある。 どの程度の精度と成果をバランス良く摂るのかを検討して設計して欲しい。

アクセス元の偽装

言うまでもないが、アクセス元を偽装しなければ、同じサイトに毎日現れる不審なログに気付かれる可能性がある。 とはいえ、アクセスのたびにProxyを切り替えるような手法はダメだ。 SYNのたびにProxyが切り替わるログは完全に怪しさしかないし、まともなサイトの管理者は、むしろパブリックなProxyを弾くように設定している。 ここで大事なのは、企業などからアクセスしているように見せかけることだ。 この場合、仮に頻度がある程度高くても、同じページを一斉に参照している可能性も考慮されるからだ。

ということで、この場合も最適解だけ書くと、 安価なクラウドサービスをレンタルし、自前のProxyサーバを立てること。 そして、一日以上、短くても数時間以上の長いスパンでProxyの切り替えをすることだ。 同じサイトに同じ人間がいることはおかしくない。 という心理上の正しさを追求すべきである。

クロールする時間

最後の項目だが、これは簡単だ。 24時間ページを見続けられる人間は居ない。 あなたがクロールする時、それをアルバイトに任せることを考えて、稼働のシナリオを考えるのだ。webクローラとは隠密の世界なのである。

最後に

最後に、これらのいかがわしいwebクローラのコードを貼っ付けるのと、 Apache ManifoldCFとApache Solrによって構築するwebクローリングシステムの運用方法に言及しようと思っていたのだが。 時間切れでなんにも出来てません。本当に申し訳ない。 これらの話を聞きたい方は、Twitterで直接話しかけて欲しい。 @beepcapとしていつでもリプライを待っているぞ。

以上

About this document ...

web スクレイピング隠蔽技術

This document was generated using the LaTeX2HTML translator Version 2017.2 (Released Jan 23, 2017)

Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The command line arguments were:
latex2html -split 0 webスクレイピング.tex

The translation was initiated on 2017-12-10


Footnotes

... 本記事ではwebクローラと言うことにする。[*]
そっちの方が短いし
... 「初歩的な」と書いたが、一般的なwebクローラの攻防は大体これだ。[*]
煽ってるんじゃないよ?それで十分なのさ。
... 自動化して一定時間のアクセスを禁止するものから、[*]
同じIPから port 80への3回以上のアクセスは1秒5回まで
... こうなってしまっては満足に収集も出来ない。[*]
リトライをちゃんと仕込んでおけば前者は回避できるが、 後者をされる確率が上がる

水曜日, 5月 17, 2017

愚痴

また標準テーマが気に入らなくてカスタムテーマを作ってしまった。
こうやってカスタムするから、引き継ぎができなくて、
毎回標準テーマが更新されるたびに泣く泣く時間をかけてカスタムする羽目になるというのに。

火曜日, 5月 16, 2017

cgroupを利用して処理ごとに資源を割り振る話


仕事でcgroupを使用したくなってちょっと調べたのですが、
Googleで調べると、昔こんな記事を書いたことが発覚しました。

当時は動いたのだでしょうが、現代では間違いだらけの動かないポンポコピーなので、
書き直すことにします。
なお当該のパッチは現在でも形を変えて(当時ほど率直ではないものの)残ってはいるので、この対応は全くの無駄です

でもgoogleでcgroupの使いかたの日本語記事でこのblogが引っかかるので、訂正しておかなければならないですよね・・・。





日曜日, 2月 12, 2017

なんだかTwitterにプロモーションツイート課金してみた。

どうも。
beepcapです。

最近この喋り出しがyoutuberっぽいって言われました。
そりゃああっちがネットラジオからパクってった文化ですからね!

youtubeできるより前からラジオしてたっての。
もうやってないけど。

さてさて、本題ですが、
Twitterで課金してみました。


土曜日, 12月 17, 2016

SO-FI


この記事はロックバンド Advent Calendar 17日目です。

はい皆様お久しぶりです。
beepcapです。

ばんくしのアドベントカレンダーに合わせて一年ぶりに記事を書いています。
正直眠いです。



自己紹介

自分の写真
NetRadioDJ ...since 2003, Programer ...since 1994