2011年12月31日土曜日

引越しました

はてなに引越しました。
http://d.hatena.ne.jp/minetosh/
アフィリエイトが芳しくなかったら、戻ってくるかもです。

2011年9月13日火曜日

GDD2011JP DevQuiz スライドパズルの回答ソースコード

はっきり言って恥ずかしいコードだけど晒します。

基本、双方向幅優先で物理パワーでねじ伏せる作戦。
優先して残すのは、ゴールまでのマンハッタン距離が近いもの。

以下のコードで-Xmx4608M付けてXeonの6G、64ビットUbuntu10.10マシンで40時間かけると4872問解けます(残り128)。
更に残ったものについて、幅増やしたり上限増やしたりして残り49。
さらに、最初の10手はスタートからの遠さ優先その後ゴールまでの近さ優先、次最初の20手は...と繰り返して行って、残り14。あとは手動で小さいサイズの問題に帰着させて解かせた。

一番原始的な気がする。
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.util.Hashtable;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Arrays;
import java.util.Enumeration;

public class Panel2 {
    public static final int MAX_NUM = 10000;
    public static final int MAX_COUNT = 100;

    public class InnerPanel {
        int width, height;
        char maxc;
        int maxcount;
        String panel[];
        int distance;
        int zerox, zeroy;
        StringBuffer route;

        /**
          * コンストラクタ(初期状態用).
         */
        public InnerPanel(int width, int height, String panelstr) {
            // 幅高さ
            this.width = width;
            this.height = height;
            // 配列化
            panel = new String[height];
            for (int i = 0; i < height; i++)
                panel[i] = panelstr.substring(width * i, width * (i + 1));
            // 一番でかい文字保存&0位置保存
            maxc = '0';
            char c;
            for (int i = 0; i < panelstr.length(); i++) {
                c = panelstr.charAt(i);
                if (c == '=')
                    continue;
                if (c == '0') {
                    zeroy = i / width;
                    zerox = i % width;
                }
                if (maxc < c)
                    maxc = c;
            }
            maxcount = toInt(maxc);
            route = new StringBuffer();
        }

        /**
          * コンストラクタ(ゴール作成用).
         */
        private InnerPanel(int width, int height) {
            // 幅高さ
            this.width = width;
            this.height = height;
            this.route = new StringBuffer();
        }

        /**
          * ゴール作成.
         */
        public InnerPanel makeGoal() {
            InnerPanel goal = new InnerPanel(width, height);
            goal.maxc = maxc;
            goal.maxcount = maxcount;

            // 整列した状態作成
            goal.panel = new String[height];
            int count = 0;
            String line = null;
            char c;
            for (int i = 0; i < height; i++) {
                line = panel[i];
                StringBuffer sb = new StringBuffer();
                for (int j = 0; j < width; j++) {
                    count++;
                    c = line.charAt(j);
                    if (c == '=') {
                        sb.append(c);
                    } else if (count > maxcount) {
                        sb.append('0');
                        goal.zeroy = i;
                        goal.zerox = j;
                    } else {
                        sb.append(toChar(count));
                    }
                }
                goal.panel[i] = sb.toString();
            }
            return goal;
        }

        /**
         * 文字から数値へ.
         */
        public int toInt(char c) {
            if ('0' <= c && c <= '9')
                return c - '0';
            else
                return c - 'A' + 10;
        }

        /**
         * 数値から文字へ.
         */
        public char toChar(int i) {
            if (i < 10)
                return (char)('0' + i);
            else
                return (char)('A' + i - 10);
        }

        /**
         * パネル内容の表示.
         */
        public void print() {
            System.out.println("======");
            for (int i = 0; i < panel.length; i++)
                System.out.println(panel[i]);
        }

        public int getDistance(InnerPanel target) {
            String p1[] = panel;
            String p2[] = target.panel;

            // 文字->座標のHashtable作成
            Hashtable<Character, Integer> p1hash =
                new Hashtable<Character, Integer>();
            Hashtable<Character, Integer> p2hash =
                new Hashtable<Character, Integer>();
            String p1line = null, p2line = null;
            char c1, c2;
            for (int i = 0; i < p1.length; i++) {
                p1line = p1[i];
                p2line = p2[i];
                for (int j = 0; j < p1line.length(); j++) {
                    c1 = p1line.charAt(j);
                    if (c1 != '=' && c1 != '0') {
                        p1hash.put(c1, i * 10 + j);
                    }
                    c2 = p2line.charAt(j);
                    if (c2 != '=' && c2 != '0')
                        p2hash.put(c2, i * 10 + j);
                }
            }
            char c;
            Integer xy1, xy2;
            int sa, total = 0;
            for (int i = 1; i <= maxcount; i++) {
                c = toChar(i);
                xy1 = p1hash.get(c);
                if (xy1 == null)
                    continue;
                xy2 = p2hash.get(c);
                sa = Math.abs(xy1 / 10 - xy2 / 10) +
                    Math.abs(xy1 % 10 - xy2 % 10);
                total += sa;
            }
            return total;
        }

        /**
         * 距離を計算して保存.
         */
        public int calculateDistance(InnerPanel target) {
            distance = getDistance(target);
            return distance;
        }

        /**
         * String表現.
         */
        public String toString() {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < height; i++)
                sb.append(panel[i]);
            return sb.toString();
        }

        /**
         * 次の状態.
         */
        public InnerPanel[] nextPanels() {
            ArrayList<InnerPanel> panel_array = new ArrayList<InnerPanel>();
            char up = 0, down = 0, left = 0, right = 0;
            if (zeroy > 0)
                up = panel[zeroy - 1].charAt(zerox);
            if (zeroy < height - 1)
                down = panel[zeroy + 1].charAt(zerox);
            if (zerox > 0)
                left = panel[zeroy].charAt(zerox - 1);
            if (zerox < width - 1)
                right = panel[zeroy].charAt(zerox + 1);

            try {
                if (up != 0 && up != '=')
                    panel_array.add(makeUp());
                if (down != 0 && down != '=')
                    panel_array.add(makeDown());
                if (left != 0 && left != '=')
                    panel_array.add(makeLeft());
                if (right != 0 && right != '=')
                    panel_array.add(makeRight());
            } catch (IOException ex) {
                ;
            }

            return (InnerPanel[])panel_array.toArray(new InnerPanel[0]);
        }

        /**
         * コピー作成.
         */
        public InnerPanel makeCopy() {
            InnerPanel copy = new InnerPanel(width, height);
            copy.maxc = maxc;
            copy.maxcount = maxcount;
            copy.panel = new String[panel.length];
            for (int i = 0; i < panel.length; i++)
                copy.panel[i] = panel[i];
            copy.distance = distance;
            copy.zerox = zerox;
            copy.zeroy = zeroy;
            copy.route = new StringBuffer(route.toString());
            return copy;
        }

        /**
         * 上へ移動.
         */
        public InnerPanel makeUp() throws IOException {
            InnerPanel copy = makeCopy();
            byte tline[] = copy.panel[zeroy - 1].getBytes("UTF-8");
            byte sline[] = copy.panel[zeroy].getBytes("UTF-8");
            byte b = sline[zerox];
            sline[zerox] = tline[zerox];
            tline[zerox] = b;
            copy.panel[zeroy - 1] = new String(tline, "UTF-8");
            copy.panel[zeroy] = new String(sline, "UTF-8");
            copy.zeroy--;
            copy.route.append('U');
            return copy;
        }

        /**
         * 下へ移動.
         */
        public InnerPanel makeDown() throws IOException {
            InnerPanel copy = makeCopy();
            byte tline[] = copy.panel[zeroy + 1].getBytes("UTF-8");
            byte sline[] = copy.panel[zeroy].getBytes("UTF-8");
            byte b = sline[zerox];
            sline[zerox] = tline[zerox];
            tline[zerox] = b;
            copy.panel[zeroy + 1] = new String(tline, "UTF-8");
            copy.panel[zeroy] = new String(sline, "UTF-8");
            copy.zeroy++;
            copy.route.append('D');
            return copy;
        }

        /**
         * 左へ移動.
         */
        public InnerPanel makeLeft() throws IOException {
            InnerPanel copy = makeCopy();
            byte tline[] = copy.panel[zeroy].getBytes("UTF-8");
            byte b = tline[zerox - 1];
            tline[zerox - 1] = tline[zerox];
            tline[zerox] = b;
            copy.panel[zeroy] = new String(tline, "UTF-8");
            copy.zerox--;
            copy.route.append('L');
            return copy;
        }

        /**
         * 右へ移動.
         */
        public InnerPanel makeRight() throws IOException {
            InnerPanel copy = makeCopy();
            byte tline[] = copy.panel[zeroy].getBytes("UTF-8");
            byte b = tline[zerox + 1];
            tline[zerox + 1] = tline[zerox];
            tline[zerox] = b;
            copy.panel[zeroy] = new String(tline, "UTF-8");
            copy.zerox++;
            copy.route.append('R');
            return copy;
        }
    }

    /**
     * メイン.
     */
    public static void main(String args[]) {
        int skip = 0;
        if (args.length > 0)
            skip = Integer.parseInt(args[0]);
        try {
            InputStreamReader isr = null;
            try {
                isr = new InputStreamReader(System.in, "UTF-8");
                BufferedReader br = null;
                try {
                    br = new BufferedReader(isr);
                    new Panel2().play(br, skip);
                } finally {
                    if (br != null) br.close();
                }
            } finally {
                if (isr != null) isr.close();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
        System.exit(0);
    }

    public int lx = 0;
    public int rx = 0;
    public int ux = 0;
    public int dx = 0;
    public int cases = 0;

    /**
     * コンストラクタ.
     */
    public Panel2() {
    }

    /**
     * 開始.
     */
    public void play(BufferedReader br, int skip) throws IOException {
        String numberstr = br.readLine();
        String numbers[] = numberstr.split(" ");
        lx = Integer.parseInt(numbers[0]);
        rx = Integer.parseInt(numbers[1]);
        ux = Integer.parseInt(numbers[2]);
        dx = Integer.parseInt(numbers[3]);
        cases = Integer.parseInt(br.readLine());

        for (int i = 0; i < skip; i++)
            br.readLine();

        for (int i = 0; i < cases; i++) {
            solve(skip + i + 1, br.readLine());
        }
    }

    /**
     * 1問解く.
     */
    public void solve(int panel_num, String line) {
        String lines[] = line.split(",");
        int width = Integer.parseInt(lines[0]);
        int height = Integer.parseInt(lines[1]);
        InnerPanel start_panel = new InnerPanel(width, height, lines[2]);
        InnerPanel goal_panel = start_panel.makeGoal();
        Hashtable<String, String> route_hash = new Hashtable<String, String>();

        // 距離設定
        start_panel.calculateDistance(goal_panel);
        goal_panel.calculateDistance(start_panel);

        // 既出状態保存
        Hashtable<String, InnerPanel> start_already_hash = new Hashtable<String, InnerPanel>();
        Hashtable<String, InnerPanel> goal_already_hash = new Hashtable<String, InnerPanel>();

        // 先端状態保存
        ArrayList<InnerPanel> start_edge_array = new ArrayList<InnerPanel>();
        Hashtable<String, InnerPanel> start_edge_hash =
            new Hashtable<String, InnerPanel>();
        ArrayList<InnerPanel> start_next_array = new ArrayList<InnerPanel>();
        ArrayList<InnerPanel> goal_edge_array = new ArrayList<InnerPanel>();
        Hashtable<String, InnerPanel> goal_edge_hash =
            new Hashtable<String, InnerPanel>();
        ArrayList<InnerPanel> goal_next_array = new ArrayList<InnerPanel>();

        // 初期状態にする
        start_edge_array.add(start_panel);
        start_edge_hash.put(start_panel.toString(), start_panel);
        start_already_hash.put(start_panel.toString(), start_panel);
        goal_edge_array.add(goal_panel);
        goal_edge_hash.put(goal_panel.toString(), goal_panel);
        goal_already_hash.put(goal_panel.toString(), goal_panel);

        // ここからぶんまわす

        InnerPanel onePanel, newPanel, newPanels[], bingo;
        String sum = null;
        boolean bingo_flg = false;
int loop_count = 0;
        while (true) {

++loop_count;

            if (loop_count > MAX_COUNT) {
                System.out.println(panel_num + ":");
                break;
            }

            // ゴール側からの比較用Hashtableクリア
            start_edge_hash = new Hashtable<String, InnerPanel>();
            start_next_array = new ArrayList<InnerPanel>();


            // スタート状態からの先端状態モノでループ
            for (int i = 0; i < start_edge_array.size(); i++) {
                onePanel = start_edge_array.get(i);
                // 次の状態取得
                newPanels = onePanel.nextPanels();
                for (int j = 0; j < newPanels.length; j++) {
                    newPanel = newPanels[j];
                    sum = newPanel.toString();

                    // 既出なら捨てる
                    if (start_already_hash.containsKey(sum))
                        continue;
                    // 追加
                    start_already_hash.put(sum, newPanel);

                    // 既出とビンゴ?
                    bingo = goal_already_hash.get(sum);
                    if (bingo != null) {
                        checkMate(newPanel, bingo, true, panel_num, route_hash);
                        bingo_flg = true;
                    }
                    // ビンゴ?
                    bingo = goal_edge_hash.get(sum);
                    if (bingo != null) {
                        checkMate(newPanel, bingo, true, panel_num, route_hash);
                        bingo_flg = true;
                    }

                    // 既出じゃなければゴールとの距離計算
                    newPanel.calculateDistance(goal_panel);
                    if (newPanel.distance == 0) {
                        checkMate(newPanel, null, true, panel_num, route_hash);
                        bingo_flg = true;
                    }
                    // 次の先端状態モノに入れる
                    start_next_array.add(newPanel);
                    start_edge_hash.put(sum, newPanel);
                }
            }
            start_edge_array = sort(start_next_array);

            // スタート側からのがビンゴ
            if (bingo_flg)
                break;

            // スタート側からの比較用Hashtableクリア
            goal_edge_hash = new Hashtable<String, InnerPanel>();
            goal_next_array = new ArrayList<InnerPanel>();
            // ゴール側からの先端状態モノでループ
            for (int i = 0; i < goal_edge_array.size(); i++) {
                onePanel = goal_edge_array.get(i);
                // 次の状態取得
                newPanels = onePanel.nextPanels();
                for (int j = 0; j < newPanels.length; j++) {
                    newPanel = newPanels[j];
                    sum = newPanel.toString();

                    // 既出なら捨てる
                    if (goal_already_hash.containsKey(sum))
                        continue;
                    // 追加
                    goal_already_hash.put(sum, newPanel);

                    // 既出とビンゴ?
                    bingo = start_already_hash.get(sum);
                    if (bingo != null) {
                        checkMate(bingo, newPanel, false, panel_num, route_hash);
                        bingo_flg = true;
                    }
                    // ビンゴ?
                    bingo = start_edge_hash.get(sum);
                    if (bingo != null) {
                        checkMate(bingo, newPanel, false, panel_num, route_hash);
                        bingo_flg = true;
                    }

                    // 既出じゃなければ初期との距離計算
                    newPanel.calculateDistance(start_panel);
                    if (newPanel.distance == 0) {
                        checkMate(null, newPanel, true, panel_num, route_hash);
                        bingo_flg = true;
                    }
                    // 次の先端状態モノに入れる
                    goal_next_array.add(newPanel);
                    goal_edge_hash.put(sum, newPanel);
                }
            }
            goal_edge_array = sort(goal_next_array);

            // ゴール側からのがビンゴ
            if (bingo_flg)
                break;
        }
        Enumeration<String> e = route_hash.keys();
        while (e.hasMoreElements()) {
            String key = e.nextElement();
            System.out.println(panel_num + ":" + route_hash.get(key) + ":" + key);
        }
    }

    public ArrayList<InnerPanel> sort(ArrayList<InnerPanel> source) {
        InnerPanel source_array[] = source.toArray(new InnerPanel[0]);
        Arrays.sort(source_array, new PanelCompare());
        ArrayList<InnerPanel> sorted_list = new ArrayList<InnerPanel>();
        for (int i = 0; i < MAX_NUM && i < source_array.length; i++)
            sorted_list.add(source_array[i]);
        return sorted_list;
    }

    public class PanelCompare implements Comparator {
        public int compare(Object o1, Object o2) {
            InnerPanel p1 = (InnerPanel)o1;
            InnerPanel p2 = (InnerPanel)o2;
            return p1.distance - p2.distance;
        }
    }

    /**
     * チェックメイト.
     */
    public void checkMate(InnerPanel start_side, InnerPanel goal_side,
        boolean even, int panel_num, Hashtable<String, String> route_hash) {
        StringBuffer resolv_sb = null;
        if (start_side != null)
            resolv_sb = new StringBuffer(start_side.route.toString());
        else
            resolv_sb = new StringBuffer();

        StringBuffer goal_sb = null;
        if (goal_side != null)
            goal_sb = goal_side.route;
        else
            goal_sb = new StringBuffer();

        char c;
        for (int i = goal_sb.length() - 1; i >= 0; i--) {
            c = goal_sb.charAt(i);
            switch (c) {
                case 'U':
                    resolv_sb.append('D');
                    break;
                case 'D':
                    resolv_sb.append('U');
                    break;
                case 'L':
                    resolv_sb.append('R');
                    break;
                case 'R':
                    resolv_sb.append('L');
                    break;
            }
        }
        String result = resolv_sb.toString();

        // カウント
        int u_count = 0, d_count = 0, l_count = 0, r_count = 0;
        for (int i = 0; i < result.length(); i++) {
            c = result.charAt(i);
            switch (c) {
                case 'U':
                    u_count++;
                    break;
                case 'D':
                    d_count++;
                    break;
                case 'L':
                    l_count++;
                    break;
                case 'R':
                    r_count++;
                    break;
            }
        }
        StringBuffer save_sb = new StringBuffer();
        save_sb.append("U=");
        save_sb.append(u_count);
        save_sb.append(",D=");
        save_sb.append(d_count);
        save_sb.append(",L=");
        save_sb.append(l_count);
        save_sb.append(",R=");
        save_sb.append(r_count);
        route_hash.put(save_sb.toString(), result);
    }
}

2011年8月4日木曜日

mod_proxyでエンコード・デコードされる文字

技術メモ。のちのち参照の可能性アリ。
apache2.2.17のap_proxy_canonencから(modules/proxy/proxy_util.cの153〜238行目)

  • /とエンコードされた/(%2F)はそのまま。
  • ~$-_.+!*'(),;:@&=(%7E%24%2D%5F%2E%2B%21%2A%27%28%29%2C%2B%2A%40%26%3D)はデコードされる。
  • 英数字でもなく上記の文字でもないものはエンコードされる。

以上の処理が行われた後、バックエンドに渡される。

2011年7月13日水曜日

XSS in Google Maps (again)

おそらくは前回のXSSのあとで機能追加されたリンクだと思う。
cnnでWeb検索してからMapsに移動すると、「Search the web for cnn」というリンクが左側に表示される。
このリンクのhrefがシングルクォート(')でくくられていた。
しかも前回見つけた「必要なのかどうかわからない機能」がこのリンクで発動していた。
前回は同じサービス内のリンクでのみ発動したので、なんでここだけ?と不思議に思ったが同じ手口であっさり通った。


成功して報告したあとに気づいたが、このリンク実は普通にqパラメータにシングルクォート入れるだけでhrefを終わらせることができていた。しかしへんな文字を入れるとすぐにこのリンク自体が表示されなくなってしまう。

がんばれば普通にXSSできるのではないか?と思ってORでつなげたりしてみたが、残念ながらイコール(=)がエンコード(%3D)されてしまった。


ひょっとしてこれ、気づいている人はたくさんいて、攻めあぐねていたんじゃないだろうか?

2011年4月28日木曜日

sendmailでGmailみたいに複数アドレス使えるようにする方法

Gmailではメールアドレスのアカウント部分に+(プラス)文字を入れると、それ以降の文字列が無視されて、複数アドレスが使えるっていう機能がある。
つまりhoge@gmail.comっていうアドレスを持っていれば、hoge+moge@gmail.comでもhoge@gmail.comで受け取れるというもの。
例えば、この機能を利用して登録サイト毎にメールアドレスを変えておけば、メールアドレスが漏洩してダイレクトメールが来るようになったときに、どのサイトから漏れたかがわかる。

ってこんなの自宅サーバでも会社でも、とっくの昔にやってたぜってことでその設定方法を紹介しておく。ただし、うちのやり方ではデリミタは+(プラス)ではなく.(ドット)。

/etc/mail/sendmail.cfに次のような行がある。
R$+ < @ $=w . >         $#local $: $1                   regular local name
これは、ホスト/ドメイン名付きのメールアドレスをローカル判定する部分なので、この上に
R$+ . $+ < @ $=w . >            $#local $: $1           regular local name
という行を追加。
あとローカル上でのアカウント部だけの判定部分
R$+                     $#local $: $1                   regular local names
っていう行があるので、同様にこの上に
R$+ . $+                $#local $: $1           regular local names
という行を追加する。
要は「regular local names」ってコメントされている行の上に、パターンに「. $+」を追加したルールを増やせばよい。

で、sendmailを再起動するなりSIGHUPを送るなりすれば、
hoge.moge@hoge.jpもhoge.psn@hoge.jpもhoge@hoge.jpに送られるようになる。

※当然だが、sendmail.mcからsendmail.cfを新たに生成した場合、この修正は上書きされる。

2011年4月2日土曜日

Ubuntu10.10にGNU Prolog1.3.1

今時GNU Prologのインストールなんて「Ubuntuソフトウェアセンター」でバイナリをダウンロードすればいいだけの話だが、昔ながらのUNIXプログラマはソースからコンパイルするのです。

gplc -c --fast-math fd2c.pl
fd2c.pl:215-220: fatal error: exception raised: error(instantiation_error,sort/2)
compilation failed
make[1]: *** [fd2c.o] エラー 1

だめじゃん。orz...

このエラーメッセージそのままコピペしてぐぐったら、すぐにこのページが引っかかった。
http://comments.gmane.org/gmane.comp.gnu.prolog.general/1170

ってことで、
./configure --with-c-flags='-O3 -fomit-frame-pointer -fno-strict-aliasing'
でコンパイルは通る。

2011年3月18日金曜日

RTA55iでPPTPパススルー

もう古い機種だからか、「RTA55i+VPNパススルー」で検索してもなかなか情報がなく、「RTA55i+PPTPパススルー」でようやく参考になるページを見つけた。
http://blog.goo.ne.jp/takuminews/e/b0278679e6c849b8a7c16710e4230dd6
これを参考に、「付加機能」→「ファイアウォール機能」で、「表示インターフェース」を「WANポート(LAN2)」に切り替えて、「静的フィルタの設定」でフィルタ追加。

フィルタ:pass(ログなし)
プロトコル:gre
送信元IPアドレス:PPTP接続先のグローバルアドレス
受信先IPアドレス:内部ローカルアドレス(192.168.100.0/24)
送信元ポート番号、受信先ポート番号:*

フィルタ番号40で追加後、「適用」の「入」にチェックを入れて「静的フィルタと動的フィルタの適用」の「適用」クリック。

ファームウェアのリビジョンは、Rev.4.06.67。

2011年3月7日月曜日

Google Code Jam Japan 2011

http://code.google.com/codejam/japan/
こっちも参戦するす。またお金ください>ぐーぐるさん

9位以内入賞はまずムリだけど。

2011年2月24日木曜日

CentOS5.5にmod-chxj_0.12.37

結構ハマったわりにはネット上に情報がないので、書いておく。

前提は、openssl, apacheはソースからコンパイルしたものを使い、それぞれopenssl0.9.8oが/home/opensslに、apache2.2.16が/home/httpdにインストールされている。
あちこちで書かれている前提パッケージはすでにインストール済みとする。

まず引っかかったのは、make。

$ ./configure \
> --with-apache-header=/home/httpd/include \
> --with-apxs=/home/httpd/bin/apxs \
> --with-apr-config=/home/httpd/bin/apr-1-config \
> --with-apu-config=/home/httpd/bin/apu-1-config \
> --with-openssl=/home/openssl


でconfigureするとmakeで、

(cd serf; CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --enable-shared --with-apr=`dirname /home/httpd/bin/apr-1-config` --with-apr-util=`dirname /home/httpd/bin/apu-1-config` --with-openssl /home/openssl; make)
configure: WARNING: you should use --build, --host, --target
configure: WARNING: invalid host type: /home/openssl
checking for chosen layout... Serf
checking for working mkdir -p... yes
checking build system type... Invalid configuration `/home/openssl': machine `/home/openssl' not recognized
configure: error: /bin/sh build/config.sub /home/openssl failed

なんで/home/opensslでinavlid host typeとか言われるんだ?と思ってよく見てみると、src/serfの下でconfigureやってて、--with-opensslと/home/opensslの間に=がない。
直接src/Makefileの該当個所を変更して、コンパイルは通った。

インストールしてapacheを再起動すると今度は、
# ./apachectl restart
httpd: Syntax error on line 427 of /home/httpd/conf/httpd.conf: Cannot load /home/httpd/modules/mod_chxj.so into server: /home/httpd/modules/mod_chxj.so: undefined symbol: libiconv

0.8.5のインストール手順を参考にしてlibiconvをソースから入れたのがいけなかったらしく、最新はlibiconvは必要ないとのこと。

/usr/local/libの下のlibiconv関係のライブラリ(3つ)削除、/usr/local/includeの下のiconv関係のヘッダ(3つ)削除して、configureからやり直して、起動できた。

2011年2月1日火曜日

グーグル脆弱性報奨プログラム(まとめ) - グーグルからお金をもらう

今回は「ですます」調で。

日本語のセキュリティのページに書かれていないので、日本では気付いている人が少ないのかもしれません。
Googleでは昨年の11月から脆弱性報奨プログラム(Vulnerability Reward Program)というのをやっていて、同社のサービスに脆弱性を見つけて報告すると、報奨として$500から$3,133.7もらえます。

今後続く方のために、私がXSSを見つけた時の経緯を書いておきます。

最初、ショッピングにXSSを見つけて、すぐにSecurity Team(security@google.com)にメールしました。最初に見つけたものは、ページ表示後ユーザの操作によってスクリプトが動くものでしたが、その後同じサービス内の別のページで、ページ表示時点ですぐにスクリプトが動くものを見つけました。
この時点ではまだ、バグの管理番号が割り振られていませんでしたが、その後他のサービスで同じバグを見つけ、そのとき初めてちゃんとしたリストを作成し、管理番号の入ったサブジェクトのメールに返信しました。
これがちょっとしたトラブルの元になったようです。どうやら共通のバグであっても、サービス毎に管理しているようで、ショッピング以外のバグが見落とされそうになったそうです。

現に私の見つけたXSSは複数のサービスに共通にありましたが、報奨については別のバグとしてカウントされました。
しかも、共通で使われているフレームワークに元々の問題がありそうなので、すべてのサービスで共通に対応するだろうと思っていましたが、直し方はサービス毎にばらばらでした。

ですので、共通に存在するバグを見つけても、サービス毎に別のバグとして報告したほうがよさそうです。また一度報告してしまえば開発者は同じサービス内のその他の部分に気付くでしょうから、同じサービスをひと通り調べてから報告したほうが、とりっぱくれを少なくできると思います。彼らとしてもちまちま報告されるよりは、ある程度まとめてからの方が助かるでしょう。

報奨を受け取るには、
1.USDを受け取れる銀行に口座を持つ。
2.Googleのページでサプライヤ登録する。
3.W8-BEN form(米国源泉税を免除してもらうための書類)を作成、提出する。
ことが必要になります。これらはすべてメールで指示してもらえます。

1.については私の場合、もともとソニー銀行に口座を持っていたので、ここに振りこんでもらいました。
振りこんでもらうにあたり、銀行から被仕向送金手数料を取られます。ソニー銀行の場合$25なので、もし最低額の$500だと5%を取られることになります。私の場合2回に分けて振り込まれましたが、初回は被仕向手数料無料キャンペーン中だったので、1%で済みました。

2.は書くべき項目をメールで教えてもらえますが、受け取り口座は自分で調べて書く必要があります。私の場合不備があったようで、メールで足りない情報を確認されました。
受け取り銀行のSWIFTコード、中継銀行がある場合は中継銀行のSWIFTコードも必須のようです。ソニー銀行の場合、必要な情報はこのページに書いてあります。


3.は、ぐぐればフォーマットのPDFや書き込み例が出てきますので、特に問題ないと思います。


そして、バグが修正されて振込み手続きがひと通り完了したら、バグ情報をBlogに書いたりつぶやいていいそうです。
また、Google Security Hall of Fameページ(セキュリティの殿堂ページ?)に名前が載ります。どう書くか、どこにリンクさせるかは指定した通りにやってもらえました。

報奨を受け取られた他の方のご意見おまちしております。
 

2011年1月27日木曜日

Google Vulnerability Reward Program: XSS in Google Shopping, Maps and Blogs

There was a common XSS bug in Google Shopping, Maps and Blogs.
I don't describe it in detail because I think many web applications have same bug.

Shopping.
Maps.
Blogs.

I got $2,500 in total. Thanks Google.

2011年1月26日水曜日

グーグル脆弱性報奨プログラム(その3) - XSS in Google Blogs

これで最後、ブログ検索。
Shopping、Maps、Blogsで合計$2,500頂いた。
そろそろ新しいネタ探さないと。

2011年1月21日金曜日

グーグル脆弱性報奨プログラム(その2) - XSS in Google Maps

前回のShoppingと同じ手口。
「次へ」とかページ番号のリンクにonMouseOverで。
これで$500。㌧クス。>Google

2011年1月17日月曜日

グーグル脆弱性報奨プログラム(その1) - XSS in Google Shopping

昨年12月の始め、最近日本でサービスが開始されたグーグル・ショッピングにXSSを見つけた。商品リスト(/products)にはイベント駆動(onなんとか)でスクリプティング可能なものが1つ。商品情報(/products/catalog)では、ページ表示時点で可能なものが1つ、イベント駆動で可能なものが2つあった。

まずは商品リストから。
 画面上URLが短めだが、これはスクリプティングによって遷移先のURLが途中で切られてしまったことによる。ダイアログ表示後、表示されているURLに遷移している。

次、商品情報で遷移一発ででるもの。
 aikoで検索後、URL入力欄にスクリプトを仕込んだ。

続いて、 商品情報イベント駆動もの。

ダイアログすぐ上にある、「ショップ数」のリンクに細工をしてある。このリンクはUSのページにはないので、日本語のページで説明したところ、最初Security Teamは見つけられなかったらしい。UKのページにもあったので、これで説明した。

そして次は、逆にUSのページにしか見つけられなかったもの。

「Similar Items」のリンクに細工をしてある。

これらは共通のバグによって発生していて、個人的には「フレームワークの設計ミス」によるものだと考えている。
同じような問題を持つサイトがかなりあると思われるので、ここでは詳細については書かない。

実はこれと同じ問題は複数のサービスにあって、次回以降にレポートする予定だが、そのなかで「ヒント」くらいは出すかもしれない。

と、ここまでで$1,500頂いだ。ごっちゃんです。>Google