Назад | Перейти на главную страницу

Что происходило в этой ошибке CAA Let's Encrypt?

Недавно Let's Encrypt поделилась этой ошибкой, которая произошла в их системах и приводила к проблемам с сертификатами для их клиентов. Они описывают ошибку так:

Ошибка: когда запрос сертификата содержал N доменных имен, которые требовали перепроверки CAA, Боулдер выбирал одно доменное имя и проверял его N раз. На практике это означает, что если подписчик проверил доменное имя в момент X, а записи CAA для этого домена в момент X позволили выпуск Let's Encrypt, этот подписчик сможет выпустить сертификат, содержащий это доменное имя, до X + 30 дней, даже если позже кто-то установил на это доменное имя записи CAA, которые запрещают выпуск Let's Encrypt.

Означает ли это, что, когда у пользователя было несколько доменных имен, требующих повторной проверки CA, Let's Encrypt проверит только первый домен? Была ли проблема в том, что сертификаты были выпущены на доменах, которые не принадлежали пользователю, получавшему сертификат?

Let's Encrypt выдал менее безопасные сертификаты из-за ошибки. Это скорее состояние гонки, чем что-либо еще.

А CAA record - это дополнительная запись DNS, которая ограничивает поставщиков сертификатов, которым разрешено выдавать сертификаты для домена. Итак, если подписчик проверяет доменное имя во время X и записи CAA для домена разрешили выпуск Let's Encrypt в то время, когда подписчик проверял свой домен, у подписчика будет 30 дней с даты проверки для выдачи действительных сертификатов. Таким образом, если кто-то позже добавит записи CAA, запрещающие выдачу сертификатов LE в любое время между временем X + 30 дней, подписчик сможет выдавать сертификаты, игнорируя запись CAA.

Если вы посмотрите на PR с ошибкой, здесь она впервые сработала, когда NewAuthorizationSchema флаг функции был включен в продукте.

Код проблемы - это Общая ошибка в Go см. соответствующий код для боулдер-ра:

// authz2ModelMapToPB converts a mapping of domain name to authz2Models into a
// protobuf authorizations map
func authz2ModelMapToPB(m map[string]authz2Model) (*sapb.Authorizations, error) {
    resp := &sapb.Authorizations{}
    for k, v := range m {
        // Make a copy of k because it will be reassigned with each loop.
        kCopy := k
        authzPB, err := modelToAuthzPB(&v)
        if err != nil {
            return nil, err
        }
        resp.Authz = append(resp.Authz, &sapb.Authorizations_MapElement{Domain: &kCopy, Authz: authzPB})
    }
    return resp, nil
}

В чем проблема: взяв ссылку на переменную итератора цикла. Они не смогли обработать вторую переменную итератора цикла. v должным образом.

А в свою очередь не удалось учесть два важных поля в этой функции: IdentifierValue и RegistrationID

func modelToAuthzPB(am *authzModel) (*corepb.Authorization, error) {
    expires := am.Expires.UTC().UnixNano()
    id := fmt.Sprintf("%d", am.ID)
    status := uintToStatus[am.Status]
    pb := &corepb.Authorization{
        Id: &id,
        Status: &status,
        Identifier: &am.IdentifierValue,
        RegistrationID: &am.RegistrationID,
        Expires: &expires,
    }

Боулдер (в частности, boulder-ra) определяет, что данное полное доменное имя требует повторной проверки CAA, и использует поле идентификатора из объекта авторизации, которое было неверным из-за вышеуказанной фиксации. Таким образом, способ обработки поля идентификатора будет одинаковым для всех значений на одной карте. Так, например, если у вас было несколько авторизаций, требующих повторной проверки CAA, boulder-ra перепроверил бы только одно полное доменное имя, а не остальные.

Действительно плохая ошибка.