Использовать одноразовый сессионный ключ, чтобы форсить сценарий:
«сначала запроси страницу, и только потом — json-данные».
При генерации страницы, на сервере создаётся ключ, который записывается в сессию (на сервере) и в код страницы.
При запросе данных из страницы, с запросом отправляется и этот ключ, где сравнивается с записанным в сессию. Только при совпадении отдаются данные, а ключ удаляется из сессии.
Чтобы не передавать данные в открытом виде, можно их дополнительно упаковать-зашифровать. См.
JSON Web Tokens. Но это чисто декоративно, т.к. всё, что оказывается в итоге в открытом виде в браузере клиента – считай, утекло.