2011年6月2日木曜日

今更ながら Zend_Gdata を試しました。その1「サンプルコードが動かない」編

こんにちは。
シナプスソフトのかわせ@おばグラマです。

私以外誰も更新しなくなった当ブログですが、気にせず好き勝手に投稿しますよ! ほぼ半年ぶりですがががが。

さて、タイトルの件。
今更ながら Zend_GData 試してみました。
検索してみても最近触ってる人少ないカンジです……。バージョン1.0が出てから5年くらい経つので当然かもしれません。

気を取り直して、ダウンロード&インストール!
特に何をしなくても、解凍して /library にパスを通せば動きます。

さっそくデモを試しましょう。
/demos にデモが入っています。WEBブラウザ越しでも、コマンドラインでも使えます、気が利いてるぅ!
今回はGoogleドキュメントの文書を扱いたかったのですが、こんな私にぴったりのAPIがあります。Google Documents List Data API です。Zend のリファレンスガイド内の Google Documents List Data API を見ると“Google Documents List Data API は、 クライアントアプリケーションから Google Documents にドキュメントをアップロードしたり、 ドキュメントの一覧を Google Data API ("GData") 形式のフィードで取得したりするためのものです。 クライアントアプリケーションからユーザのドキュメントの一覧をリクエストしたり、 ドキュメントの中身を問い合わせたりできます”とあります。
サンプルは下記を試します。今回CLI版を利用。
/demos/Zend/Gdata/Docs.php

$ php Docs.php retrieveAllDocuments ユーザネーム パスワード

さあ全ドキュメントの名前が表示されるはず……!

こんなの絶対おかしいよ

サンプルコードが期待どおり動かないと本当に萎えますorz
該当箇所のソールを見ると以下のようになっています。


function retrieveAllDocuments($client, $html)
{
if ($html) {echo "

Your documents

\n";}

$feed = $client->getDocumentListFeed();

printDocumentsFeed($feed, $html);
}

function printDocumentsFeed($feed, $html)
{
if ($html) {echo "
    \n";}

    // Iterate over the document entries in the feed and display each document's
    // title.
    foreach ($feed->entries as $entry) {

    if ($html) {
    // Find the URL of the HTML view of the document.
    $alternateLink = '';
    foreach ($entry->link as $link) {
    if ($link->getRel() === 'alternate') {
    $alternateLink = $link->getHref();
    }
    }
    // Make the title link to the document on docs.google.com.
    echo "
  • \n";
    }

    echo "$entry->title\n";

    if ($html) {echo "
  • \n";}
    }

    if ($html) {echo "
\n";}
}


Zend のリファレンスガイドを見る限りでは、上記であってそうなんすけどね……。
が、導入の「フィードやエントリ内のデータの操作」を見ると“各データモデルクラスのアクセス用メソッドを使用するか、 あるいはマジックメソッドを使用します”とあり、サンプルコードが掲載されています。こっちに直してみましょう。

function printDocumentsFeed($feed, $html)
{
// (省略)
// echo "$entry->title\n";
echo $entry->getTitle()->getText()."\n";
//または
echo $entry->title->text."\n";
// (省略)
}


無事表示されました。

2011年1月20日木曜日

ブラウザでは見えてるのに、wget で「unable to resolve host address 」

こんにちは。
シナプスソフトのかわせ@おばグラマです。

さてタイトルの件。目下困っている状態です。どなたかご教示ください^^;

PHP の file_get_contents() 関数や wget コマンドで、サブドメインの最後がハイフンで終わってるURLはエラーが発生してしまいます。

ちなみに file_get_contents() はWindowsでは成功します。うーん。


(追記)
教えてもらったところによると、ハイフン終わりはRFC的にNGみたいです。
http://ja.wikipedia.org/wiki/ホスト名


有効なホスト名の制限
ラベルの最初と最後の文字にハイフンを使うことは出来ない。

2010年12月20日月曜日

503 Service Unavailable: Back-end server is at capacity ってなによ!?

こんにちは。
先日、UNIXタイムスタンプがゼロの日時が「1970年1月1日午前9時」であるということに衝撃を受けたシナプスソフトのかわせ@おばグラマです。ずーっと「1970年1月1日午前0時」だと思ってました。なんでまた午前9時なんだ? ……そうです、時差なんですよ。

さてこの週末、のんびりすごしているところに、唐突にケータイにメールが飛んできやがりました。あ、この着信音は……!! そうです、サーバに障害が発生したときのアラートメールです。ホントこいつだけは何度受け取っても慣れるものではありません。

超テンパリながら、wget してみると……

503 Service Unavailable: Back-end server is at capacity

なんすかそれ!?
検索しても英語のページして出てこない! やーめーてー。


……で、まあ結果を言うと、こいつは Amazon EC2 のロードバランサー( ELB )による「振り分け先のサーバが死んでます。私が悪いんじゃないです」というメッセージでした。ちゃんちゃん。

2010年10月7日木曜日

DNSチェックサービスのXSS

こんにちはこんにちは!よしだです。
昨日、DNSの問い合わせ結果にJavaScriptを埋め込むというXSSが紹介されていたので、実験してみました。

Geekなぺーじ : メタ情報によるXSS
http://www.geekpage.jp/blog/?id=2010/10/6/1

このXSSは、DNSのチェックサービスなどでDNSの問い合わせ結果をエスケープせずにHTMLに出力する事でJavaScriptが発動してしまうというものでした。
仕組みは、DNSで可変長文字列を指定するパラメータにJavaScriptを埋め込むというもので、
MXレコード以外にもNSレコードやCNAMEレコードなどでも発生しそうです。
なぜかは知りませんが、 ( や " などはエスケープされるみたいなので、外部スクリプトを呼び出す方法以外はなさそうです。

ちなみに、Bindでは設定エラーとなり起動出来ませんでした。
(今回は、自力でバイナリを生成して返しています。)

キャプチャは元記事で使われていたサイトを使いましたが、日本語で検索してヒットするDNSチェックサービスでもあらかた発生しました。
実験に使ったdns-xss.d.xia.jpとdnsxss.d.xia.jpは、とりあえず登録しっぱなしにしておくので、ご自由にどうぞ。(アラートが出るだけで無害です)



JavaScriptが発動するパターン



# dig dns-xss.d.xia.jp. mx

; <<>> DiG 9.3.6-P1-RedHat-9.3.6-4.P1.el5_4.2 <<>> dns-xss.d.xia.jp. mx
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4753
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;dns-xss.d.xia.jp. IN MX

;; ANSWER SECTION:
dns-xss.d.xia.jp. 86400 IN MX 10 <script/src='http://g-storage.appspot.com/share/root/hello.js'></script>.

;; Query time: 300 msec
;; SERVER: 172.16.0.23#53(172.16.0.23)
;; WHEN: Thu Oct 7 17:51:10 2010
;; MSG SIZE rcvd: 122


JavaScriptが発動しないパターン



# dig dnsxss.d.xia.jp. mx

; <<>> DiG 9.3.6-P1-RedHat-9.3.6-4.P1.el5_4.2 <<>> dnsxss.d.xia.jp. mx
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10946
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;dnsxss.d.xia.jp. IN MX

;; ANSWER SECTION:
dnsxss.d.xia.jp. 86400 IN MX 10 <script>alert\(\"Hello\032DNS\032XSS!\"\)\;</script>.

;; Query time: 371 msec
;; SERVER: 172.16.0.23#53(172.16.0.23)
;; WHEN: Thu Oct 7 17:36:01 2010
;; MSG SIZE rcvd: 90

2010年9月29日水曜日

-1を3で割ったら余りはいくつ?

こんにちは。シナプスソフトのかわせ@おばグラマです。

案の定、社内の誰も更新しやがりませんねぇ。。。
この手のものは、強制力がないとやらないものなんですね、やっぱり。

さて、気を取り直して、今日の記事は「-1を3で割ったら余りはいくつ?」です。
ぐだぐだ言うより結果をみましょうか。

Mod.java

public class Mod {
public static void main(String args[]) {
int retValue = -1 % 3;
System.out.println(retValue);
}
}


クリックで拡大します。

結果は
PHP,JAVA :-1
python, perl, ruby:2

この「 % 」は剰余 ( modulo:モジュロ ) 演算子といいます。
負の数のときは挙動がプログラムによって違うのですね。
理屈はいろいろあるんでしょうが、子供の頃に「 3 x 0 + (-1) = -1 」と習ったので、結果が -1 と出てくれたほうがわかりやすいです。

2010年8月16日月曜日

Dropboxの仕様

こんにちは、シナプスソフトのプログラマよしだです。
Dropboxの裏側、セキュリティ部分について調べてみました。

C:\Documents and Settings\USERNAME\Application Data\Dropbox\host.db


1行目


16進数表記の文字列。
16進数表記で40バイト。
バイナリ表記で20バイト。

2行目


Base64エンコードされた、Dropbox Folder Location。

C:\Documents and Settings\USERNAME\Application Data\Dropbox\dropbox.db


sqlite形式のデータベースファイル。
テーブルは以下3個。

block_cache
config
file_journal


configテーブル


設定が保存されてる。
1行でKeyとValue。

CREATE TABLE config (
id INTEGER PRIMARY KEY,
key TEXT NOT NULL UNIQUE,
value TEXT
);


config.keyカラム


設定項目名。

sqlite> select id, key from config;
1|schema_version
2|last_revision
3|root_ns
4|host_id
5|email
6|ns_p2p_key_map
7|recently_changed3


config.valueカラム


PythonのPickleでシリアライズされたものをBase64でエンコードしてある。
デコードするPythonプログラム。

#!/usr/local/bin/python
# set encoding=utf8

import sys, base64, pickle

def main():
input = sys.argv[1]
input = base64.b64decode(input)
print pickle.loads(input)

def usage():
print 'Usage: %s encoded_str' % sys.argv[0]

if __name__=='__main__':
if (len(sys.argv)==2):
main()
else:
usage()


config.value (WHERE key='schema_version')


型 : int
値 : 6

config.value (WHERE key='last_revision')


型 : Dict
値 : {ルートNS: 見せられないlong}
ファイルを追加したりすると増えるのかもしれない。
リビジョンにしては数がでか過ぎる。
全ユーザ共通?とも考えたけど、コミットしても値は変わらない。

config.value (WHERE key='root_ns')


型 : long
いろんな所でキーとして使われている。
見せていいものか悪いものか解らないから、とりあえず「ルートNS」と表現する。

config.value (WHERE key='host_id')


型 : str
16進数表記で32バイト。
MD5っぽい。

config.value (WHERE key='email')


型 : str
メールアドレス。

config.value (WHERE key='ns_p2p_key_map')


型 : Dict
値 : {ルートNS: (u'-----BEGIN CERTIFICATE-----\n見せられない\n-----END CERTIFICATE-----\n', u'-----BEGIN RSA PRIVATE KEY-----\n見せられない\n-----END RSA PRIVATE KEY-----\n', '見せられない')}
1つ目のstrは、RSA公開鍵。
2つ目のstrは、RSA秘密鍵。
3つ目のstrは、パスフレーズ?初期ベクトル?
MD5っぽい。
詳細不明。

公開鍵と秘密鍵はペアになっていた。
この使い方、公開鍵暗号方式として間違ってる気がする。
このペア、サーバ側にも置いてあるはず。
こんな使い方をするなら、Diffie-Hellmanとかでその都度鍵交換してもいいような気がする。
けど、それだと1回の通信の為に2回接続しなきゃいけない。
多分そこが問題だったから、こういう奇妙な形になったんだと予想。

サーバにサーバ秘密鍵とクライアント公開鍵、クライアントにクライアント秘密鍵とサーバ公開鍵、を入れれば、RSAを使って1回の通信で更にセキュアになる。
サーバとクライアントでRSAキーを分けない理由は不明。
多分面倒だったんだと思う。
再発行出来なくなるし。

クライアントを再インストールしたら、このRSAキーが復活してた。
もしかしたら、サーバから送られてきてるのかもしれない。

ペアになっている事を確認するPHPのプログラム。

<?php
$public = '-----BEGIN CERTIFICATE-----
見せられない
-----END CERTIFICATE-----';

$private = '-----BEGIN RSA PRIVATE KEY-----
見せられない
-----END RSA PRIVATE KEY-----';

// 文字列からキーリソース生成
$public = openssl_pkey_get_public($public);
$private = openssl_pkey_get_private($private);

// 暗号化するデータ
$data = 'あいうえお!';

// public keyで暗号化
openssl_seal($data, $sealed_data, $env_keys, array($public));

// private keyで復号化
openssl_open($sealed_data, $open_data, $env_keys[0], $private);

echo $open_data."\n";


公開鍵の詳細。
どうやらこのキーはクライアント側で生成してるらしい。

$ openssl x509 -in public.crt -text
Certificate:
Data:
Version: 4 (0x3)
Serial Number: 1 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=dropbox-client
Validity
Not Before: Jan 1 00:00:00 1970 GMT
Not After : Jul 18 17:14:19 2030 GMT
Subject: CN=dropbox-client
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1536 bit)
Modulus (1536 bit):
見せられない
Exponent: 見せられない (見せられない)
Signature Algorithm: sha256WithRSAEncryption
見せられない
-----BEGIN CERTIFICATE-----
見せられない
-----END CERTIFICATE-----


$ openssl asn1parse -in public.crt
0:d=0 hl=4 l= 548 cons: SEQUENCE
4:d=1 hl=4 l= 333 cons: SEQUENCE
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :03
13:d=2 hl=2 l= 1 prim: INTEGER :01
16:d=2 hl=2 l= 13 cons: SEQUENCE
18:d=3 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
29:d=3 hl=2 l= 0 prim: NULL
31:d=2 hl=2 l= 25 cons: SEQUENCE
33:d=3 hl=2 l= 23 cons: SET
35:d=4 hl=2 l= 21 cons: SEQUENCE
37:d=5 hl=2 l= 3 prim: OBJECT :commonName
42:d=5 hl=2 l= 14 prim: PRINTABLESTRING :dropbox-client
58:d=2 hl=2 l= 30 cons: SEQUENCE
60:d=3 hl=2 l= 13 prim: UTCTIME :700101000000Z
75:d=3 hl=2 l= 13 prim: UTCTIME :300718171419Z
90:d=2 hl=2 l= 25 cons: SEQUENCE
92:d=3 hl=2 l= 23 cons: SET
94:d=4 hl=2 l= 21 cons: SEQUENCE
96:d=5 hl=2 l= 3 prim: OBJECT :commonName
101:d=5 hl=2 l= 14 prim: PRINTABLESTRING :dropbox-client
117:d=2 hl=3 l= 221 cons: SEQUENCE
120:d=3 hl=2 l= 13 cons: SEQUENCE
122:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
133:d=4 hl=2 l= 0 prim: NULL
135:d=3 hl=3 l= 203 prim: BIT STRING
341:d=1 hl=2 l= 13 cons: SEQUENCE
343:d=2 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
354:d=2 hl=2 l= 0 prim: NULL
356:d=1 hl=3 l= 193 prim: BIT STRING


config.value (WHERE key='recently_changed3')


型 : list
値 : [(u'ルートNS:/Public/How to use the Public folder.rtf', 0), (u'ルートNS:/Photos/Sample Album/Pensive Parakeet.jpg', 0), (u'ルートNS:/Photos/Sample Album/Costa Rican Frog.jpg', 0), (u'ルートNS:/Photos/Sample Album/Boston City Flow.jpg', 0), (u'ルートNS:/Photos/How to use the Photos folder.rtf', 0)]
file_journal.server_pathが5個入ってた。
keyの通り、最近更新されたファイル。
新しいものが先頭に追加されるらしい。
タプル2つ目はリビジョンかと思ったけど違うらしい。
ファイルを追加・更新したら0。
ファイルを削除したらNULL。

block_cacheテーブル


ファイルのキャッシュ(ログ)。
ファイルを追加・更新すると1行増える。
ファイルを削除しても、追加も削除もされない。

CREATE TABLE block_cache (
id INTEGER PRIMARY KEY,
hash VARCHAR(43) NOT NULL UNIQUE,
sig TEXT,
size INT,
delete_after INT,
needed_for INT
);


block_cache.hashカラム


43文字のASCII文字列。
file_journal.active_blocklistと一致。
Base64エンコードされた文字列かと思いきや、なんとなく違う。
多分プログラムがユニークになるように生成してる。

block_cache.sigカラム


Base64エンコードされた文字列。
ファイルのサイズに応じて長くなるらしい。
keyから察するに、署名だと思われるが、長さが違う気がする。
最初の16文字(バイナリ表記で12バイト)は全ての行で一致していた。

block_cache.sizeカラム


ファイルのサイズ。(バイト数)
コミット時、file_journal.active_sizeと一致。

block_cache.delete_afterカラム


全部0。
詳細不明。

block_cache.needed_forカラム


全部NULL。
詳細不明。

file_journalテーブル


ファイル一覧。
ファイルを追加すると、このテーブルに1行追加される。
ファイルを削除すると、このテーブルから物理削除される。

CREATE TABLE file_journal (
id INTEGER PRIMARY KEY,
server_path TEXT NOT NULL UNIQUE,
active_server_path TEXT,
active_blocklist TEXT,
active_mtime INT,
active_size INT,
active_sjid INT UNIQUE,
active_dir INT,
active_attrs TEXT,
updated_server_path TEXT,
updated_blocklist TEXT,
updated_mtime INT,
updated_size INT,
updated_sjid INT,
updated_dir INT,
updated_attrs TEXT,
on_disk TINYINT
);


file_journal.server_pathカラム


値 : ルートNS:/photos/how to use the photos folder.rtf
日本語はそのまま日本語で入っていた。

file_journal.active_server_pathカラム


file_journal.server_pathと一致。

file_journal.active_blocklistカラム


block_cache.hashと一致。
file_journal.server_pathがディレクトリの場合は空文字列。

file_journal.active_mtimeカラム


ファイルを最後に更新(コミット?)した時間。
Unixtime。

file_journal.active_sizeカラム


ファイルのサイズ。(バイト数)
最終更新のblock_cache.sizeと一致。
file_journal.active_dirが1の場合は0。

file_journal.active_sjidカラム


詳細不明。
UNIQUEのint。

file_journal.active_dirカラム


ディレクトリだったら1。
ファイルだったら0。

file_journal.active_attrsカラム


config.valueと同様、PickleでシリアライズされたものをBase64でエンコードしたもの。
デフォルトで入ってたファイルは{}で、自分で追加したファイルはNULLだった。
LinuxとかMacとかで使った時に、パーミッションとか属性とかが入るのかも知れない。

file_journal.updated_server_pathカラム


詳細不明。
全部NULLだった。

file_journal.updated_blocklistカラム


詳細不明。
全部NULLだった。

file_journal.updated_mtimeカラム


詳細不明。
全部NULLだった。

file_journal.updated_sizeカラム


詳細不明。
全部NULLだった。

file_journal.updated_sjidカラム


詳細不明。
全部NULLだった。

file_journal.updated_dirカラム


詳細不明。
全部NULLだった。

file_journal.updated_attrsカラム


詳細不明。
全部NULLだった。

file_journal.on_diskカラム


詳細不明。
全部1だった。

登録からセキュアな通信までの流れ(妄想)


登録


・クライアントソフトインストール。
・RSA生成。
・ユーザ情報・RSA登録。

入力するemail/passは人間(ブラウザ)認証用。
バックグラウンドで生成されるRSAはデータ暗号化用。

通信


・RSA公開鍵でデータ暗号化。
・送る。
・RSA秘密鍵でデータ復号化。

パケットを見る限り、443ポートとやりとりしてる。
暗号化したデータをHTTPSで流してる気がする。(2重で暗号化)
RSA-encryption over HTTP over SSL。
HTTP(アプリケーション層)の上に仮想的なレイヤー(Dropboxの独自仕様)を作ってその上で動かす。
レイヤーと言える程きっちりしたものではないかもしれないけど。
これは前に作ったOAuth Encryptionに似てる。

データ部分は2重で暗号化されているとして、何かユニークなIDを普通のHTTPSで見れる状態で送ってるはず。
考えられるユニークIDは、email位しかなさそう。

2010年8月9日月曜日

[PHP]関数にわたす引数の順番を調べる

こんにちは。
シナプスソフトのおばさんプログラマ、略しておばグラマのかわせです。
おばグラマとは言ってもプログラミング歴は社内で一番短い、超下っ端です。
この「SynapseLabo」は株式会社シナプスソフトのエンジニアによる様々なアウトプットの場として活用していきます。どうぞよろしく。

もっと高度な投稿は他のエンジニアにまかせて(ぉ、私は軽めの内容を投稿していきます。
で、タイトルの件。

PHPユーザのみなさん、関数にわたす引数の順番を覚えてますでしょうか? strpos() とか。ヘイスタック、ニードル?ニードル、ヘイスタックだっけ?おばグラマは記憶力もすっかり衰えてるので、全く覚えられません。
実は毎回マニュアルのお世話になっていましたが、 --rf オプションでコマンドラインから表示させる方法がわかったので便利に使っています。

$ php --rf strpos
Function [ function strpos ] {
- Parameters [3] {
Parameter #0 [ $haystack ]
Parameter #1 [ $needle ]
Parameter #2 [ $offset ]
}
}