Обновление: некоторые студенты в области комментариев предположили, что они могут быть атакованы при получении IP-адресов через доменные имена.Спасибо за напоминание. Я не профессионал в области безопасности и мало что об этом знаю, поэтому мне очень стыдно.
Когда дело доходит до веб-безопасности, наш интерфейс может подвергаться воздействию XSS и CSRF. По работе я столкнулся с SSRF во внутреннем сервисе, за который отвечал, процесс обучения и решения буду описывать здесь. SSRF (Server-Side Request Forgery), то есть подделка запроса на стороне сервера, представляет собой уязвимость системы безопасности, созданную злоумышленником для формирования запроса, инициированного сервером. Как правило, SSRF-атаки нацелены на внутренние системы, недоступные из внешней сети.
Большинство причин формирования SSRF заключается в том, что сервер обеспечивает функцию получения данных от других серверных приложений и не фильтрует и не ограничивает целевой адрес. Например, получить текстовое содержимое веб-страницы с указанного URL-адреса, загрузить картинку по указанному адресу, загрузить и так далее. Злоумышленник может использовать сервер, на котором расположено приложение, для отправки HTTP-запроса, который злоумышленник хочет отправить в соответствии с потоком программы, и использовать эту уязвимость для обнаружения служб в производственной сети.Атакующий может быть напрямую проксирован во внутреннюю сеть. , позволяя злоумышленнику обходить доступ к сети.Контроль, загружать неавторизованные файлы, получать прямой доступ к интрасети и даже получать учетные данные сервера.
Во внутреннем веб-приложении есть интерфейс для скачивания файлов, за который отвечает автор/download
, который принимает параметр url, указывающий на адрес загружаемого файла, приложение инициирует запрос на адрес, загружает файл на сервер, на котором находится приложение, а затем выполняет последующую обработку. Возникает проблема: плацдармом здесь выступает сервер, на котором находится приложение, через этот интерфейс злоумышленник получает доступ во внутреннюю сеть и может выполнять множество вредоносных операций.
Опасности, вызванные SSRF, включают:
- Вы можете выполнять сканирование портов во внешней сети, во внутренней сети, где расположен сервер, и локально для получения баннерной информации некоторых служб;
- Атака на приложения, работающие в интрасети или локально (например, переполнение);
- Снятие отпечатков веб-приложений интрасети, доступ к файлам осуществляется по умолчанию;
- Атакующие веб-приложения на внутренние и внешние сети, в основном атаки, которые могут быть достигнуты с использованием получения параметров (таких как StrUTS2, SQLI и т. Д.);
- Используйте файловый протокол для чтения локальных файлов и т. д.
Общие решения:
- Отфильтруйте возвращаемую информацию. Проще всего проверить ответ удаленного сервера на запрос. Если веб-приложение должно получить файл определенного типа, проверьте, соответствует ли возвращенная информация стандарту, прежде чем отображать возвращенный результат пользователю;
- Унифицировать информацию об ошибках, чтобы пользователи не могли судить о состоянии порта удаленного сервера на основе информации об ошибках;
- Ограничьте запрашиваемый порт портом, обычно используемым http, например, 80, 443, 8080, 8090;
- Белый список внутренних IP-адресов. Избегайте приложений для получения данных интрасети, атакуйте интранет;
- Отключите нежелательные протоколы. Разрешены только запросы http и https. Может предотвратить такие проблемы, как file:///, gopher://, ftp:// и т. д.
По заявке автора/download
Адрес файла, запрашиваемый интерфейсом, является относительно фиксированным, поэтому используется метод внесения IP-адресов в белый список. Разумеется, автор почерпнул и более комплексное решение.Вот идеи коллег из отдела безопасности:
-
Ограничения протоколов (по умолчанию разрешены протоколы HTTP, HTTPS), 30-кратные переходы (30-кратные переходы по умолчанию не разрешены), унифицированная информация об ошибках (по умолчанию не унифицирована, унифицированная информация об ошибках позволяет избежать вредоносных атак за счет оценки информации об ошибках)
-
Оценка IP-адреса:
- Блокировать доступ к зарезервированным сегментам сети, таким как 0.0.0.0/8, 169.254.0.0/16, 127.0.0.0/8 и 240.0.0.0/4.
- Если IP-адрес 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, сегмент частной сети, запросите IP-адрес и оцените ответ.
contents-type
Этоapplication/json
-
Метод устранения несоответствия между получателем URL-адресов и анализатором URL-адресов: после анализа URL-адреса удалите пользователя и передайте RFC3986 и рекомбинируйте URL-адрес.
Далее идет код основной функции по работе с SSRF-уязвимостями версии Node.js, реализованный по изложенным выше идеям:
const dns = require('dns')
const parse = require('url-parse')
const ip = require('ip')
const isReservedIp = require('martian-cidr').default
const protocolAndDomainRE = /^(?:https?:)?\/\/(\S+)$/
const localhostDomainRE = /^localhost[\:?\d]*(?:[^\:?\d]\S*)?$/
const nonLocalhostDomainRE = /^[^\s\.]+\.\S{2,}$/
/**
* 检查链接是否合法
* 仅支持 http/https 协议
* @param {string} string
* @returns {boolean}
*/
function isValidLink (string) {
if (typeof string !== 'string') {
return false
}
var match = string.match(protocolAndDomainRE)
if (!match) {
return false
}
var everythingAfterProtocol = match[1]
if (!everythingAfterProtocol) {
return false
}
if (localhostDomainRE.test(everythingAfterProtocol) ||
nonLocalhostDomainRE.test(everythingAfterProtocol)) {
return true
}
return false
}
/**
* @param {string} uri
* @return string
* host 解析为 ip 地址
* 处理 SSRF 绕过:URL 解析器和 URL 获取器之间的不一致性
*
*/
async function filterIp(uri) {
try {
if (isValidLink(uri)) {
const renwerurl = renewUrl(uri)
const parseurl = parse(renwerurl)
const host = await getHostByName(parseurl.host)
const validataResult = isValidataIp(host)
if(!validataResult) {
return false
} else {
return renwerurl
}
} else {
return false
}
} catch (e) {
console.log(e)
}
}
/**
* 根据域名获取 IP 地址
* @param {string} domain
*/
function getHostByName (domain) {
return new Promise((resolve, reject) => {
dns.lookup(domain, (err, address, family) => {
if(err) {
reject(err)
}
resolve(address)
})
})
}
/**
* @param {string} host
* @return {array} 包含 host、状态码
*
* 验证 host ip 是否合法
* 返回值 array(host, value)
* 禁止访问 0.0.0.0/8,169.254.0.0/16,127.0.0.0/8,240.0.0.0/4 保留网段
* 若访问 10.0.0.0/8,172.16.0.0/12,192,168.0.0/16 私有网段,标记为 PrivIp 并返回
*/
function isValidataIp (host) {
if ((ip.isV4Format(host) || ip.isV6Format(host)) && !isReservedIp(host)) {
if (ip.isPrivate(host)) {
return [host, 'PrivIp']
} else {
return [host, 'WebIp']
}
} else {
return false
}
}
/**
* @param {string} uri
* @return {string} validateuri
* 解析并重新组合 url,其中禁止'user' 'pass'组合
*/
function renewUrl(uri) {
const uriObj = parse(uri)
let validateuri = `${uriObj.protocol}//${uriObj.host}`
if (uriObj.port) {
validateuri += `:${uriObj.port}`
}
if (uriObj.pathname) {
validateuri += `${uriObj.pathname}`
}
if (uriObj.query) {
validateuri += `?${uriObj.query}`
}
if (uriObj.hash) {
validateuri += `#${uriObj.hash}`
}
return validateuri
}
Для наиболее важных функций обработки интерфейса, которые могут иметь уязвимости из-за различной логики, конкретная реализация здесь не приводится. Однако, пока вышеупомянутые принципы избегания уязвимостей SSRF сочетаются с вышеупомянутыми функциями, это может быть примерно завершено.
Наконец, краткое изложение одного предложения: никогда не доверяйте вводу пользователя!
Эта статья была впервые опубликована в моем блоге (Нажмите здесь, чтобы просмотреть), добро пожаловать, чтобы следовать