Salesforce で、送信されたお問い合わせが簡単に消えてしまう話

Salesforce の Web-to-ケースで reCAPTCHA を有効にした場合[^1]、ユーザの時計が1分ほど遅れているだけで、ケースが消えてなくなります。特にエラーなどは表示されず、ただ消失してしまうようです。Web-to-リード も同様です。

以下の手順で簡単に再現します。

## 再現手順

### 1. Salesforce にログインする

アカウントがない場合は [Developer Edition](https://developer.salesforce.com/jpblogs/2016/04/developer-edition-signup/) でアカウントを作成します。

### 2. Web-to-ケースのセットアップを行う

ドキュメントのとおりに Web-to-ケースをセットアップします。

 * [Web-to-ケースの設定](https://help.salesforce.com/s/articleView?id=sf.setting_up_web-to-case.htm&type=5)
 * [reCAPTCHA の有効化](https://help.salesforce.com/s/articleView?id=customizesupport_web_to_case_enable_recaptcha.htm&type=5&language=ja)

[reCAPTCHA キー](https://www.google.com/recaptcha/admin/) (v2 checkbox または invisible) を Salesforce に登録し、HTML を生成します。送信完了ページには適当な URL を設定すれば OK です。

[reCAPTCHA 設定](https://www.google.com/recaptcha/admin/)には、`localhost` など、テストに使うサーバのドメインを追加しておいてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/6f263d17-254e-e5f6-8839-e5068fdf2e81.png)

### 3. 正常にケースを生成できることを確認する

`localhost` など reCAPTCHA キーに登録したドメインで生成した HTML を開き、お問い合わせを送信します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/f69a79b1-4080-09cc-4872-4b537ecda8b7.png)
送信完了画面にリダイレクトし、Salesforce にケースが作成されるのを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/3195b97e-781e-ac2d-d1b7-4511e119680a.png)

### 4. 時計を遅らせて、ケースが消失するのを確認する

次に、手元の時計を1分以上遅らせて、同じように送信し、送信完了画面にリダイレクトするのに、Salesforce にはケースが作成されていないことを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/11507686-e306-16e5-8a75-18c85715e7fc.png)

生成された HTML で、`captcha_settings` の `ts` 値に時刻を書き込んでいる以下の箇所を書き換えることでも再現できます。

```js
// 修正前
elems["ts"] = JSON.stringify(new Date().getTime());

// 修正後。ts 値を 90 秒遅らせる
elems["ts"] = JSON.stringify(new Date().getTime() - (1000 * 90));
```

## 原因について

Salesforce は、`captcha_settings` という値で、reCAPTCHA の設定を送信しています。

```html
<!-- Salesforce が生成する HTML より引用、JSON の値を修正 -->
<input type=hidden name='captcha_settings' value='{"keyname":"reCAPTCHA キー名","fallback":"フォールバックするかどうか","orgId":"組織ID","ts":"タイムスタンプ"}'>
```

JavaScript が 0.5 秒ごとにこの JSON をパースし、`ts` 値をローカルのエポックタイムに設定、再び JSON としてエンコードして、`captcha_settings` に書き戻す、という処理を行っています。(この時点で、既にだいぶ微妙な感じはありますが)

```js
// Salesforce が生成する HTML より引用
 function timestamp() { var response = document.getElementById("g-recaptcha-response"); if (response == null || response.value.trim() == "") {var elems = JSON.parse(document.getElementsByName("captcha_settings")[0].value);elems["ts"] = JSON.stringify(new Date().getTime());document.getElementsByName("captcha_settings")[0].value = JSON.stringify(elems); } } setInterval(timestamp, 500);
```

そして、Salesforce サーバは、`ts` 値が遅れていると、黙ってケース・リードを破棄する、という不思議な実装になっているようです。

サポートともやり取りをさせていただき、詳細を書くのは避けますが、これが設定の不備などではないのは間違いないようです。

### タイムスタンプは reCAPTCHA 仕様で必要？

reCAPTCHA の [Verification API](https://developers.google.com/recaptcha/docs/verify) を見なおしましたが、このような制約は必要無いと思います。

API で必須のパラメータは、シークレットキーとクライアントから送られてきたトークンの2つだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/520cccc5-48a1-4ec4-e0aa-47811a6a1284.png)
API レスポンスには `challenge_ts` というトークン取得時のタイムスタンプが含まれています。仮にもしこの値を独自に検証するのだとしたら (それも必要ないと思いますが) サーバ時刻を使うのが適切でしょう。クライアント側で自由に書き換えられる値を使う意味がわかりません、、。

Google が Salesforce に向けて、独自に非公開の Validation API を提供している可能性はありえなくはないと思いますが、普通に使うよりも品質が下がっていますし、考えにくいのではと思います。

## Salesforce の reCAPTCHA 実装の品質について

数年前までは、`captcha_settings` 値を**送信しないことで**、reCAPTCHA の検証を回避できる問題がありました。

 * https://trailblazer.salesforce.com/ideaView?id=0873A000000lFDlQAM

:thinking: 

## なんとかしてください

このような制約は不要なはずです。もし本当に必要なら、詳細をドキュメントで説明してください。

Ideas というところにも書きました。(よかったら vote してください)

 * [Case is lost when the reCAPTCHA is enabled and the local clock is 1minute behind](https://trailblazer.salesforce.com/ideaView?id=0874V000001HmBSQA0)

## 代替

単に問い合わせを受けられるだけでよいなら、[Kwes Forms](https://kwesforms.com/) などのフォームサービスもあります。

 * [How to Handle Forms on Your Jamstack Website?](https://bejamas.io/blog/how-to-handle-forms-on-jamstack/) 
 * [Services | Forms | The Power of Serverless](https://serverless.css-tricks.com/services/forms/)


[^1]: [スパムケースを防止するための reCAPTCHA の有効化](https://help.salesforce.com/s/articleView?id=sf.customizesupport_web_to_case_enable_recaptcha.htm&type=5) に「Winter '19 より後に作成された組織では、この設定はデフォルトで有効になっています。無効にできますが、それはお勧めしません」とあり、デフォルトで有効になっているものと思われます。が、アカウントを新規に作成して確認はしていません。