Уязвимость веб-безопасности SSRF Введение и решения

Node.js Безопасность
Уязвимость веб-безопасности SSRF Введение и решения

Обновление: некоторые студенты в области комментариев предположили, что они могут быть атакованы при получении IP-адресов через доменные имена.Спасибо за напоминание. Я не профессионал в области безопасности и мало что об этом знаю, поэтому мне очень стыдно.


Когда дело доходит до веб-безопасности, наш интерфейс может подвергаться воздействию XSS и CSRF. По работе я столкнулся с SSRF во внутреннем сервисе, за который отвечал, процесс обучения и решения буду описывать здесь. SSRF (Server-Side Request Forgery), то есть подделка запроса на стороне сервера, представляет собой уязвимость системы безопасности, созданную злоумышленником для формирования запроса, инициированного сервером. Как правило, SSRF-атаки нацелены на внутренние системы, недоступные из внешней сети.

Большинство причин формирования SSRF заключается в том, что сервер обеспечивает функцию получения данных от других серверных приложений и не фильтрует и не ограничивает целевой адрес. Например, получить текстовое содержимое веб-страницы с указанного URL-адреса, загрузить картинку по указанному адресу, загрузить и так далее. Злоумышленник может использовать сервер, на котором расположено приложение, для отправки HTTP-запроса, который злоумышленник хочет отправить в соответствии с потоком программы, и использовать эту уязвимость для обнаружения служб в производственной сети.Атакующий может быть напрямую проксирован во внутреннюю сеть. , позволяя злоумышленнику обходить доступ к сети.Контроль, загружать неавторизованные файлы, получать прямой доступ к интрасети и даже получать учетные данные сервера.

Во внутреннем веб-приложении есть интерфейс для скачивания файлов, за который отвечает автор/download, который принимает параметр url, указывающий на адрес загружаемого файла, приложение инициирует запрос на адрес, загружает файл на сервер, на котором находится приложение, а затем выполняет последующую обработку. Возникает проблема: плацдармом здесь выступает сервер, на котором находится приложение, через этот интерфейс злоумышленник получает доступ во внутреннюю сеть и может выполнять множество вредоносных операций.

Опасности, вызванные SSRF, включают:

  • Вы можете выполнять сканирование портов во внешней сети, во внутренней сети, где расположен сервер, и локально для получения баннерной информации некоторых служб;
  • Атака на приложения, работающие в интрасети или локально (например, переполнение);
  • Снятие отпечатков веб-приложений интрасети, доступ к файлам осуществляется по умолчанию;
  • Атакующие веб-приложения на внутренние и внешние сети, в основном атаки, которые могут быть достигнуты с использованием получения параметров (таких как StrUTS2, SQLI и т. Д.);
  • Используйте файловый протокол для чтения локальных файлов и т. д.

Общие решения:

  1. Отфильтруйте возвращаемую информацию. Проще всего проверить ответ удаленного сервера на запрос. Если веб-приложение должно получить файл определенного типа, проверьте, соответствует ли возвращенная информация стандарту, прежде чем отображать возвращенный результат пользователю;
  2. Унифицировать информацию об ошибках, чтобы пользователи не могли судить о состоянии порта удаленного сервера на основе информации об ошибках;
  3. Ограничьте запрашиваемый порт портом, обычно используемым http, например, 80, 443, 8080, 8090;
  4. Белый список внутренних IP-адресов. Избегайте приложений для получения данных интрасети, атакуйте интранет;
  5. Отключите нежелательные протоколы. Разрешены только запросы http и https. Может предотвратить такие проблемы, как file:///, gopher://, ftp:// и т. д.

По заявке автора/downloadАдрес файла, запрашиваемый интерфейсом, является относительно фиксированным, поэтому используется метод внесения IP-адресов в белый список. Разумеется, автор почерпнул и более комплексное решение.Вот идеи коллег из отдела безопасности:

  1. Ограничения протоколов (по умолчанию разрешены протоколы HTTP, HTTPS), 30-кратные переходы (30-кратные переходы по умолчанию не разрешены), унифицированная информация об ошибках (по умолчанию не унифицирована, унифицированная информация об ошибках позволяет избежать вредоносных атак за счет оценки информации об ошибках)

  2. Оценка 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
  3. Метод устранения несоответствия между получателем 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 сочетаются с вышеупомянутыми функциями, это может быть примерно завершено.

Наконец, краткое изложение одного предложения: никогда не доверяйте вводу пользователя!

Эта статья была впервые опубликована в моем блоге (Нажмите здесь, чтобы просмотреть), добро пожаловать, чтобы следовать