Как исправить уязвимость разделения HTTP-ответов с помощью ESAPI
После недавнего запуска findbugs (FB) он жалуется на: Безопасность - уязвимость расщепления ответов HTTP Следующий код вызывает это:
String referrer = req.getParameter("referrer");
if (referrer != null) {
launchURL += "&referrer="+(referrer);
}
resp.sendRedirect(launchURL);
По сути, параметр http "referrer" содержит URL-адрес, по которому при нажатии кнопки "Назад" в нашем приложении браузер возвращается. Он добавляется к URL в качестве параметра. После небольшого исследования я знаю, что мне нужно очистить URL реферала. После небольшого исследования я нашел проект esapi, который, кажется, предлагает такую функциональность:
//1st canonicalize
import org.owasp.esapi.Encoder;
import org.owasp.esapi.Validator;
import org.owasp.esapi.reference.DefaultEncoder;
import org.owasp.esapi.reference.DefaultValidator;
[...]
Encoder encoder = new DefaultEncoder(new ArrayList<String>());
String cReferrer = encoder.canonicalize(referrer);
Однако я не выяснил, как обнаружить, например, код jscript или другой материал, который не принадлежит URL-адресу реферера. Так как я могу достичь этого с помощью esapi?
Я старался:
Validator validator = new DefaultValidator(encoder);
validator.isValidInput("Redirect URL",referrer,"HTTPParameterValue",512,false);
Однако это не работает. Что мне нужно, это функция, которая приводит к:
http://www.google.com/ (хорошо)
http://www.google.com/login?dest=http://google.com/%0D%0ALocation: javascript:% 0D% 0A% 0D% 0Aalert (document.cookie) (не в порядке)
Или достаточно назвать следующее утверждение?
encoder.encodeForHTMLAttribute(referrer);
Любая помощь приветствуется.
3 ответа
Вот мое окончательное решение, если кому-то интересно. Сначала я канонизирую, а затем URL декодирую строку. Если существует CR или LF (\n \r), я просто отрезаю остальную часть этой потенциальной строки "атаки", начиная с \ n или \r.
String sanitize(String url) throws EncodingException{
Encoder encoder = new DefaultEncoder(new ArrayList<String>());
//first canonicalize
String clean = encoder.canonicalize(url).trim();
//then url decode
clean = encoder.decodeFromURL(clean);
//detect and remove any existent \r\n == %0D%0A == CRLF to prevent HTTP Response Splitting
int idxR = clean.indexOf('\r');
int idxN = clean.indexOf('\n');
if(idxN >= 0 || idxR>=0){
if(idxN>idxR){
//just cut off the part after the LF
clean = clean.substring(0,idxN-1);
}
else{
//just cut off the part after the CR
clean = clean.substring(0,idxR-1);
}
}
//re-encode again
return encoder.encodeForURL(clean);
}
Теоретически я мог бы позже проверить значение по регулярному выражению "HTTPParameterValue", которое определено в ESAPI.properties, однако ему не понравилось двоеточие в http://, и я больше не исследовал его.
И еще одно замечание после тестирования: большинство современных браузеров (Firefox > 3.6, Chrome, IE10 и т. Д.) Обнаруживают подобные уязвимости и не выполняют код...
Я думаю, у вас есть правильная идея, но вы используете неподходящий кодировщик. Значение заголовка Referer [sic] на самом деле является URL, а не атрибутом HTML, поэтому вы действительно хотите использовать:
encoder.encodeForURL(referrer);
Кевин
Я хотел бы предложить подход белого списка, где вы проверяете referrer
строка только для допустимых символов. Regex будет хорошим вариантом.
РЕДАКТИРОВАТЬ:
Класс org.owasp.esapi.reference.DefaultEncoder
использование вами на самом деле ничего не кодирует. Посмотрите на исходный код метода encodeForHTMLAttribute(referrer)
здесь, в grepcode. Типичная кодировка URL (кодирование возврата каретки и перевода строки) тоже не поможет.
Таким образом, дальнейшим шагом будет устройство логики проверки, которая проверяет правильность набора символов. Вот еще одна проницательная статья.
Принятый ответ не будет работать, если в строке есть "\n\r". Пример: если у меня есть строка:"This is str\n\rstr"
, он возвращается "This is str\nstr"
Исправленная версия принятого выше ответа:
String sanitizeCarriageReturns(String value) {
int idxR = value.indexOf('\r');
int idxN = value.indexOf('\n');
if (idxN >= 0 || idxR >= 0) {
if ((idxN > idxR && idxR<0) || (idxR > idxN && idxR>=0)) {
value = value.substring(0, idxN);
} else if (idxN < idxR){
value = value.substring(0, idxR);
}
}
return value;
}