Bug

這個 Let’s Encrypt CAA 錯誤發生了什麼?

  • March 28, 2020

最近,Let’s Encrypt 分享了他們系統中出現的這個錯誤,該錯誤導致他們的客戶出現證書問題。他們這樣描述錯誤:

漏洞:當一個證書請求包含 N 個需要 CAA 重新檢查的域名時,Boulder 會選擇一個域名並檢查 N 次。這在實踐中意味著,如果訂閱者在時間 X 驗證了一個域名,並且在時間 X 時該域的 CAA 記錄允許 Let’s Encrypt 發布,那麼該訂閱者將能夠在 X+30 之前發布包含該域名的證書天,即使後來有人在該域名上安裝了禁止 Let’s Encrypt 發行的 CAA 記錄。

這是否意味著當使用者擁有多個需要 CA 重新檢查的域名時,Let’s Encrypt 只會檢查第一個域?證書是在獲得證書的使用者不擁有的域上頒發的問題嗎?

Let’s Encrypt 基於該錯誤頒發了不太安全的證書。這比其他任何事情都更像是一種競爭條件。

CAA記錄是一個可選的 DNS 記錄,它限制允許哪些證書提供者為域頒發證書。因此,如果訂閱者在某個時間驗證了一個域名,X並且該域的 CAA 記錄允許在訂閱者驗證其域期間進行 Let’s Encrypt 發布,那麼訂閱者將有 30 天的時間從驗證之日起頒發有效證書。因此,如果後來有人添加了禁止在 X + 30 天之間的任何時間頒發 LE 證書的 CAA 記錄,則訂閱者將能夠頒發證書而忽略 CAA 記錄。

如果您查看有錯誤的PR,這是NewAuthorizationSchema在 prod 中啟用功能標誌時首次觸發它的地方。

問題程式碼是 Go 中的常見錯誤,參見 boulder-ra 的相關程式碼:

// 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

反過來,未能考慮此功能中的兩個重要領域:IdentifierValueRegistrationID

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(特別是 boulder-ra)確定給定的 FQDN 需要 CAA 重新檢查,並且它使用來自授權對象的 Identifier 欄位,由於上述送出,這是不正確的。因此,標識符欄位的處理方式對於單個映射中的所有值都是相同的。因此,例如,如果您有多個需要重新檢查 CAA 的授權,則 boulder-ra 將只重新檢查一個 FQDN 而不會重新檢查其他 FQDN。

確實是一個糟糕的錯誤。

引用自:https://serverfault.com/questions/1007743