独創アプリ開発日記 100日目 不正文字チェック

あれ?いつの間にか100日経ってた・・・

本命のアプリ作る前に、ちょっとだけテキスト漫画サイトをガワネイティブアプリ化する作業を行おうと思ったら、こっちがメインになってしまいました。

とは言ってももう一息で完成しそうです。今日は絵文字を入力すると正常動作しない箇所があるので対策をしました。

絵文字を入力可能とするには

・DB(MariaDB)の文字コード変更(utf8mb4に変更)
・JSの文字処理で”サロゲートペア”に対応する

の2つを行う必要があります。

しかし絵文字は各環境で同じものが出るわけではありません。iPhoneで入れた絵文字って、Androidで同じように出ないんですよ。知っていました?え?知ってる?まじで?

・・・

・・

やろうと思えば絵文字の対応は可能ですが、混乱を避けるため、泣く泣く入力出来ないよう、JSで以下のように入力禁止対応をしました。


function isContainSP(s) {
    for (var i = 0; i < s.length; i++) {
        var c = s.charCodeAt(i);
        if ((0xD800 <= c && c <= 0xDBFF) || (0xDC00 <= c && c <= 0xDFFF)) { 
            return true; 
        }
    }
    return false;
}

・・・

・・

え?手抜き?

いや、絵文字が各環境で異なるので、混乱を避けるためです・・・

まじで。

独創アプリ開発日記 99日目 ID不正変更対策

今日はID不正変更対策を行っていました。

今作っているガワネイティブアプリはユーザID変更機能があります。

ユーザIDを変更できる機能があるという事は、IDを試しまくって入力していればいつかは他人のIDでログインできてしまうという事です。この力任せの総当たり攻撃の不正をブルートフォースアタックといいます。

対策は100億通りくらいありますが書ききれないです。しょうがないので3つくらいの対策を紹介します。

1.不正な入力があったら、その応答に時間をかける。あまりに長い時間待たせるとID変更しようとして単に間違えた人にも迷惑がかかるため、1、2回目の間違いは3秒くらい、3、4回以上間違えたら10秒待たせるとか工夫すると良いです。
2.同一アクセス元から短時間に高頻度のアクセスがあったら遮断する。
3.後は・・・たぶんなんかあります。

テキスト漫画のガワネイティブアプリは、1の方法を採用し、対策を行いました。

独創アプリ開発日記 98日目 ID変更処理の実装完了

本日は、昨日の続きです。

1.Web側で、URLに特定のリクエストパラメタを乗せてサーバアクセスする。
2.アプリ側で、画面遷移のタイミングでURLをチェックする。
3.2で、特定のリクエストパラメタを見つけたら、その内容を元にアプリ側処理を実行する。

昨日は1と2が完了したので、3を行いました。

アプリ内の”UserDefaults”や”キーチェーン”にユーザIDを保存しているのですが、これらをWebから書き換えることに成功しました。

昨日作成した”getUrlParam”を使い、コードは以下のようになりました。


    // IDの更新
    if let id: String = getUrlParam(url: url.absoluteString, param: "特定のパラメタ名") {
        // UserDefaultsに保存
        UserDefaults.standard.set(id, forKey: Constants.User.idfv.rawValue)
        // キーチェーンに保存
        KeychainService.savePassword(service: _ksService, account: _ksAccount, data: id);
    }

Constants.User.idfv.rawValue とか _ksService とか _ksAccount とかはそれぞれの環境で適切な値を指定して下さい。

今回で基本的な部分はかなり完成してきました。ただ、まだ細かい詰めの作業が残っています。

テキスト漫画サイトのガワネイティブアプリなんて、2,3日でサクッとリリース出来るかと思ったらとんでもなかったです・・・

残作業は以下です。

・絵文字を入力すると正常動作しない箇所があるので対策
・IDを総当たり攻撃で不正変更される対策
・各メニュー(1〜3)での登録/変更テスト
・ボタンやリンク押した時に音を出す
・秘密の作業

独創アプリ開発日記 97日目 url解析

本日はWeb(WKWebView)からアプリへのアクセス処理を行う予定でした。

Webからアプリへの処理依頼は以下の手順で行います。

1.Web側で、URLに特定のリクエストパラメタを乗せてサーバアクセスする。
2.アプリ側で、画面遷移のタイミングでURLをチェックする。
3.2で、特定のリクエストパラメタを見つけたら、その内容を元にアプリ側処理を実行する。

今回は特定のリクエストパラメタが指定されているか、URLを解析する処理を作りました。

以下がコードとなります。Swift4.0.3です。最新ですよ。Webで調べても古くて動かないコードばかりで苦労しました・・・

オプショナル型とかオプショナルバインディングとか強制アンラップとか復習してコーディングしました。


    func getUrlParam(url: String, param: String) -> String? {
        if let urlComps: NSURLComponents = NSURLComponents(string: url) {
            if let urlParamsCount: Int = urlComps.queryItems?.count {
                for i in 0..<urlParamsCount {
                    if let item: URLQueryItem = urlComps.queryItems?[i] {
                        if param == item.name {
                            return item.value
                        }
                    }
                }
            }
        }
        return nil
    }

オプショナル型は一度分かってしまえば便利ですね。
毎回nilチェックしていた日々が過去の思い出となりました。

独創アプリ開発日記 96日目 ID変更処理

今回はID変更処理を実装しました。

IDを参照/変更可能とすることで、以下の用途などに利用出来ます。

・新しいiPhoneなどに変えた場合、前の設定を引き継ぐ
・複数端末で同一IDを使って同じユーザとしてアプリを使う
・将来的に、そのIDがあればアプリと同じユーザでPCサイトを使える
・将来的に、AndroidとiPhone間で同じユーザとしてアプリを使える

処理内容は昨日のニックネーム変更とほぼ同じため、すんなり行くかと思いました。
が、1つだけ作業が残りました。

iOSではidfvという識別IDを初回アプリ起動時に取得・保存し、次回アプリ起動時も保存したIDを元にWebサイトにアクセスすることでユーザを識別しています。

本日の作業ではWebサイト内でこのIDを変更する処理を行ったため、ID変更後にアプリを落としてしまうと、アプリ再起動時に保存しておいたIDを読み込んでIDが元に戻ってしまいます。

対処としては、WebサイトでID変更を行った際に、アプリに保存したIDも同時に変更します。ガワネイティブアプリにおけるよくあるパターンで、Webサイト内からアプリ側へのアクセスを行う処理が必要になります。

明日はこの、Web(WKWebView)からアプリへのアクセス処理を行う予定です。

独創アプリ開発日記 95日目 ニックネーム変更処理完了

ニックネーム変更処理で唯一未実装だったサーバ側のユニークチェックですが、結局ajaxを使って実装を済ませました。

クラアント側はjQueryを、サーバ側はphpを使っています。最終的にコードは以下のような形になりました。

ajax使ったとか言いつつ async:false で同期処理にしています。なんかカッコ悪いな・・・

クライアント側:


    function change_nickname() {
        bExit = false;
        nickname = ""
        while(!bExit) {
            nickname = window.prompt("新しいニックネームを入力してください", nickname);
            
            if (null == nickname) {
                // キャンセル
                bExit = true;
            } else {
                nickname = jQuery.trim(nickname);
                if (nickname.length < 3 || 30 < nickname.length) {
                    alert('ニックネームは3文字以上30文字以下で入力して下さい。');
                } else if (nickname.indexOf(' ') != -1 || nickname.indexOf(' ') != -1) {
                    alert('ニックネームにスペースを含めることは出来ません。');
                } else {
                    var exist = $.ajax({
                      url: "ncc/" + encodeURIComponent(nickname),
                      async: false
                     }).responseText;
                    if ('1' == exist) {
                        alert('すでに存在するニックネームです。違うニックネームを入力して下さい。');
                    } else if ('0' == exist) {
                        bExit = true;
                        $("#formNickName").submit();
                    } else {
                        bExit = true;
                        alert('予期しないエラーです。お手数ですがもう一度やり直して下さい。');
                    }
                }
            }
        }
    }

サーバ側:


〜 略 〜
class UserController extends Controller
{
    public function ncc(Request $request, $nickname) {
        try {
            $nickname = rawurldecode($nickname);
            〜 略:ここでDBアクセスしてニックネームの重複チェック。存在すれば return '1' 〜
            return '0'; // 重複してなければ 0 を返す
        } catch (Exception $e) {
            〜 略:エラー処理 〜
        }
    }
〜 略 〜

Lumenの設定ファイル:


〜 略 〜
$router->get('ncc/{nickname}', 'UserController@ncc');
〜 略 〜

独創アプリ開発日記 94日目 ニックネーム変更

今日は”そのほか”画面のニックネーム変更処理を一部を除き実装しました。

入力チェックって結構労力使いますよね。入力項目が何百とかあればバリデータとか使うんですが、1、2個ですし、画面構成が結構特殊なので自力で実装しています。

ニックネームの場合、文字数チェックはjsだけで出来るのですが、ユニークチェックがサーバ処理になるため、一旦画面遷移が入ってしまいます。ここら辺をどう綺麗に実装しようかと悩んでいました。ajax使うのもなぁ・・・

独創アプリ開発日記 93日目 Q&A完成

今日はQ&Aのページとサポートサイトへのリンクを作成しました。

スマホ版のQ&Aは以下のようになりました。

 

Q&Aのページにはテキスト漫画が配置され、動きます!

サポートサイトへのリンクはSafariで開きます。ガワネイティブアプリ(WKWebView)から以下のようなコードを書くことでSafariを起動してサイトを表示することが出来ます。

swiftのバージョンは4.0.3です。swiftのバージョン頻繁に変わりすぎ・・・。Safariで開く方法調べても、ほとんどが古いコードで使えなかったです。死ぬほど苦労して作成したコードは以下の通りです。

このコードを利用された方はテキスト漫画のサイトでテキスト漫画を1つ登録してくれると嬉しいなあ・・・。作りかけっぽいのを完成させるだけでも良いです。


class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate{
〜略〜
        webView.uiDelegate = self
        webView.navigationDelegate = self
〜略〜
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        
        guard let url = navigationAction.request.url else {
            decisionHandler(.cancel)
            return
        }
        // サポートサイトへのリンクか判定
        if url.absoluteString.range(of: "support/textmanga") != nil {
            // Safariで開く
            UIApplication.shared.open(url, options: [:], completionHandler: nil)    // iOS 10以降
            decisionHandler(.cancel)
            return
        }
        decisionHandler(.allow)
    }

独創アプリ開発日記 92日目 そのほか画面の追加

テキスト漫画サイトのアプリ版について、当初はメニュー画面から”つかいかた”画面にリンクする予定でした。

しかし2段階で画面が構成されると操作手順が増えてしまうので、以下のようにタイトルの”つかいかた”へのリンクを”そのほか”にして

“そのほか”の画面に、以下のようにIDやニックネームの変更、QAなどへのリンクを配置するようにしました。

尚、IDは端末を引き継ぐ時などに利用出来ます。ニックネームは作ったテキスト漫画を他の人に見てもらう際に表示されます。

また、従来の”つかいかた”画面に存在した”コピーのしかた”や”編集のしかた”は説明不要と判断して追加しないか、もし追加するとしてもQA内で説明することとしました。

独創アプリ開発日記 91日目 表示モード完成

本日は昨日の構想を元に実装しました。スマホユーザは自動ログインし、3つの表示モードを使えるようになります。

1を選ぶと自分専用のテキスト漫画が表示されます。まだ1つも作っていません。

2を選ぶと誰でも編集可能なテキスト漫画が表示されます。

3を選ぶと他の人専用のテキスト漫画が表示されます。コピーして使うことは出来ます。