Hatena::ブログ(Diary)

xmallocのプログラミングノート

2010年08月01日

Twitter APIから取得したテキストをHTMLに変換するための公式ライブラリ

Twitter statuses/public_timeline APIとか、ツイートを取得する系のAPIのデータから@とか#とかhttp://をURLに変換する公式ライブラリがあるようなので簡単にレポ。

まずライブラリ自体は以下のURLからダウンロードできる。

mzsanford's twitter-text-rb at master - GitHub

mzsanford's twitter-text-java at master - GitHub

mzsanford's twitter-text-php at master - GitHub

Python Package Index : twitter-text-py 1.0.3

Tweet Entities | dev.twitter.comにツイートからエンティティ抜き出すにはtwitter-text使えって書いてるからこれらが公式ってことでほぼ問題無いと思う。mzsanford氏はツイッターの国際化チームの一員。ただしPythonは作者違うようなので公式扱いでは無いかもしれない。Perlは無いっぽい。



使い方

Ruby版が大元っぽかったのでそっちの使い方説明しようと思ったんだけど、rakeの動かし方がいまいちよく分からんかった(何かファイルが無いと怒られた)ので、PHP版で説明。(個人的にはPython版が一番要るんだけど作者違うのでアレ。)

  1. mzsanford's twitter-text-php at master - GitHubからリポジトリをcloneしてダウンロード
    $ git clone http://github.com/mzsanford/twitter-text-php.git
    
  2. リポジトリ中のsrc/Twitterをinclude_pathの通ったディレクトリにコピー。
    $ cd twitter-text-php/src
    $ cp -r Twitter WHERE
    
  3. Twitter_Autolinkクラスのautolinkメソッドで、APIから取得したテキストをHTMLに変換できる。
    <?php
    require_once "Twitter/Autolink.php";
    $a = new Twitter_Autolink();
    echo $a->autolink('RT @mashable @LinkedIn Beefs Up Its Twitter Integration [PICS] http://bit.ly/bkB7cA #linkedin #tweets #twitter'), "\n";
    ?>
    
    出力はこんな感じ。スクリーンネームハッシュタグURLが<a>タグでリンクになってる。
    RT @<a class="tweet-url username" href="http://twitter.com/mashable">mashable</a> @<a class="tweet-url username" href="http://twitter.com/LinkedIn">LinkedIn</a> Beefs Up Its Twitter Integration [PICS] <a href="http://bit.ly/bkB7cA">http://bit.ly/bkB7cA</a> <a href="http://twitter.com/search?q=%23linkedin" title="#linkedin" class="tweet-url hashtag">#linkedin</a> <a href="http://twitter.com/search?q=%23tweets" title="#tweets" class="tweet-url hashtag">#tweets</a> <a href="http://twitter.com/search?q=%23twitter" title="#twitter" class="tweet-url hashtag">#twitter</a>
    
    ちなみに$Twitter_Autolink->noFollowで<a>タグにnofollow属性付けれるってソースに書いてあるんだけどPHP版は実装されてないっぽい。有効にしたかったら要改造。

Githubにも使い方が書いてあるのでそっちも見てくれ・・・まあそれ紹介すれば済む話なんだけど。

他の言語のは誰か親切な人が説明してくれるはず。



後書き

不勉強ながらこのライブラリの存在自体知らなかったりしました。Tweet Entities | dev.twitter.comを読んでたらたまたま見つけた感じです。twitter-text-rbとかでググるとそれなりに使ってる人いるみたいですが、普及してるとは言い難い感じ。

とりあえず、一時期あった日本語ハッシュタグとかの公式変換ルールがいまいちよく分からん問題とかはこれ一発で解決ですよ、ということで。

2010年07月13日

DOMモジュールをテンプレートエンジン代わりに使う三つの利点+1

PHP5からPHP標準添付のDOMモジュールもlibxml2ベースのものになって、htmlも処理できるようになったのでだいぶ使いやすくなったと思う。DOMを使うと文書構造にアクセスして編集できるので、テンプレートエンジンの代わりとしてそれなりに利用されてても良いと思うんだけど少なくとも個人的観測範囲内だと全ったくもって流行ってなかったりして*1大変残念なので、PHPDOMモジュールテンプレートエンジン代わりに使う利点をまとめてみました。



1.インスコ不要

最初に書いたけど、PHP5のDOMモジュールインスコしなくても標準で使える。もう少し具体的に言うと--disable-dom configureオプションを付けてビルドしてなかったら使える、ってことなのでその辺のレン鯖でも普通は動くので便利です。

インスコ不要なものはデプロイするときに「あれ入れた?」「忘れたー」「あれは入れた?」「忘れたー」「あっちは」「うーん知らない」「氏ね」ループに陥らなくて精神的に大変よろしいと思います。



2.デザイナー/コーダーへの説明が楽

どのテンプレートエンジンにしろ、だいたいそれ用の構文があったりします。無いやつもあるけどだいたいあります。特にSmartyとか人気のやつはだいたいあります。

いずれにしてもテンプレートエンジンを使うとなったら、「ここはテンプレートエンジンの構文なんで変えないでくださいね」「そこも同じなんで以下同文」「あーそれも同じなんで以下同文」「だからそこ変えるんじゃねーよボケ」という不毛な説明を延々と繰り返すハメになることを覚悟する必要がある。世の中には大変素晴らしい企業さんもあるようで、そういう説明が要らないところもあるみたいですが、うち場合デザイナーがいない上、デザイナー/コーダー=お客さんってプロジェクトが大半なので、単に可否の問題でなく営業的な問題であまりこの辺を強く言えない事情もあったりします。なので物分かりのあまり良くないお客さんの場合は延々と不毛な説明するとかえってややこしいので、htmlもらってこっちでテンプレートに変換する退屈な作業を延々と繰り返してます。

でもDOMなら不毛な説明も退屈な作業も不要。id属性とかclass属性とか、テンプレートとして使用する要素がhtmlそのものなので向こうも分かるはず。さすがにこの程度も分からない奴とは一緒に仕事できないのです。



3.DOMなんで覚えるのも簡単

DOMなんでJavaScriptかじってたら多少は知ってるはず・・・て言うかウェブ関係のプログラミングやってれば知らないのはかなり問題。知らんと思ってる人もgetElementByIdとかcreateElementとかがDOMだと言われれば「へぇー、これって2年くらい前に流行ってたよね、2年くらい前に見たわ」と返事してくれることでしょう。

という感じでたぶんDOM自体は知ってると思うので、簡単なサンプルも載せときます。


まずテンプレートとしてexam1.htmlを作ります。モジュールの仕様の関係で、doctypeとmetaタグによるcharsetの指定を省略すると挙動が変わるので省略しないように。

<!doctype HTML>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>
  <body>
    <div id="message"></div>
  </body>
</html>

次にexam1.htmlを読み込んでid="message"のdivの中身を"あいうえお"に書き換えるスクリプトを作ります。

<?php
$doc = new DOMDocument();
$doc->loadHTMLFile("exam1.html");
$message = $doc->getElementById("message");
$message->appendChild($doc->createTextNode("あいうえお"));
echo $doc->saveHtml();
?>

このスクリプトを動かすと以下の出力が得られます。

<!DOCTYPE HTML>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head>
<body>
    <div id="message">あいうえお</div>
  </body>
</html>

ところどころ改行とスペースが詰められるは、まあそういう仕様なんで諦めてください。スペース入れたいときは&nbsp;使った方が良いですよ、ということです。

もっと詳しいことはPHP: DOM - Manualからどうぞ。



+1.ドキュメントに直接アクセスできる

うちだけの話かもしれないので+1にしたけど、例えばhtmlフォームのラベルとか、ユーザーなんだかデザイナーなんだかよく分からない人が適当にいじくりたいという要望がたまにあります。それでまあフォームの場合、そのラベルをエラーメッセージに埋め込みたいとか言う話になるので、コンフィギュレーションファイルを用意したり結局プログラムで二重持ちしたりしてることがありますが、色々なやましい問題がこの辺で噴出します。

そこでDOMの出番ですよ、と。テンプレートDOMにしとけば、htmlから適当な要素の値を抜いてくれば良いだけですので、この手の問題は全部解決です。


例えばこんな感じのexam2.htmlがあって、

<!doctype HTML>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p id="error" style="display:none"></p>
    <form method="post">
      <label for="username">ユーザー</label>
      <input type="text" id="username" name="username" />
      <input type="submit" />
    </form>
  </body>
</html>

usernameが未入力ならlabelを含んだエラーメッセージを表示する場合はこんなスクリプトで解決できます。

<?php
$doc = new DOMDocument();
$doc->loadHTMLFile("exam2.html");
if (isset($_POST['username']) && !$_POST['username']) {
    $username = $doc->getElementById('username');
    $labels = $doc->getElementsByTagName('label');
    for ($i = 0; $i < count($labels); $i++) {
        $label = $labels->item($i);
        if ($label->getAttribute('for') == $username->getAttribute('id')) {
            $mesg = "{$label->textContent}が未入力です。";
            $error = $doc->getElementById('error');
            $error->removeAttribute('style');
            $error->appendChild($doc->createTextNode($mesg));
            break;
        }
    }
}
echo $doc->saveHtml();
?>

エラーメッセージを含む出力はこんな感じ。ちゃんとメッセージにlabel for="username"の値が表示されます。

<!DOCTYPE HTML>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head>
<body>
    <p id="error">ユーザーが未入力です。</p>
    <form method="post">
      <label for="username">ユーザー</label>
      <input type="text" id="username" name="username"><input type="submit">
</form>
  </body>

</html>


ということで、騙されたと思って一度騙されてください。

*1DOMテンプレート代わりに使うといったことを書いてるブログは確認してますが、流行ってないってところに力点置いてます

2010年05月13日

Chroma-Hashを試してみる

入力したパスワードのカラーパターンを横に表示しておけばミスがなくなるんじゃね?『Chroma-Hash』が面白そうだったので試してみました。リンク先辿っていけば行き着きますが、githubJavaScriptのChroma-Hash jQuery extensionがありますので、これを使えば既存のHTMLフォームも簡単にChroma-Hashizeできそうです。

以下、とりあえず動かしてみたい俺向きの簡単な例です。githubからchroma-hash.jsをダウンロードして、ファイルをダウンロードしたディレクトリと同じディレクトリに以下のhtmlを置きます。

<html>
  <head>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"></script>
    <script type="text/javascript" src="chroma-hash.js"></script>
    <script type="text/javascript">
      $(document).ready(function() {
        $("input:password").chromaHash({bars: 3});
      });
    </script>
  </head>
  <body>
    <input type="password">
  </body>
</html>

ブラウザから直接立ち上げて、パスワードフィールドに適当に入力するとChrome-Hashが表示されます。

f:id:xmalloc:20100513180158p:image

続けて入力していくとChrome-Hashが変わっていきます。

f:id:xmalloc:20100513180159p:image

これはなかなか面白いですねw

2010年05月11日

PHPをクロスサイトスクリプティングできなくするパッチを作ってみた

PHPをクロスサイトスクリプティングできなくするパッチを作ってみました。とっととブツを見たい人はhttp://github.com/xmalloc/php-auto-escapeからどうぞ。



クロスサイトスクリプティングとは

パッチの話に入る前に、クロスサイトスクリプティング(以下XSS)について簡単に説明します。XSSとはあるウェブアプリケーションの入力可能な項目にユーザーが任意のJavaScriptを入力できるようになっている場合に発生する脆弱性のことで、XSS可能なウェブサイトではユーザーが任意のJavaScript*1を埋め込むことが可能になります。

例えば以下のようなPHPスクリプトがあった場合に、$contentに入力フォームなどからユーザーが任意の文字列を入力できる形になっていると、

<p>
<?php echo $content; ?>
</p>

$contentに任意のJavaScriptを埋め込むことができます。

<p>
<script type="text/javascript">alert('XSS');</script>
</p>

この例で埋め込んだコードは'XSS'をalertするだけですが、ウィルスを読み込ませるコードを埋め込むような悪意あるコードも埋め込むことができます。


XSS対策として一般的なのは、PHPからHTMLを出力する際にhtmlspecialchars関数などを使ってすべての変数出力をエスケープすることです。上のPHPコードは以下のように書き換えることでXSS脆弱性を取り除くことができなくなります。

<p>
<?php echo htmlspecialchars($content); ?>
</p>

HTMLが埋め込み可能になってる場合のほか、javascript:スキーマのURLを埋め込むことができたり、任意のCSSを埋め込むことができる場合*2にもXSS脆弱性が存在します。そのようなXSS脆弱性については、htmlspecialcharsでは対応できません。URLの妥当性チェックや、任意のCSSをユーザーから入力できないようにする必要があります。



PHPをクロスサイトスクリプティングできなくするパッチとは

このパッチはPHPに自動エスケープ機能を追加するものです。このパッチを適用したPHPでは、__auto_escape関数を呼び出して自動エスケープの有効/無効を切り替えることができるようになります。自動エスケープを有効にすると、echo、print、printf、vprintf、<?= ?>からの出力はすべて、htmlspecialchars(ENT_QUOTES)によりエスケープしてから出力されるようになります。

以下のPHPスクリプトをパッチを適用したPHPから動かすと、

<?php __auto_escape(1); ?>
<html>
<?php echo "<>&\"'\n"; ?>
</html>

スクリプトの出力は以下の様になります。

<html>

&lt;&gt;&amp;&quot;&#039;

</html>


__auto_escape(1)を呼び出すか、<?php ?>タグの中の出力(<?= ?gt;含む)がすべて自動でhtmlspecialcharsでエスケープされるようになります。__auto_escape(0)を呼び出すと自動エスケープは無効になります。

<?php __auto_escape(1); ?>
<html>
<?php echo "<エスケープされる>\n"; ?>
<?php __auto_escape(0); ?>
<?php echo "<エスケープされない>\n"; ?>
</html>

自動エスケープを有効にした上でエスケープ済みの文字列を出力したい場合は__safe関数が使用できます。__safeは自動エスケープの有効/無効に関わらず、文字列をエスケープせずに出力します。

<?php __auto_escape(1); ?>
<script type="text/javascript">
var x = <?php __safe(json_encode($x)); ?>;
</script>

自動エスケープの有効/無効は、__auto_escape関数以外にphp.iniや.htaccessでも制御できます。__auto_escape ini変数にOnを指定すると、PHP起動時に自動エスケープが有効になります。パッチしていないPHPとの互換性を確保するため、__auto_escape ini変数のデフォルトはOffです。パッチしたソースツリー上のphp.iniファイルに__auto_escapeが追加されてますので、詳しくはそちらをご覧ください。



インストール

PHPのアーカイブをhttp://www.php.net/downloads.phpからダウンロードし、パッチをhttp://github.com/xmalloc/php-auto-escapeからダウンロードしてから、アーカイブとパッチを同じディレクトリに置いて以下のコマンド(PHP-5.3.2の場合)を実行してください。configureのオプションは環境に合わせて適宜設定してください。(パッチにより追加されるconfigureオプションはありません。)

$ tar xvzf php-5.3.2.tar.bz2
$ cd php-5.3.2
$ patch -p0 < ../php-5.3.2-auto-escape.diff
$ ./configure
$ make
$ make install

今のところパッチを用意しているPHPのバージョンは、5.2.12、5.2.13、5.3.2です。



開発経緯とか

最近、既存プログラムを修正するプロジェクトに携わったのですが、すべてのPHPスクリプトにXSS脆弱性が存在する上コード量も結構あり、すべて手直しするのは時間がかかるし修正漏れも発生しそうだったので、PHP自体を改造することにしました。元々Djangoのautoespaceフィルタがみたいな機能がPHPにもあれば便利かなーと思っていたのですが、実際に実装してみるとハンパ無く便利です。普段は以下のようなhtmlspecialcharsのラッパー関数を用意しているのですが、抜けが無いかチェックするのも結構しんどかったりするので、そういう瑣末な作業から開放されるのは気分良いです。

<?php
function xecho($str) {
    echo htmlspecialchars($str);
}

PHP自体にパッチするので共用レンタルサーバとかだとちょっと使うのは難しそうですし、新規プロジェクトならテンプレートエンジンの自動エスケープ機能があればそれを使えば事足りるので、パッチの実用性について若干???な部分もありますが、XSS対策のオプションが増えること自体は悪いことでも無いと思うので公開することにしました。

パッチが既存のPHPの機能と衝突してる箇所は無いはずです。自動エスケープを有効にした場合もob_startによるバッファリングとは干渉しないことは確認しています。何か問題を見つけた方は、このエントリにコメントしていただけると助かります。

*1:VBScriptとかも埋め込めるはずですがw

*2:CSSからJavaScriptを実行できるため

2010年05月01日

「どちらがオープンか」でFlashとiPhoneOSを比べたらFlashの方が100倍マシ

ジョブスがFlash批判した記事については、目を通した方が多いと思います。てっく煮ブログさんで和訳されてますのでまだの方はどうぞ。で、御大はiPhoneはHTML5で動くからオープン、Flashはオープンな規格に従ったものじゃないからクローズド、クローズドは悪いことなんでFlashは死ね、と持論を展開しています。

1. Flash はオープンじゃない


Adboe の Flash は 100% プロプライエタリな製品である。Flash は Adobe のみが提供し、Adobe のみが将来的な機能拡張や値段付けなどを決定できる。Flash は多くの環境で利用可能ではあるが、だからといってオープンとは言えない。なぜなら、Flash は Adobe によって完全に制御されているし、Adobe のみが提供しているものだからだ。どのように定義しようと、Flash はクローズドなシステムである。


Apple の製品もプロプライエタリである。iPhone OS はプロプライエタリであるが、我々は Web に関連する標準は全てオープンでなければならないと強く信じている。Apple は Flash を捨て、オープンな標準である HTML5・CSS・JavaScript を選択した。Apple のモバイル端末はこれらの標準の高性能・低電力な実装を搭載している。HTML5 は新しい Web 標準であり、Apple や Google などの多くの会社が採用している。HTML5 を利用すれば、Flash のようなサードパーティーのプラグインなしに、グラフィックやタイポグラフィ・アニメーション・トランジッションを実現できる。HTML5 は完全にオープンであり、Apple も参加している標準化委員会によって管理されている。

http://d.hatena.ne.jp/nitoyon/20100430/thoughts_on_flash_jp

FlashはAdobeの製品ですし、クローズドなのは確かです。※ただしiPhoneほどではない。

上の話で何かおかしいなと思うのは、今の時点でHTML5がiPhoneの一番重要な部分だと見ている人ってどれぐらいいるんでしょうか?という点です。やっぱiPhoneで魅力的なのはネイティブアプリなんじゃないでしょうか?CMでもアプリがどーとか言ってたと思いますし、とりあえず個人的にはHTML5ベースのiPhoneアプリにはそれほど魅力を感じません。

iPhoneネイティブアプリを作りたかったら、iPhone SDKが必要です。公開する場合はデベロッパー登録する必要があり、年間参加費10,800円必要になります。一方のFlashはというとFlex SDKはタダで配布されていますし、SDKのライセンスはMPLです*1。配布したくなったらgeocitiesにでも置けばいいだけですので、その辺の制限はまったくありません・・・なんてことはあえて説明する必要無いと思いますが。iPhone SDKのライセンスなんか完全にプロプライエタリライセンスな訳で、それと比べるとFlashの方がよほどオープンな環境に見えます。

結局Flashで本当にオープンじゃないのはFlash Playerということになるのですが、iPhoneOSだってほぼプロプライエタリな訳で、そこを無視してiPhoneがオープンだと言うのはちょっと無理があるんじゃないかと思います。それにFlash PlayerにはGnashみたいなフリーの実装もあって、オープンにこだわりたい人はこれを使えばいいです。これで完全にオープンな環境で、Flashアプリを作って動かして楽しめます*2。GnashはGNUのHigh Priority Free Software Projectsにも入ってますので、GNUもそれなりに力入れてるみたいです。


HTML5のcanvasとマルチスレッドがあれば、Flashアプリと同等なことをHTML/JavaScript/CSSのみで実装することは確かにできます。ただそれはiPhoneがオープンか否かとは別の話です。Flashはブラウザ依存性みたいなやっかいな問題を含んでいないので、ビジュアルが重要なプログラムを手早く作れるので結構重宝しています。OS問わず(Playerさえあれば)どの環境でもだいたい動きますし、今のところHTMLよりもFlashの方が便利な部分も多々あります。

ということなので、自然な流れでHTML5がFlashを駆逐することは歓迎しますが、Appleの戦略の一環としてFlashが下火になるのはマジで本当にちょっとめちゃくちゃ勘弁してもらいたいです。

*1:一部フリーでは無いライセンスの配布物も含まれてます、詳しくはFlex SDKのサイトを見てください

*2:対応しているSWFのバージョンが古いので真剣にFlashで遊びたい人にはgnashだとちょっと力不足ですが