Эта статья морально устарела :( . Приглашаю продолжить ваше знакомство с Salesforce на нашем Форуме!
Есть в web dev распространенный тип задачи, как вывод пользовательских данных на странице. И мы не можем гарантировать что пользователь предоставит нам безопасные данные, поэтому должны предусмотреть какой-нибудь механизм защиты от "плохих" данных. Данный тип уязвимости называется XSS - это случай когда при определенных обстоятельствах пользовательские данные, содержащие, например, javascript становятся частью страницы и могут быть выполнены.
Теперь ближе к Salesforce. По умолчанию все данные, выводимые в Visualforce странице с помощью formula expression (конструкция вида {!...}) автоматически экранируются, тем самым превращая все спецсимволы в безопасные html entities, которые воспринимаются браузерами как простой текст. Для большинства задач этого хватает.
Но есть задачи когда надо вывести кусок HTML, который предоставил нам пользователь, как часть страницы. Самый простой и опасный способ вывести переменную как часть HTML это воспользоваться конструкцией:
<apex:outputText value="{!ourVariable}" escape="false" />
Вот в escape="false" кроется вся проблема. Я могу передать в ourVariable следующую строку
Hello Doom <script>alert('Can you see me?')</script>
И получить выполнение кода внутри тега script. Это одна сторона медали. Вторая сторона медали в том, что даже если вы придумаете супер проверку на стороне apex перед выводом переменной, все равно будете долго объяснять Security Team зачем вам нужен escape="false" при прохождении Security Review и большая вероятность что у вас не получится его пройти.
Недавний практический пример показал, что товарищи из SF крайне против использования escape="false" в пакете и более того не сильно хотят возиться и разбираться в кастомных решениях по фильтрации (sanitize) пользовательского html перед выводом. Но чем они смогли помочь по теме вопроса, так это дали очень интересный рецепт, который для них (Security Team) является приемлемым.
Решение от Security Team:
<apex:inputTextarea value="{!ourVariable}" richText="true" disabled="true" />
Имеем наш кусок html, который действительно выводится как часть страницы. НО наш html находится внутри salesforce виджета Rich Text Editor. Вторая часть решения - это с помощью css+js скрыть сам виджет но оставить наш кусок html.
На этом можно было остановиться. Решение рабочее. но как по мне достаточно кастыльное. Проблема в том что чистым css тут не обойтись, потому что наш html выводится внутри iframe, доступ к которому, чтобы применить стили, необходимо получать через js.
Я пошел немного дальше. Сам виджет - это ckEditor. Значит имеем что пользовательский кусок html выводится как и чистится средствами самого ckEditor. У данного редактора очень качественная документация, поэтому найти нужный функционал не составило труда.
Смысл моего решения:
По идее я мог бы подключить ckEditor библиотеку напрямую из Static Resources, но по-умолчанию настройки фильтра не пропускают ничего из html разметки и чтобы на выходе получить что-то больше чем просто текст, мне нужно вводить свои правила фильтрации. Вот для этого нам и нужен экземпляр редактора из Rich Textarea виджета.
Реализация
function sanitizeHtml (dirtyHtml) {
var ckInstance = CKEDITOR.instances[jQuery('[id$="ckEditorInstance"]').attr('id')];
var fragment = CKEDITOR.htmlParser.fragment.fromHtml(dirtyHtml);
var writer = new CKEDITOR.htmlParser.basicWriter();
ckInstance.filter.applyTo(fragment);
fragment.writeHtml(writer);
return writer.getHtml();
}
Это минимально рабочий код который можно использовать в сочетании с виджетом <apex:inputTextarea richText="true" id="ckEditorInstance" />
Конечно его можно улучшать, но я не буду здесь выкладывать код полностью, потому что это собственность чужого проекта. Скажу только что можно и нужно дополнить.
Что в итоге имеем? Избавились от escape="false" и используем стандартные правила очистки html от самого Salesforce. Я думаю Security Team будет довольно таким решением. Плюс моего подхода - все работает на уровне JS - чистим html сколько угодно и когда угодно. Например, делал отличный функционал, где пользователь может набрать свой html и тут же просмотреть его в режиме preview - не надо ничего перезагружать, обрабатывать на бэкенде - все тут же через JS чистится и показывается.