（考察）JSカスタマイズで非表示や編集不可にしたkintoneのフィールド、実は編集できちゃう問題

## この記事の目的
kintoneのレコード編集画面は、JavaScriptカスタマイズによってフィールドを非表示にしたり変更不可にすることが可能となっています。特定のフィールドを編集させたくない場合に使われている方も多いかと思います。

しかし、この機能は見た目上は非表示になったり編集不可になっていますが、実は比較的簡単に突破して編集することができます。

この問題について技術面と運用設計の両面からの考察を行います。

## JavaScriptカスタマイズによる非表示や編集不可の実装について
方法を説明するためのサンプルアプリとJavaScriptを作成します。

### サンプルアプリ
1. 実験のためフィールドを2つだけ用意した簡単なアプリを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/91287de5-94c5-3597-a970-73eac844ba83.png)

1. 作成したアプリに非表示と編集不可のJavaScriptカスタマイズを組み込みます。

```hideAndDisable.js
(() => {
    kintone.events.on([
        'app.record.create.show',
        'app.record.detail.show',
        'app.record.edit.show',
    ], (event) => {
        kintone.app.record.setFieldShown('非表示テスト', false);
        event.record.編集不可テスト.disabled = true;
        return event;
    });
})();
```

### サンプルアプリを動かしてみる
1. レコード編集画面の見た目はこうなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/5f2a145d-7d99-0af0-b77a-0d31a8b30592.png)
非表示になってるし、編集不可になってる！ヨシ！

1. 実は、ヨシ！ではない
Chromeの検証ツールなどでElement情報を表示して編集不可にしたフィールドを見ると、DOM上は存在していることがわかります。よく見ると`disabled`という属性が指定してあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/38324335-df40-2916-6177-e02e8bb8b0b0.png)
そして、その上の方を展開していくと非表示にしたフィールドもしっかり存在しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/0f0233be-2403-f3fc-9e10-2caaf8019b3d.png)

1. さらに
非表示にしたフィールドはアプリの構造を知っている人じゃないと場所を特定しにくいものですが、kintoneのJavaScriptカスタマイズに少し詳しい人ならConsoleからこんな風に打ち込んで調査できちゃいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/fdcf4888-bb9c-1a45-430c-4287fd623fc3.png)

### 突破して編集する方法(1)
DOMの属性を書き換えればフィールドは編集可能になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/5b723d67-de23-1cce-f372-682366a8592c.png)
上図のようにinputタグの`disabled`属性を消せば、下図のように普通のフィールドの見た目になってそのまま値を編集できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/2cc3c660-17ca-849e-39ac-4a17e1a09fcb.png)
非表示フィールドについても、CSSのプロパティを直接編集すると見えるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/77adf962-4e0b-b676-fec0-9028a4f0a19f.png)
CSSを書き換えると下図のようになり、編集できる状態になってしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/c68ac773-5b30-f25a-0f19-7a92edfaee77.png)

### 突破して編集する方法(2)
JavaScriptをConsoleから実行して値を書き換えることもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/a2bb60d0-4db0-7901-db9b-a7968eb084b3.png)
↑こうすると、↓こうなる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125488/75270704-a007-4d51-74de-d233ab5a2efa.png)


## 回避策の考察
### kintoneの編集画面で防ぐには？
ブラウザの仕様上、上記のような編集方法を完全にブロックすることは不可能で、考えれらる回避策はいずれも弱点があります。

### 対策案(1) DOMを消す
CSSやdisabled属性を消して編集できないようにするための方法として、該当のDOMを消してしまうということが考えられます。
* 非表示にするフィールド: querySelectorで特定してElementを削除する
* 編集不可にするフィールド: querySelectorで特定してElementを削除し、代わりにラベルのDOMを作って表示する

しかし、この方法では[突破して編集する方法(2)](#突破して編集する方法2)に書いた`kintone.app.record.set()`を強制的に実行する方法には対応できません。

### 対策案(2) 保存時にチェックする
次に、保存時のイベントハンドラでチェックする方法で防ぐ方法が考えられます。
```check.js
(() => {
    // 非表示のみ実装
    kintone.events.on([
        'app.record.create.submit',
    ], (event) => {
        if (event.record.非表示テスト.value) {
            alert('非表示項目が編集されていますが破棄して保存します。');
            event.record.非表示テスト.value = '';
        }
        return event;
    });

    // 編集不可のみ実装
    kintone.events.on([
        'app.record.edit.submit',
    ], (event) => {
        const client = new KintoneRestAPIClient();
        return client.record.getRecord({
            app: kintone.app.getId(),
            id: event.record.$id.value,
        }).then((result) => {
            if (result.record.編集不可テスト.value !== event.record.編集不可テスト.value) {
                // 注意)この実装だと前のバージョンが不正に更新されてた場合にダメ
                alert('編集不可項目が編集されていますが破棄して保存します。');
                event.record.編集不可テスト.value = result.record.編集不可テスト.value;
            }
            return event;
        });
    });
})();
```

しかし、この方法もCSVファイルの読込みや、外部からのkintone REST API実行によって突破されてしまいます。

### そもそも、本来は権限設定で対処すべき
そもそも、kintoneには**利用者IDとパスワードを使って外部からkintone REST APIを実行できる**という[仕様](https://developer.cybozu.io/hc/ja/articles/201941754)が定義されていますので、画面をどう小細工しても回避することはできません。
根本的な解消には、レコードやフィールドのアクセス権をかなり細かく設定する必要があります。

## アプリ設計の基本戦略とJavaScriptカスタマイズの使い所
じゃぁどうするか。ここからは設計の話になります。

### 基本戦略
まず、権限を解放しているフィールドに対しては完全に編集や表示をブロックすることはできない、ということをしっかりと受け入れます。`権限設定`という言葉の通り、権限を設定しているのは管理者自身です。

公開したくない情報や編集されると困る情報などはJavaScriptの小手先で対応しないというのが基本的な戦略となります。

### JavaScriptの使い所
前提を受け入れた上で、操作する上で見えてなくてもいい情報を隠蔽したり、誤操作を防ぐために編集を不可にするなどの用途で利用するのが本来の使い方です。

### 諦めてJavaScriptカスタマイズを使う(1)
「想定外の更新は監査でチェックする」と決めて諦めた上で、編集画面をJavaScriptでカスタマイズし、念の為、保存時にイレギュラーな編集をされていないかをチェックするというアプローチも十分考えられます。

この時、保存時のチェック以外に外部プログラムを使ったデータチェックをちゃんとやることが重要です。定期的に監査ログやレコードデータをチェックし、想定外のAPIリクエストや更新処理が発生していないことを監査します。これらの監査を目視だけでやるのは限界があるため、チェック用のスクリプトを開発するなどの必要が出てくることでしょう。

### 諦めてJavaScriptカスタマイズを使う(2)
JavaScriptカスタマイズによる非表示と編集不可の実現はある程度低コストで実現できるので、「わかっちゃいるけど使いたい」というケースは多いものです。お手軽に実現したいがチェック処理を作るのもコスト的に合わないという場合は、不正な編集へのプログラム的な対策は諦めてその他の業務ルールでブロックするという方法が考えられます。

例えば、社内kintoneの利用規定に「提供されている方法以外でデータを編集した場合懲戒対象とすることがある」と明記するだで十分な効果を発揮するかもしれません。

## まとめ
* JavaScriptカスタマイズで非表示や編集不可にしても突破される
* 本来は権限をちゃんと設定して回避するべき
* お手軽カスタマイズでいくなら、運用やルールで対策する