Base64でデコード出来ない文字をデコードしようとするとどうなるか?

調べても出てこなかったので、メモ。

何かわかりやすいエラーが出るのかとおもいきや、エラーとわかりやすい挙動はしなかった…orz

答えは『実装による』っぽい。 実際に平文→base64.decode→sha256でhash→base64.encodeの処理をOpensslとJavaの両方で試したら挙動に違いがった。

例えばOpenSSLだとこうなる

$ echo "aaaa" | openssl base64 -d | openssl sha -sha256 -binary | openssl base64
# u6nbXovptodruQ0AGBFeI/x0G6a/IyXn/PiO/tdQxMc=
$ echo "aaaa,,,," | openssl base64 -d | openssl sha -sha256 -binary | openssl base64
# 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=

aaaaの場合とaaaa,,,,の場合で結果が異なる。 ,base64で使用していない文字なのでデコード出来ない。 そのため、,が入るとエラーになって、その結果毎回同じ文字列が返ってきている(ように見える)。

試しにaaaaaaaa,も試してみる

$ echo "aaaaaaa," | openssl base64 -d | openssl sha -sha256 -binary | openssl base64
# 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=

やっぱり47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=になる。

なぜ47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=が返ってくるかはOpenSSLの中身を見ていないのでわからない。

Apache Commons Codecだとこうなる

package Base64Test;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base64;

public class Base64TestExecutor {
    public static void main(String[] agrs) throws NoSuchAlgorithmException{
        String target1 = "aaaa";
        String target2 = "aaaa,,,,";
        String result1 = testDigest(target1);
        String result2 = testDigest(target2);
        System.out.println("result1 : " + result1);
        System.out.println("result2 : " + result2);
        System.out.println("isEqual : " + result1.equals(result2));
    }

    private static String testDigest(String targetString) throws NoSuchAlgorithmException {
        
        byte[] testResult = Base64.decodeBase64(targetString);
        
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] digestVal = md.digest(testResult);
        String encPassword = Base64.encodeBase64String(digestVal);
        
        return encPassword;
    }
}

実行結果

run:
result1 : u6nbXovptodruQ0AGBFeI/x0G6a/IyXn/PiO/tdQxMc=
result2 : u6nbXovptodruQ0AGBFeI/x0G6a/IyXn/PiO/tdQxMc=
isEqual : true
ビルド成功(合計時間: 0秒)

こっちは中身を見てみた。

    /**
     * <p>
     * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once
     * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1"
     * call is not necessary when decoding, but it doesn't hurt, either.
     * </p>
     * <p>
     * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are
     * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in,
     * garbage-out philosophy: it will not check the provided data for validity.
     * </p>
     * <p>
     * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
     * </p>
     *
     * @param in
     *            byte[] array of ascii data to base64 decode.
     * @param inPos
     *            Position to start reading data from.
     * @param inAvail
     *            Amount of bytes available from input for encoding.
     * @param context
     *            the context to be used
     */
    @Override
    void decode(final byte[] in, int inPos, final int inAvail, final Context context) {
        if (context.eof) {
            return;
        }
        if (inAvail < 0) {
            context.eof = true;
        }
        for (int i = 0; i < inAvail; i++) {
            final byte[] buffer = ensureBufferSize(decodeSize, context);
            final byte b = in[inPos++];
            if (b == pad) {
                // We're done.
                context.eof = true;
                break;
            } else {
                if (b >= 0 && b < DECODE_TABLE.length) {
                    final int result = DECODE_TABLE[b];
                    if (result >= 0) {
                        context.modulus = (context.modulus+1) % BYTES_PER_ENCODED_BLOCK;
                        context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result;
                        if (context.modulus == 0) {
                            buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS);
                            buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
                            buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);
                        }
                    }
                }
            }
        }

        // Two forms of EOF as far as base64 decoder is concerned: actual
        // EOF (-1) and first time '=' character is encountered in stream.
        // This approach makes the '=' padding characters completely optional.
        if (context.eof && context.modulus != 0) {
            final byte[] buffer = ensureBufferSize(decodeSize, context);

            // We have some spare bits remaining
            // Output all whole multiples of 8 bits and ignore the rest
            switch (context.modulus) {
//              case 0 : // impossible, as excluded above
                case 1 : // 6 bits - ignore entirely
                    // TODO not currently tested; perhaps it is impossible?
                    break;
                case 2 : // 12 bits = 8 + 4
                    context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits
                    buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
                    break;
                case 3 : // 18 bits = 8 + 8 + 2
                    context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits
                    buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
                    buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
                    break;
                default:
                    throw new IllegalStateException("Impossible modulus "+context.modulus);
            }
        }
    }

final int result = DECODE_TABLE[b]; の箇所でDECODE出来ない文字の場合は-1が返される。 そのため、result >= 0falseになるので、その文字は読み飛ばされる。 その結果、例えばaa,,,の結果はおなじになる。

こういうのは実際に試して動作確認して、中身のソースまで見てみないとわかんないですね!

HTML/CSS用テキストエディタ導入メモ~SublimeText~

趣味アプリ作るのに、HTMLでMOCK作ると思います。

その時、Eclipseでも出来るんですけどHTMLとCSSだけ書くのに(あともしかしたらJavascriptも)いいエディタないかなと思って探してみたらSublimeTextというのを見つけました。

カスタマイズしなくても好きなPlugin適当に入れれば使いやすくなりそうなのでちょっと使ってみます。 で、参考にしたサイトをメモのためにペタペタ。

テキストエディタ選びに迷ったらコレ!SublimeTextがすごい | Code部

catcher-in-the-tech.net

WordPress高速化~オブジェクトキャッシュ編~

友人の会社のサイトが遅いというので、高速化を頼まれて以下を実施したところ劇的に改善した!のでメモっておく。 ※表示に10秒以上かかっていたのが3秒台にまで改善

topコマンドでリソース監視してみたところ、負荷かけたらCPUのwaitがかなり高かったのでCPUがボトルネックになってると判断したのでPHPのオブジェクトキャッシュを導入した。 ※たった2-3リクエストでwaitの値が増えちゃう…。WordPressってすごく重いですね。

PHPは5.3なので、apcというものになるらしい。 ※調べたところそれ以降のバージョンでは標準でopcacheってのがあるらしいのでINIの設定を追加するだけで使える。

参考にしたサイト www.webcreator-net.com

また、Apacheのチューニングもした。 やったのはtop -d1コマンド叩いてterminalのswapのusedの値見ながら、swapしないぎりぎりの設定を探った。

やっぱり、サイトがさくさく動くと気持ちいいですね。

WordPressの外観のカスタマイズが開けない

という、相談を友人から受けたので調査した。

その結果、Pluginの組み合わせが悪い時にこの現象が起きているようだ。 ソース読んでないので症状からの判断だが、

Page Builder by SiteOrigin && (Dynamic Widgets || WP Widget Cache) の条件の時に起きるみたい。 ※日本語でわかりやすく書くのがめんどくさいからJavaっぽい文法で表現してみた

あと、キャッシュPluginのせいで管理バーがサイトに表示されちゃってたので、 functions.phpadd_filter( 'show_admin_bar', '__return_false' ); の1行を追加して対応。

WordPress3.1からサイトにも管理バー表示できるようにしたらしいけど、キャッシュ系Pluginと相性悪いみたい。

ってな感じの調査をしてたら疲れたので今日はここまでなり。

さあ、本職のためにJavaの勉強するぞ!!っと。

CoreDumpの取り方(自分用メモ)

友人のサイトの一部のページがPHPがクラッシュ?しているか何かで落ちてしまって見れなかったので調査中 その時、coredump取る手順を調べたのでメモしておく

ulimit -aで、現在の設定を確認 coreの行が0だったら吐かれてない

ulimit -c unlimitedで一時的に吐かれるように設定する

httpd.confにログの出力先を設定して、gracefulする CoreDumpDirectory /tmp

すると、ログを設定したパスに出力されるようになる、はず ※というのも私の環境だと全然違うパスに出力されていた…。謎。

んで、 #gdb /usr/local/apache2/bin/httpd -c /tmp/core.30864 で、gdbでのデバッグ

whereで落ちた場所を特定(at何とかって出てくるはず)するが、 私の環境だと、無限ループっぽい…。(at何とかがでてこない)

参考にしたページ sarface2012.hatenablog.com

Linux環境設定/コアダンプを出力するようにする - Linuxと過ごす

別のアプローチで調べる必要がありそう。 さて、どうしようかなぁ。

※注意※ dump取り終わったら、ulimit -c 0で、設定オフにしないと、dump吐き続けます。かなり容量食うので、ディスク圧迫します。取り終わったら、オフにしましょう。 私の環境では数日で、8Gくらい溜まってました。