JSON-RPCパスワード
JSON-RPCにパスワードを追加する変更をSVNにアップロードした。ビルド環境がある方はテストしてほしい。
-serverスイッチは -rpcpw=<password> に置き換えられ、bitcoindでも使用される。bitcoin -rpcpw=<password> — JSON-RPCポートを開いて実行bitcoind -rpcpw=<password> — パスワード付きデーモン
スイッチ名のより良いアイデアがあれば知らせてほしい。ただし、データベースの暗号化用のパスワードもいずれ必要になることを念頭に置いてほしい。確信はないが、2つのパスワードを別々に使いたい人もいるかもしれない。
パスワードを設定しないと警告が表示される。
すべてのコマンドで最初のパラメーターとしてパスワードが必要になった。bitcoind helpを実行するとその旨が表示される。
中核コード:
// パスワード確認
if (params.size() < 1 || params[0].type() != str_type)
throw runtime_error("First parameter must be the password.");
if (params[0].get_str() != strRPCPassword)
{
if (strRPCPassword.size() < 15)
Sleep(50);
begin = strRequest.end();
printf("ThreadRPCServer incorrect password attempt
");
throw runtime_error("Incorrect password.");
}
これらの判断について、何かコメントはある?
-
if (strRPCPassword.size() < 15) Sleep(50); — これは短いパスワードの場合、各試行後に50ms待機することを意味する。これはDoS攻撃に利用される可能性があるが、短いパスワードの場合はブルートフォースのパスワードスキャンから保護する方が重要だと判断した。これにより外部の人にパスワードが15文字未満かどうかがわかってしまう可能性があるが、15文字未満はそれほど注目すべきことではなく、ほとんどのパスワードは15文字未満だ。DoSの可能性を閉じたい場合は、15文字以上のパスワードを使用してほしい。
-
begin = strRequest.end(); — 複数の呼び出しを含む単一のリクエストの場合、1つが不正なパスワードであれば残りを破棄する。これは1つのパケットに何百万ものパスワード試行を詰め込めないようにするためだ。これは正しい判断だと思う? (複数呼び出しはほとんど使われないと思うが)
ヘルプに重複して表示されていた2つのコマンドも修正した:
getaddressesbylabel <pw> <label> getbalance <pw> getblockcount <pw> getblocknumber <pw> getconnectioncount <pw> getdifficulty <pw> getgenerate <pw> getinfo <pw> getlabel <pw> <bitcoinaddress> getnewaddress <pw> [label] getreceivedbyaddress <pw> <bitcoinaddress> [minconf=1] getreceivedbylabel <pw> <label> [minconf=1] help <pw> listreceivedbyaddress <pw> [minconf=1] [includeempty=false] listreceivedbylabel <pw> [minconf=1] [includeempty=false] sendtoaddress <pw> <bitcoinaddress> <amount> [comment] [comment-to] setgenerate <pw> <generate> [genproclimit] setlabel <pw> <bitcoinaddress> <label> stop <pw>
リモートで行う分にはまあいいが、同じUnixマシン上の他のユーザーからビットコインを盗まれることが心配なら、これでも解決しない。/proc、top、psなどでコマンドラインが見えるからだ。少なくともそれを防ぐために、stdinでパスワードを読むか、readlineか何かを使うこともできるはずだ。コマンドラインで渡せるようにするのは良くないと思う。
さらに良いのは鍵ファイルを使うことだろう。そうすればUnixのパーミッションでそのユーザーだけが読めるようにできる、sshみたいに……bitcoindにauthorized_keysファイルを持たせ、公開鍵を置けばいい。ともかく、きつい言い方をするつもりはないんだけど、コマンドラインでのやり方はただの偽りの安心感だよ。
確かに、それはかなり良い。
そのようにやっている他のソフトの例を教えてもらえないだろうか? (コマンドラインがどんな形か)
ここで主に話しているのは、bitcoindを起動する際に -rpcpw= を使う代わりに、テキストファイルを指定してそこから読み取るスイッチを使うということだよね? (スイッチの名前に、何かいい案はある?)
Bitcoin Faucet(本番とTEST)はこの変更を適用して稼働中だ。
パスワードがコマンドラインでは一番最後に指定するのに、JSON-RPCのパラメーターリストでは一番最初になるので少し混乱した。コマンドラインのパスワードをファイルから読み取る方が便利で安全だという意見には同意する。
他のプロジェクトがJSON-RPC認証にどう取り組んでいるか調査してみるつもりだ。
TransmissionのBitTorrentクライアントは認証付きJSON-RPCを実装している。「Remote Control」セクションを参照:https://trac.transmissionbt.com/wiki/ConfigurationParameters
例えば、setting.jsonファイルは次のようになる:
{
"rpc-enabled":1
"rpc-authentication-required": 1,
"rpc-password": "xxxxxxxxxx",
"rpc-port": 9091,
"rpc-username": "xxxxxxxxxx",
"rpc-whitelist-enabled":1
"rpc-whitelist":"127.0.0.1,192.168.*.*"
}
HTTPの「basic」認証(HTTPヘッダーにAuthorization: basic base64(username:password))を使用している。 さらに調査した結果……Transmissionのアプローチと、既存の「127.0.0.1からの接続のみ許可」を組み合わせるのが、短期・中期的には良い解決策だと思う。
Bitcoinディレクトリ内のsettings.jsonファイルにusername:passwordを置く方式はうまく機能するはずだ(BitcoinはすでにJSONをパースできるので)。認証情報をコマンドラインに置かず、JSONリクエストパラメーターの代わりにHTTPヘッダーに含めるのは、すっきりしていて良い。
長期的には、認証付きセキュアJSON-RPCの「正しい」方法はクライアント側証明書とhttpsだ。しかし、実装には多大な作業が必要で、ユーザーがクライアント側証明書の生成方法やJSON-RPC接続の両端での使用方法を理解するのは大きな学習曲線となる。そして、本格的なクライアント証明書ソリューションが、悪意のあるJavascriptがXMLHttpRequestsでlocalhostにJSON-RPCリクエストを送る問題を解決するかどうかさえ確信が持てない。もしユーザーがブラウザーにクライアント証明書をインストールしたら(Bitcoinを操作するためのJSON-RPCベースのWebフロントエンドがあったとして)、悪意のあるウェブサイトがリクエストを送った時にブラウザーは自動的にクライアント証明書を送信するだろうか?
~/.bitcoinディレクトリに設定ファイルを置くということだな、それは良さそうだ。「パスワードが設定されていない」の警告で、ファイルの場所と何をすべきかを伝えることができる。
最も普及していて一般的な設定ファイル形式は何だろうか?
HTTPベーシック認証を検討すべきだ。ただし実際には、HTTPやJSON-RPCのラッパーの追加パラメーターを通じてパスワードを指定する方法を理解するのは、パラメーターリストの先頭に追加パラメーターを付けるよりもウェブ開発者にとって手間がかかる。どう思う? HTTPベーシック認証には追加のメリットはある? パラメーターリストから移動しても、より難解な場所で指定する必要があるのでは、純粋な改善とは言えないかもしれない。
GAギャビン・アンドレセンの投稿(2010年7月19日12:02 UTC)パスワードがコマンドラインでは一番最後に指定するのに、JSON-RPCのパラメーターリストでは一番最初になるので少し混乱した。コマンドラインのパスワードをファイルから読み取る方が便利で安全だという意見には同意する。
あなたにも混乱させられている。どういう意味? 私が何か意図しない動作をしてしまった?
~/.bitcoinディレクトリに設定ファイルを置くということだな、それは良さそうだ。「パスワードが設定されていない」の警告で、ファイルの場所と何をすべきかを伝えることができる。 最も普及していて一般的な設定ファイル形式は何だろうか?
難しい質問だ! 最も一般的:おそらくWindows INIファイルだろう。WindowsがOSとして最も普及しているから。
JSONを推したい。JSONは(ほぼ)YAMLのサブセット(YAMLは設定ファイルの一般的な選択肢)なので、JSONまたはYAMLパーサーで読める。 大きな利点は、認証を本来あるべきトランスポート層に置けることだけだと思う。そうすれば将来、本当に本格的なHTTPSと証明書を導入する場合にAPIを変更する必要がない。 いや、単に「command」と「parameter」を混同して、こうしただけだ:
> bitcoind help
error: First parameter must be the password.
> bitcoind <my password> help
error: unknown command: <my password>
>bitcoind help <my password>
... that works. bitcoindはもっとシステム全体のデーモンであるべきだろうか? PAM認証で、実行ユーザーだけでなくすべてのシステムユーザーをサポートする。 例えば/home/*/.bitcoinフォルダーのウォレットファイルをスキャンして、ユーザー名とシステムパスワードの両方でアクセスを提供するとか……
自分で書いた別のJSON-RPCクライアントを使うならパスワードの保護に気をつけられるが、bitcoinバイナリをクライアントとして使ってコマンドラインでパスワードを渡すのは、デーモン起動時と同じ問題がある。どちらの方法でもすべてのユーザーに見えてしまう。
だからサーバーモードとクライアントモードの両方の起動でファイルを使い、コマンドラインでパスワードを受け付けないようにする必要がある。一般的にこの種のプログラムは、ファイルのパーミッションが600でなければ起動を拒否する。他のユーザーが読めることを意味するからだ。
Linuxで最も一般的な設定ファイル形式が何かまだ知りたい。標準のファイル拡張子はある? JSONを使った設定ファイルは見たことがなく、すべてをクォートで囲む必要があり、人間にとってあまり親切には見えない。私がよく見るのはこのような形式だ:
# コメント
setting=value
Boostに設定ファイルを扱うものはある?
コマンドラインからクライアントとしてbitcoindを使ってコマンドを発行する場合、その時も設定ファイルからパスワードを取得できるようにできないかな?
ギャビンに指摘されたが、CommandLineRPCの数字の列をインクリメントするのを忘れていたため、現在の -rpcpw= の実装はコマンドラインからの文字列以外のパラメーターでは正しく動作しない。(JSON-RPCは問題ない)まだ開発中だ。
設定ファイル形式を調査していた。比較を以下に示す。
YAMLは巨大だ。プロジェクトに統合できる軽量で簡単にビルドできるライブラリがあるかどうかわからない。過剰に思える。
JSONは魅力的で好む傾向にあるが、2つの主要な問題点がある:
- コメントがない! 行をコメントアウトして無効にできない設定ファイルなんてどうやって使うのか?
- キーを含むすべての文字列を”クォート”で囲む必要があり、行末のカンマも忘れずに付けなければならないのは、あまりユーザーフレンドリーではない。
{
"key" : "value",
}
設定ファイルを1行ずつ読み込んで、#文字(および/または”//”?)の位置で行を切り詰め、文字列に連結してJSONに渡すことで、簡単にJSONを前処理できるだろう:
# コメント
"key" : "value", # カンマは忘れずに
"key2" : "value", // このようなコメントや両方
Boostにはboost::program_optionsがある。
行を自分で読み取ってmap<string, string> mapConfigに入れることもできる。
while (!eof)
行を読み込む
'#'が見つかったら行を切り詰める
最初の':'で行を分割 -> key, value
mapConfig.insert(key, value)
構文として:
# コメント
key : value
を使い、キーの前に空白インデントを許可しなければ、YAMLのサブセットとなり、より複雑な機能が必要になった時にYAMLに切り替えることができるだろう。
自前パースにした場合でも、必要に応じて特定のパラメーター値にJSONを使用できないわけではない。オプションがリストやより構造化されたデータを必要とする場合、その値をJSONとしてパースできる:
key : ["item1", "item2", "item3"]
ただし、すべて1行に収める必要がある。
自前パースのmapConfigに傾いている:
# コメント
key : value 最もシンプルなLinux設定ファイルは通常、以下のようなフォーマットだ:
# how often (in minutes) data is saved when all interface are offline
OfflineSaveInterval 30
# force data save when interface status changes (1 = enabled, 0 = disabled)
SaveOnStatusChange 1
# file used for logging if UseLogging is set to 1
LogFile "/var/log/vnstat.log"
# file used as daemon pid / lock file
PidFile "/var/run/vnstat.pid"
標準の拡張子は、あるとすれば.confだ。ハッシュ(#)が圧倒的に最も一般的なコメント文字だ。セミコロンやCスタイルのコメントも見かける。
ウォレットの一部ではない、人間が操作しやすい設定ファイルがあれば大きな前進だと思う。現在コマンドラインで指定されている多くのオプション(例えばnoircや -minimizetotray)は、設定ファイルで指定した方が良いかもしれない。
Linuxには「典型的な」設定ファイルというものはないと思う!
自分のDebianシステムの/etcにある20個の.confファイルを手短に調べてみたところ:1ファイルは”key value”を使用5ファイルは”key=value”を使用(実際にはいくつかは”key = value”で、”=“の前後にスペースを許容)14ファイルは独自の方式
独自の方式の14ファイルは実に様々で、1行1値から”key:value”、本格的なXMLまであった。#はLinux世界における共通のコメント文字だ。
私の一票は:
# comment
key1=value1 自分のDebianシステムの/etcにある20個の.confファイルを手短に調べてみたところ:1ファイルが”key value”を使用5ファイルが”key=value”を使用
調査ありがとう!
「key value」は少し不自然に感じる。キーと値の間に、代入を示唆するより明確な区切りがあるべきだ。スペースを使う人は、自分の言語のsplit関数を使って手を抜いているだけかもしれない。
key=some full sentence with spaces in it. # こちらの方がより明確
key some full sentence with spaces in it. # これよりも
それでは、自前パースのmapConfigで行こう。構文:
# コメント
key=value
ファイル拡張子は.conf。ファイル名は~/.bitcoin/settings.conf? ~/.bitcoin/bitcoin.conf? それとも別のもの?
キーと値の先頭と末尾の空白を除去した方が良いと思う。
# カラム整形が好きなユーザー
k = value
key = value
longerkey = this sentence would be this # "this sentence would be this"
key = value # これも大丈夫だろう
nextkey = value
right = justified
通常の構文はkey=valueにすべきだが、時々key = valueにする人を責めることはできない。
boost::program_optionsは同じkey=value形式を持っている。ギャビンが指摘したように、型付き値抽出のような難解なC++構文に踏み込むことなく、シンプルなパーサーとして使用できる。後で必要に応じてより多くの機能を使うことができる。
パラメーターとしてのパスワードではなく、HTTPベーシック認証で進めよう。
この実装を自分から引き受けて、今日かなり進捗があった。サトシ:明日にはパッチを送れるはずだ。
完了:Bitcoinが{BITCOIN_DIR}/bitcoin.confファイルから設定を読み込めるようにした。-conf=path_to_config_file.confコマンドラインオプションも追加した。
完了:Bitcoin RPCにHTTP Basic認証を要求させ、ユーザー名・パスワードが間違っているリクエストを拒否するようにした。
TODO:BitcoinのコマンドラインからAuthorization:ヘッダーを追加するようにする。コマンドラインからBitcoinを操作する際にユーザー名・パスワードを入力する必要はなく、bitcoin.confから読み取って、ちゃんとうまくやってくれる。TODO:rpc.user/rpc.passwordが設定されていない場合、ダイアログボックスまたはdebug.logで設定方法を説明する警告を表示する。TODO:rpc.passwordが15文字未満の場合、パスワード推測の試行を制限する。TODO:JSON-RPCのWikiページを更新する。
以上すべてが完了してサトシにパッチを送った後、bitcoin.confにいくつか追加する予定だ:
port= # listenポートの設定(デフォルト8333を上書き)rpc.port= # JSON-RPCポートの設定(デフォルト8332を上書き)
既存の -datadirオプションと合わせれば、1台のマシンで複数のBitcoinを実行しやすくなる。
素晴らしいよ、ギャビン。このスレッドで設定ファイルのオプション案のリストを作り始めるべきだ。
まず、noirc(デフォルトオフ)オプションとminimizetotrayオプションを提案する。どちらも同名のコマンドラインスイッチと同じことをする。毎回 -minimizetotrayを付けてBitcoinを起動するハック的なやり方をやめられる。あと、-serverと -daemonにも同様のオプションがあるべきでは?
すべてのコマンドラインオプションをbitcoin.confファイルでも指定できるように実装した。
コマンドラインで指定されたオプションはconfファイルのオプションを上書きする。ただし、特にaddnodeのような「複数引数」オプションについてはもっとテストが必要だ。
TODO:rpc.user/rpc.passwordが設定されていない場合、ダイアログボックスまたはdebug.logで設定方法を説明する警告を表示する。
このRPC関連の多くのコンテキストでは、fprintf(stdoutでコンソールに出力できる。このように:
#if defined(__WXMSW__) && wxUSE_GUI
MyMessageBox("Warning: rpc password is blank, use -rpcpw=<password>
", "Bitcoin", wxOK | wxICON_EXCLAMATION);
#else
fprintf(stdout, "Warning: rpc password is blank, use -rpcpw=<password>
");
#endif RPC wikiページを更新して、Bitcoin 0.3.3でのパスワード機能の仕組みを説明した。
1つの良い副作用:今すぐ変更に備えることができる。bitcoin.confファイルにユーザー名とパスワードを作成し、JSON-RPCコードをHTTP Basic認証に対応するよう修正すればいい。古いコードは.confファイルとAuthorization: HTTPヘッダーを単に無視する。
皆に質問:HTTP Basic認証の詳細な方法をwikiページに追加すべきだろうか? PHPとPythonでは非常に簡単だ——http://user:pass@host:port/ のURL構文を使うだけだ。HTTP Basic認証のWikipediaページをただ複製したくはない。
皆に質問だ:HTTP Basic認証のやり方を詳しく説明するセクションをwikiページに追加すべきだろうか? PHPとPythonでは
http://user:pass@host:port/というURL構文を使うだけで非常に簡単にできる。
はい、各開発者が自分で調べなくて済むように、それは本当に良いと思う。Python、PHP、Javaそれぞれでjson-rpcライブラリをインポートしてgetinfoなどを実行する簡単な例が必要だ。HTTP認証部分も含めて。
ギャビンの変更は良さそうだ。すべて完了したと思う。テストビルドはこちらだ。テストしてくれ!
http://www.bitcoin.org/download/bitcoin-0.3.2.5-win32.zip
http://www.bitcoin.org/download/bitcoin-0.3.2.5-linux.tar.gz
はい、各開発者が自分で調べなくて済むように、それは本当に良いと思う。Python、PHP、Javaそれぞれでjson-rpcライブラリをインポートしてgetinfoなどを実行する簡単な例が必要だ。HTTP認証部分も含めて。
分かった。PythonとPHPは書いた。Javaについては知っている限りのことを追加した。Java JSON-RPCを使ったことのある人、動く例でwikiページを更新してもらえないか?
パスワードは絶対に必須にすべきではない。また、新しいコードは”rpcpassword=“で動作しなくなる。設定ファイルがない場合、設定ファイルにrpcpassword行がない場合、または”rpcpassword=“が含まれている場合は、パスワードを無効にすべきだ。
パスワードは絶対に必須にすべきではない。
強く反対する。ソフトウェアはデフォルトで安全であるべきであり、パスワードなしでbitcoindを実行する(またはbitcoin -server)のは明らかに安全ではない。
「まったく、設定ファイルにパスワードを追加しないとデーモンとして実行できないなんて、Bitcoinは最悪だ」と言う人はまずいないだろう。「まったく、うっかり -serverスイッチを付けて実行したら誰かに全額盗まれた、Bitcoinは最悪だ」と言う人はあり得る。
confファイルがない場合や設定ファイルにrpcpasswordが含まれていない場合に認証をデフォルトで無効にすべきではないと思うが、rpcpassword= を含む場合はどうだろうか?
両方の意見がわかる。
プログラマーが使用する言語(Fortranなど)でHTTP認証のやり方がわからない場合や、JSON-RPCライブラリでサポートされていない場合はどうだろうか? パスワード要件を明示的に無効にできるようにすべきだろうか?
一方で、テンプレートのconfファイルがあって、rpcpassword= # ここにパスワードを入力 と書いてある場合はどうだろうか?
パスワードなしではログインできないシステムは多くある。例えばこのフォーラムがそうだ。ギャビンの意見の方が説得力がありそうだ。
ところで、テストはしていないが、confファイルにrpcpassword=があっても有効であることを願う。-serverや -daemonやbitcoindを使う場合のみ警告付きで失敗すべきだ。パスワードが不要な場合は問題ないはずだ。合っているかな?
ところで、テストはしていないが、confファイルにrpcpassword=があっても有効であることを願う。
-serverや-daemonやbitcoindを使う場合のみ警告付きで失敗すべきだ。パスワードが不要な場合は問題ないはずだ。合っているかな?
そう、その通りだ。rpcpasswordが必須なのは -serverや -daemon、bitcoindを使う場合だけだ(念のためたった今テストした)。
RE: プログラマが古いCOBOLコードでHTTP認証をどうやればいいか分からない場合は? それなら、「rpcpasswordを空にしたら認証が切れる」というマジカルな仕様より、RPC認証を明示的に切るための別の設定項目をconfファイルに用意する方がいいと思う。ただ、誰かが実際に困るか、認証手段が複数になる(いつかhttps対応とか)まではこれを実装するつもりはないね。
lachesis、HTTP Basic認証に対応するのは君にとって問題かい?
ギャビン、いや、問題ない。urllib2の使い方はわかっているからな。bitcoin.confでパスワード認証を無効化するオプションが欲しい。(上級)ユーザーを自分自身から守ろうとする必要はない。
ただイラついたのは、(テスト用クライアントとはいえ)-serverを起動するためだけに、わざわざconfigファイルを編集してパスワードを入れなければならなかったことだ。もし望むなら、自分のBitcoinをハックしてデフォルトでパスワード不要にすることはいつでもできる。でも、-serverと一緒にrpcpassword= と書くとbitcoinが完全に起動失敗するのは気に入らない。正しいやり方は、ユーザーに警告(GUI起動時ならポップアップなど)を出しつつ、たとえRPCサーバーが起動しなくても他の機能は起動させることだと思う。
そもそもパスワード付きで動かしてもセキュリティ向上はわずかだ。多くのアプリでは、JSON-RPCコマンドを実行するためにそのパスワードをhttpdが読めるファイルに置いておくしかないからな。私のサーバーでハックされたことのある唯一のユーザーは? httpdだ。とはいえ、複数ユーザーのマシンでは重要なステップだし、ポート番号を変更できるようにするのも同様だ。
バグと思われるものを見つけた:ユーザー名とパスワードの組み合わせが十分長いと、bitcoindのBase64エンコーダーが以下のようなAuthorizationヘッダーを生成する:
POST / HTTP/1.1
User-Agent: json-rpc/1.0
Host: 127.0.0.1
Content-Type: application/json
Content-Length: 40
Accept: application/json
Authorization: Basic YWJiYWJiYWFiYmE6aGVsbG93b3JsZGhlbGxvd29ybGRoZWxsb3dvcmxkaGVsbG93
b3JsZGhlbGxvd29ybGRoZWxsb3dvcmxk
64文字ごとに改行が挿入され、Authorizationヘッダーが壊れるため、「bitcoin getinfo」のようなコマンドが失敗する。サーバー側は正しく動作するクライアントからは問題なく動く。
Base64Encode関数の末尾でresultから改行(および’\r’)を除去すれば解決できる:
result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
result.erase(std::remove(result.begin(), result.end(), '\r'), result.end());
もっとエレガントな解決策があるかもしれないが、これで動く。パッチはこちら:http://www.alloscomp.com/bitcoin/patches/bitcoin-svn-109-rpcbug-2010-07-25.patch
こちらでもPHPで動かそうとして問題が出ている。wikiのサンプル(jsonRPCClientでfopen(http://username:password@localhost:8332/)を試みる方法)もcurlのサンプル(setopt CURLOPT_HTTPAUTH, CURLAUTH_BASICを使用)もうまくいかない。
うーん……basic認証ではなくdigest認証を使うべきではないか?
私は普段すべてをSSLかIPSecで包んでいるが、digest認証なら初心者が簡単にやられるのを防げるかもしれない。😛
ダイジェスト認証はクライアント側でもサーバー側でもかなり実装が難しい。_本来は_SSLとクライアント証明書を使うべきだが、それは本当に面倒だ。あるいはUnixソケットか。
うーん……10年ほど前に自作のウェブサーバーにdigest認証を実装したことがある。記憶では実装はかなり簡単だった。ただ当時のクライアントサポートはかなり貧弱だった。それ以来大幅に改善されている。😊
stunnel + bitcoinのシンプルな設定をwikiに文書化するのはどうだろう? 「Securely using bitcoind from remote」というセクションで。
いつものように、ちょっと口を挟んでみただけだ。😛
バグと思われるものを見つけた:ユーザー名とパスワードの組み合わせが十分長いと、bitcoindのBase64エンコーダーが以下のようなAuthorizationヘッダーを生成する:
... Authorization: Basic YWJiYWJiYWFiYmE6aGVsbG93b3JsZGhlbGxvd29ybGRoZWxsb3dvcmxkaGVsbG93 b3JsZGhlbGxvd29ybGRoZWxsb3dvcmxk64文字ごとに改行が挿入され、Authorizationヘッダーが壊れるため、「bitcoin getinfo」のようなコマンドが失敗する。サーバー側は正しく動作するクライアントからは問題なく動く。
Base64Encode関数の末尾でresultから改行(および’\r’)を除去すれば解決できる:
result.erase(std::remove(result.begin(), result.end(), '\n'), result.end()); result.erase(std::remove(result.begin(), result.end(), '\r'), result.end());
このバグを見つけるほど長いパスワードを使っていたことに+1だ。
SVNにリビジョン110としてアップロードした。
バグと思われるものを見つけた:ユーザー名とパスワードの組み合わせが十分長いと、bitcoindのBase64エンコーダーが……64文字ごとに改行を挿入する
よく見つけたね! より簡単な修正は、rpc.cpp/EncodeBase64関数でBIO_FLAGS_BASE64_NO_NLを指定することだ:
diff --git a/rpc.cpp b/rpc.cpp
index 72bdc50..703b757 100644
--- a/rpc.cpp
+++ b/rpc.cpp
@@ -765,13 +765,14 @@ string EncodeBase64(string s)
BUF_MEM *bptr;
b64 = BIO_new(BIO_f_base64());
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, s.c_str(), s.size());
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
- string result(bptr->data, bptr->length-1);
+ string result(bptr->data, bptr->length);
BIO_free_all(b64);
return result; こちらでもPHPで動かそうとして問題が出ている。 今のところ、wikiのサンプル(jsonRPCClientが
http://username:password@localhost:8332/へのfopenを試みるもの)も、setoptでCURLOPT_HTTPAUTH、CURLAUTH_BASICを指定する私のcurlサンプルも、どちらもうまくいっていない。
おかしいな。誰かがそれでうまく動くと言っていなかったか? (どのライブラリを使っていたのだろうか?)何が問題かわかったら投稿してくれ。
すべてのPHPユーザーにとってこれほど苦労することにならないことを願う。
Fortranのシナリオがもう実現してしまったようだ。
よく見つけたね! より簡単な修正は、rpc.cpp/EncodeBase64関数でBIO_FLAGS_BASE64_NO_NLを指定することだ。
SVNリビジョン111
おかしいな。誰かがそれでうまく動くと言っていなかったか? (どのライブラリを使っていたのだろうか?)何が問題かわかったら投稿してくれ。
それは自分だ。http://jsonrpcphp.org/ のライブラリ(http://jsonrpcphp.org/download.php?file=tgz&package=lightからダウンロード)を使っていて、これが動くことを確認できた。
<?php
require_once 'jsonRPCClient.php';
$bitcoin = new jsonRPCClient('http://username:password@localhost:8332/');
echo $bitcoin->getblockcount();
?>
このバグをこれほど早く直してくれたgavinandresenとsatoshiに、ありがとう。
SNサトシ・ナカモトの投稿(2010年7月25日21:44 UTC)おかしいな。誰かがそれでうまく動くと言っていなかったか? (どのライブラリを使っていたのだろうか?)何が問題かわかったら投稿してくれ。
それは自分だ。
http://jsonrpcphp.org/のライブラリ(http://jsonrpcphp.org/download.php?file=tgz&package=lightからダウンロード)を使っていて、これが動くことを確認できた。<?php require_once 'jsonRPCClient.php'; $bitcoin = new jsonRPCClient('http://username:password@localhost:8332/'); echo $bitcoin->getblockcount(); ?>
自分の環境では動かない。wikiに載っていたから真っ先に試したんだが。jsonRPCClientから返ってくるのはこれだけだ。Warning: fopen(http://…@localhost:8332/ (http://...@localhost:8332/)) [function.fopen]: failed to open stream: HTTP request failed! HTTP/1.0 401 Authorization Required in …\jsonRPCClient.php on line 132
curlでもまだ認証を通せていない。返ってくるのは ..curl_error():transfer closed with 15 bytes remaining to read.. で、当然「bad json-syntax」になる。
php5.3.0 curl7.19.4でテストしている。 何かアイデアがあれば歓迎する。
まず、bitcoind getinfoは動くか?
次に、jsonrpcclient.phpが送ろうとしているリクエストをnetcatで捕まえてみてくれ(先にインストールが必要かもしれない)。
- bitcoindを停止
- netcat -l 8332
- ユーザー名とパスワードを(安全でないものに)変更してクライアントコードを実行
- netcatをCtrl-Cで止めて、出力を投稿してくれ