這個 Let’s Encrypt CAA 錯誤發生了什麼?
最近,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
。反過來,未能考慮此功能中的兩個重要領域:
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(特別是 boulder-ra)確定給定的 FQDN 需要 CAA 重新檢查,並且它使用來自授權對象的 Identifier 欄位,由於上述送出,這是不正確的。因此,標識符欄位的處理方式對於單個映射中的所有值都是相同的。因此,例如,如果您有多個需要重新檢查 CAA 的授權,則 boulder-ra 將只重新檢查一個 FQDN 而不會重新檢查其他 FQDN。
確實是一個糟糕的錯誤。