Руководство: Поиск и устранение уязвимых зависимостей с помощью govulncheck

Govulncheck — это инструмент с низким уровнем шума, который помогает находить и устранять уязвимые зависимости в проектах на Go. Он делает это, сканируя зависимости проекта на наличие известных уязвимостей, а затем выявляя любые прямые или косвенные вызовы этих уязвимостей в вашем коде.

В этом руководстве вы узнаете, как использовать govulncheck для сканирования простой программы на наличие уязвимостей. Вы также научитесь приоритизировать и оценивать уязвимости, чтобы сосредоточиться на устранении самых важных сначала.

Чтобы узнать больше о govulncheck, ознакомьтесь с документацией к govulncheck, а также с этой статьёй о управлении уязвимостями для Go. Мы также будем рады получить ваш отзыв.

Предварительные требования

  • Go. Рекомендуется использовать последнюю версию Go для выполнения этого руководства. (Инструкции по установке см. в разделе Установка Go.)
  • Редактор кода. Подойдёт любой редактор, которым вы пользуетесь.
  • Командная строка. Go хорошо работает в любом терминале в Linux и Mac, а также в PowerShell или cmd в Windows.

В руководстве вы пройдёте следующие шаги:

  1. Создайте образец Go модуля с уязвимой зависимостью
  2. Установите и запустите govulncheck
  3. Оцените уязвимости
  4. Обновите уязвимые зависимости

Создайте образец Go модуля с уязвимой зависимостью

Шаг 1. Для начала создайте новую папку с названием vuln-tutorial и инициализируйте Go модуль. (Если вы новичок в Go модулях, ознакомьтесь с go.dev/doc/tutorial/create-module.

Например, из вашего домашнего каталога выполните следующее:

<code>$ mkdir vuln-tutorial
$ cd vuln-tutorial
$ go mod init vuln.tutorial
</code>

Шаг 2. Создайте файл под названием main.go внутри папки vuln-tutorial и скопируйте в него следующий код:

<code>package main

import (
  "fmt"
  "os"

  "golang.org/x/text/language"
)

func main() {
  for _, arg := range os.Args[1:] {
    tag, err := language.Parse(arg)
    if err != nil {
      fmt.Printf("%s: error: %v\n", arg, err)
    } else if tag == language.Und {
      fmt.Printf("%s: undefined\n", arg)
    } else {
      fmt.Printf("%s: tag %s\n", arg, tag)
    }
  }
}
</code>

Этот пример программы принимает список тегов языков в качестве аргументов командной строки и выводит сообщение для каждого тега, указывающее, был ли он успешно разобран, тег не определён, или произошла ошибка при разборе тега.

Шаг 3. Запустите go mod tidy, который заполнит файл go.mod всеми зависимостями, необходимыми для кода, добавленного в main.go на предыдущем шаге.

Из папки vuln-tutorial выполните:

<code>$ go mod tidy
</code>

Вы должны увидеть следующий вывод:

<code>go: finding module for package golang.org/x/text/language
go: downloading golang.org/x/text v0.9.0
go: found golang.org/x/text/language in golang.org/x/text v0.9.0
</code>

Шаг 4. Откройте файл go.mod, чтобы убедиться, что он выглядит следующим образом:

<code>module vuln.tutorial

go 1.20

require golang.org/x/text v0.9.0
</code>

Шаг 5. Понизьте версию пакета golang.org/x/text до v0.3.5, которая содержит известные уязвимости. Выполните:

<code>$ go get golang.org/x/text@v0.3.5
</code>

Вы должны увидеть следующий вывод:

<code>go: downgraded golang.org/x/text v0.9.0 => v0.3.5
</code>

Теперь файл go.mod должен содержать:

<code>module vuln.tutorial

go 1.20

require golang.org/x/text v0.3.5
</code>

Теперь давайте посмотрим, как работает govulncheck.

Установка и запуск govulncheck

Шаг 6. Установите govulncheck с помощью команды go install:

<code>$ go install golang.org/x/vuln/cmd/govulncheck@latest
</code>

Шаг 7. Из папки, которую вы хотите проанализировать (в данном случае vuln-tutorial). Выполните:

<code>$ govulncheck ./...
</code>

Вы должны увидеть следующий вывод:

<code>govulncheck is an experimental tool. Share feedback at https://go.dev/s/govulncheck-feedback.

Using go1.20.3 and govulncheck@v0.0.0 with
vulnerability data from https://vuln.go.dev (last modified 2023-04-18 21:32:26 +0000 UTC).

Scanning your code and 46 packages across 1 dependent module for known vulnerabilities...
Your code is affected by 1 vulnerability from 1 module.

Vulnerability #1: GO-2021-0113
Due to improper index calculation, an incorrectly formatted
language tag can cause Parse to panic via an out of bounds read.
If Parse is used to process untrusted user inputs, this may be
used as a vector for a denial of service attack.

More info: https://pkg.go.dev/vuln/GO-2021-0113

Module: golang.org/x/text
Found in: golang.org/x/text@v0.3.5
Fixed in: golang.org/x/text@v0.3.7

Call stacks in your code:
main.go:12:29: vuln.tutorial.main calls golang.org/x/text/language.Parse

=== Informational ===

Found 1 vulnerability in packages that you import, but there are no call
stacks leading to the use of this vulnerability. You may not need to
take any action. See https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck
for details.

Vulnerability #1: GO-2022-1059
An attacker may cause a denial of service by crafting an
Accept-Language header which ParseAcceptLanguage will take
significant time to parse.
More info: https://pkg.go.dev/vuln/GO-2022-1059
Found in: golang.org/x/text@v0.3.5
Fixed in: golang.org/x/text@v0.3.8

</code>

Интерпретация вывода

*Примечание: Если вы не используете последнюю версию Go, вы можете увидеть дополнительные уязвимости из стандартной библиотеки.

Наш код затрагивается одной уязвимостью, GO-2021-0113, потому что напрямую вызывает функцию Parse из пакета golang.org/x/text/language в уязвимой версии (v0.3.5).

Другая уязвимость, GO-2022-1059, существует в модуле golang.org/x/text в версии v0.3.5. Однако она отмечена как “Информационная”, поскольку наш код никогда (прямо или косвенно) не вызывает никаких уязвимых функций из этого отчета.

Теперь давайте оценим уязвимости и определим необходимое действие.

Оценка уязвимостей

a. Оцените уязвимости.

Сначала прочитайте описание уязвимости и определите, применима ли она к вашему коду и вашему сценарию использования. Если требуется дополнительная информация, перейдите по ссылке “More info” (Подробнее).

На основе описания, уязвимость GO-2021-0113 может вызвать панику при использовании функции Parse для обработки непроверенных входных данных пользователя. Допустим, мы планируем, чтобы наша программа могла противостоять таким входным данным, и мы обеспокоены возможностью отказа в обслуживании, поэтому уязвимость, скорее всего, применима.

GO-2022-1059, вероятно, не влияет на наш код, поскольку наш код не вызывает никаких уязвимых функций из этого отчета.

b. Принятие решения.

Чтобы устранить уязвимость GO-2021-0113, у нас есть несколько вариантов:

  • Вариант 1: Обновить до исправленной версии. Если исправление доступно, мы можем устранить уязвимую зависимость, обновив модуль до исправленной версии.
  • Вариант 2: Прекратить использование уязвимых элементов. Мы можем выбрать удаление всех вызовов уязвимой функции в нашем коде. Нам нужно будет найти альтернативу или реализовать её самостоятельно.

В данном случае исправление доступно, и функция Parse является критически важной для нашей программы. Давайте обновим нашу зависимость до версии “fixed in”, v0.3.7.

Мы решили отложить исправление информационной уязвимости, GO-2022-1059, но поскольку она находится в том же модуле, что и GO-2021-0113, и поскольку версия, в которой она исправлена, — v0.3.8, мы можем легко устранить обе уязвимости одновременно, обновив модуль до v0.3.8.

Обновление уязвимых зависимостей

К счастью, обновление уязвимых зависимостей довольно просто.

Шаг 8. Обновите golang.org/x/text до версии v0.3.8:

<code>$ go get golang.org/x/text@v0.3.8
</code>

Вы должны увидеть следующий вывод:

<code>go: upgraded golang.org/x/text v0.3.5 => v0.3.8
</code>

(Обратите внимание, что мы также могли бы выбрать обновление до latest, или любой другой версии после v0.3.8).

Шаг 9. Теперь снова запустите govulncheck:

<code>$ govulncheck ./...
</code>

Вы увидите следующий вывод:

<code>govulncheck is an experimental tool. Share feedback at https://go.dev/s/govulncheck-feedback.

Using go1.20.3 and govulncheck@v0.0.0 with
vulnerability data from https://vuln.go.dev (last modified 2023-04-06 19:19:26 +0000 UTC).

Scanning your code and 46 packages across 1 dependent module for known vulnerabilities...
No vulnerabilities found.
</code>

Наконец, govulncheck подтверждает, что уязвимости не найдены.

Регулярно сканируя зависимости с помощью команды govulncheck, вы можете защитить свою кодовую базу, выявляя, приоритизируя и устраняя уязвимости.

GoRu.dev Golang на русском

На сайте представлена адаптированная под русский язык документация языка программирования Golang