[secdir] draft-ietf-cdni-edge-control-metadata-12 ietf last call Secdir review

Mike Ounsworth via Datatracker <noreply@ietf.org> Sun, 30 August 2026 00:24 UTC

Return-Path: <noreply@ietf.org>
X-Original-To: secdir@ietf.org
Delivered-To: secdir@mail2.ietf.org
Received: from [10.244.8.80] (gaia.k8s.ietf.org [4.156.85.76]) by mail2.ietf.org (Postfix) with ESMTP id 9D0AE131A2955; Sat, 29 Aug 2026 17:24:56 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ietf.org; s=ietf1; t=1788049496; bh=6gDqaxdSm15NXDqmbe67p/blz1F6t1vd0swo1pbcOZw=; h=From:To:Cc:Subject:Reply-To:Date; b=RRGXDSsftQAullPyxbThK27uWSS+B/byMLB1bEfPml8XNBif+c2t1MAzPHkiIExxV KGx2LBB3MmPJhQIF5IDKXs6DlxiXxvyJZB80KzXU6qKytBhd/72/E2dNTIXh4GZ7Wj LvyhzdDsomzGQspoz0AExFEfdy8geCQm8m6lQyic=
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
From: Mike Ounsworth via Datatracker <noreply@ietf.org>
To: secdir@ietf.org
X-Test-IDTracker: no
X-IETF-IDTracker: 12.73.0
Auto-Submitted: auto-generated
Precedence: bulk
Message-ID: <178804949651.212399.1997728777047400726@dt-datatracker-6669c7b496-s9mrn>
Date: Sat, 29 Aug 2026 17:24:56 -0700
Message-ID-Hash: PM7X3MAOU4ZGF4V6CJBGX6GCUDW4EEG2
X-Message-ID-Hash: PM7X3MAOU4ZGF4V6CJBGX6GCUDW4EEG2
X-MailFrom: noreply@ietf.org
X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-secdir.ietf.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header
CC: cdni@ietf.org, draft-ietf-cdni-edge-control-metadata.all@ietf.org, last-call@ietf.org
X-Mailman-Version: 3.3.9rc6
Reply-To: Mike Ounsworth <mike@ounsworth.ca>
Subject: [secdir] draft-ietf-cdni-edge-control-metadata-12 ietf last call Secdir review
List-Id: Security Area Directorate <secdir.ietf.org>
Archived-At: <https://mailarchive.ietf.org/arch/msg/secdir/aFPonncfcbio1QnWiOKLpHvGb5A>
List-Archive: <https://mailarchive.ietf.org/arch/browse/secdir>
List-Help: <mailto:secdir-request@ietf.org?subject=help>
List-Owner: <mailto:secdir-owner@ietf.org>
List-Post: <mailto:secdir@ietf.org>
List-Subscribe: <mailto:secdir-join@ietf.org>
List-Unsubscribe: <mailto:secdir-leave@ietf.org>

Document: draft-ietf-cdni-edge-control-metadata
Title: Content Delivery Network Interconnection (CDNI) Edge Control Metadata
Reviewer: Mike Ounsworth
Review result: Has Issues

I have reviewed this document as part of the security directorate's
ongoing effort to review all IETF documents being processed by the
IESG.  These comments were written primarily for the benefit of the
security area directors.  Document editors and WG chairs should treat
these comments just like any other last call comments.

The summary of the review is HAS ISSUES

This draft is essentially defining a file-based config for CORS.

Every time I've worked with CORS on a web server, it's either simple -- ie
static CORS headers across the whole web server -- or it requires like actual
server code to set all the CORS settings in a cohesive way, and then still the
security team find problems because CORS is so complicated and hard to get
right on the first pass. I have never personally seen a config file that's able
to capture the full complexity of a dynamic CORS configuration. Do those exist?
If so, that would be useful as design input for this draft, maybe as an
informative reference.

I know that the intention here is to simply forward an existing CORS config,
not to get into the details of CORS, however, I think there are valid CORS
configurations that cannot be expressed in the current MI model, and the MI
model is missing information that the dCDN needs in order to properly set CORS
headers in some edge cases -- leading to either security issues at the UA or
strange behaviours as the dCDN tries to simultaneously comply with the MI
config and the WHATWG FETCH spec. I suggest that more work is needed to align
this document with the CORS definitions in the WHATWG FETCH spec. I think the
definitions of the new MI properties needs way more security consideration text
documenting the complex interactions between the specified config settings and
the security issues that can arise from "strange" configurations. There also a
number of unimplementable or undefined behaviours that arise from
inconsistencies between this document and its normative references; fortunately
I think those are easily fixed.

Issues
------

Presented in what I feel is most-important-first order.

1.
This is a meta-comment that applies to all the others.
Given that this draft is, almost in its entirety, dealing with configuring a
security mechanism (a rather complex and difficult-to-configure one at that),
there needs to be much more Security Consideration discussion on implications
of misconfiguration and whether the way CORS config is being represented in the
MI is a tight fit with the definition in the WHATWG FETCH spec, or whether
there are gaps that lead to security implications.

I'm not really qualified to come up with the list, but, as an example, I could
imagine that propagation delay of the MI could induce security issues in some
contexts, for example if the uCDN adds a new resource before its corresponding
CORS policy has propagated to the dCDN. You want these things to fail closed
with either the dCDN or the UA refusing to serve the content to the requesting
app until the correct config catches up. I am not qualified to suggest how
exactly you achieve that within this tech stack.

2.
As a general point across the whole draft: the potential for the dCDN to reject
the request (return a 403) based on CORS headers is never discussed. In
general, it's a totally valid use of CORS to use it to enforce that your REST
API and static resources will only serve javascript clients loaded from your
domains, and will reject requests from javascript clients loaded from any other
domain. I myself have built web apps that use CORS in this way.

This seems like a serious omission from the document to either provide a
mechanism for configuring this, or to state clearly that it's out-of-scope for
the CDN use case.

I suspect that accommodating this will require a bunch of adjustments to your
MI model. On the other hand, if you are assuming that the correct behaviour is
to always serve the requested content down to the UA, and in cases where the
origin is wrong then the CORS headers will cause the UA to refuse to let the
non-authorized javascript access it, then that needs to be explicitly stated. I
don't think there's a security issue here with serving the "blocked content" to
the UA since CORS only applies to browsers and that content could easily be
fetched, for example, via curl, but some operators may want the ability to
configure the dCDN not to stream large content to a UA with a CORS header on it
that requires the UA to delete the content immediately. Serving a 403 in those
cases would often makes more sense for bandwidth use. Also, if refusal to serve
(403) is a valid outcome, then the Origin: header needs to be included in the
Vary: header, therefore this interacts with #3.

3.
HTTP Vary: header
I expected some discussion of how the mechanisms defined in this doc interact
with the HTTP Vary: header because as soon as you start playing with CORS, you
need to be careful that all CORS request headers that could possibly affect the
response are captured in the Vary: header. Failure to do this properly leads to
downstream proxies (and UAs?) thinking that they are free to cache this
response and serve it to any client in the future who requests it. As I
understand this draft, either the MI would need to carry Vary: info, or the
dCDN needs to decide for itself which CORS headers need to be in Vary:
dynamically based on the CORS payloads in the MI and then merge with with the
Vary: headers obtained from other sources (both of which sound fairly
complicated, and may itself require specification so that dCDNs don't screw it
up). I suggest that some of the text from:
https://fetch.spec.whatwg.org/#cors-protocol-and-http-caches needs to be lifted
into this draft, and then discuss the implications for the CDNI MI.

Fix: this is just an off-the-cuff idea that will need more thought, but maybe
every CORS MI property needs a sub-property "include-in-vary: Boolean, default:
true", with clear guidance about how the dCDN is supposed to merge this with
Vary: contents coming from other sources (append to existing Vary: or add a
second Vary: header -- I think RFC 7230 says that multiple Vary headers are
allowed, but this would need a closer look). Also would need a discussion of
the effects on the behaviour and security of dowstream proxies and UAs when
this is set to "false".

4.
wildcard-return
I feel that additional Security Considerations are needed, but I'm not 100%
sure what. My understanding is that:
    Access-Control-Allow-Origin: *
signals to the UA that this response is effectively public and can be shared
with scripts running on any origin. Under that understanding, the sample
configuration in Figure 1 seems odd to me because it's telling the dCDN: "If
you get a request with Origin: https://sourcepage.example.com, then respond
telling the UA that this resource is public and javascript from any Origin can
request it in the future". In other words, "Only https://sourcepage.example.com
can request this resource, but once they do, then the browser can share it with
javascript from any origin". I am struggling to think of a situation where
would be is desired behaviour. I suspect that for the vast majority of cases,
the thing you've shown in Figure 1 is actually a misconfiguration and a
security issue. So it seems odd (and maybe dangerous?) to have this as the very
first and most basic example in the draft. (I would love to be wrong here. If
there is an obvious use for this, then I would love to learn about it.)

In general, I find that the way the wildcard-return MI payload is defined, this
seems like a footgun in that operators who don't really understand CORS will
set it to "true" because it seems to make things work, but in fact it has
effectively undermined the CORS security mechanism. I suggest that the authors
should add text indicating what the intended use of the wildcard-return setting
is -- ie in what situations is it advisable and safe to set it to "true", and
what are the security implications of doing so.

This interacts with #3 because "Vary: Origin" can protect you here by telling
the UA that this response is only valid for future requests with exactly the
same Origin as the original request (effectively cancelling out the wildcard).
Had your document specified the interaction between
"Access-Control-Allow-Origin:" and Vary:, then possible I wouldn't have even
pointed this out.

Moreover (and I'm not a deep expert here), but I notice that this seems related:
https://fetch.spec.whatwg.org/#cors-protocol-and-http-caches
(particularly the last paragraph) which is saying that if *any* of the dCDN's
responses include "Access-Control-Allow-Origin: *" (or any other static
Origin), then *all* responses (CORS and non-CORS) are required to always
include an Access-Control-Allow-Origin: header and never a Vary: header. I do
not see any of this complexity discussed in the draft.

So, likely, the configuration shown in Figure 1 is hazardous in that it will
cause a dCDN to behave in a completely bizarre way in order to stay within the
guidance of the FETCH spec.

Also, FETCH clearly states that Access-Control-Allow-Origin cannot be * on a
credentialed request (cookies, TLS client certificates and HTTP
authentication). So correct dCDN handling of MI.wildcard-return requires a
fairly complicated interaction with MI.allow-credentials, which is not
currently discussed either.

5.
Section 3.3:
"
Property: no-origin-response-headers
"
I suspect you meant this section to allow a uCDN to direct a dCDN to set *CORS*
headers on responses. But as written, this allows a uCDN to direct a dCDN to
inject *any* header into responses. Depending on the trust relationship between
the dCDN and the uCDN, the dCDN may or may not be ok with this, and may
consider this a security vulnerability. Also, this feels like a footgun for
misconfiguration; for example when happens if you specify here to overwrite
some header that already exists in the response, like Content-Length or
Content-Type?

Fix: I think you either want to hard-code in the draft, or allow the dCDN to
configure, a list of headers that it will allow to be set by this MI property.
Probably what you meant is that only CORS headers can be listed here, so
listing those out is probably the intention.

This property probably also carries interactions with the HTTP Vary: header
that should be thought about.

Finally, this applies when the request contains no Origin: header.

5a.
Section 3.2 specifies "if the Origin request header does not match, the CORS
response headers MUST NOT be included in the dCDN response". I'm wondering out
loud here if returning no CORS response headers is always the right behaviour?
To my knowledge, WHATWG FETCH is silent on this, so there is no default
behaviour or guidance -- therefore servers are allowed to return CORS headers
in response to a failed Origin check. So would some implementers want the
ability to configure headers to return in this case -- ie add a
rejected-origin-response-headers that bahaves similarly to your
no-origin-response-headers?

6.
allow-list

6a.
path vs whole URI
This one is a "I know what you meant", but as written, I think it's
unimplementable, and the examples are technically invalid data.

Your examples of allow-lists in Figures 1 and 2 definitely look reasonable:

      "allow-list": [
        {
          "pattern": "https://sourcepage.example.com"
        }
      ]

      "allow-list": [
        {
          "pattern": "*://sourcepage.example.com"
        }
      ]

but those don't match the definition of allow-list in section 3.3.1, says:
"
Property: allow-list
Type: Array of Strings, where each String is a pattern as defined in Section
4.1.5 [RFC8006] " First, "allow-list": [{"pattern":
"https://sourcepage.example.com"}] is not an Array of Strings. It's an array of
objects each containing a "pattern" element containing a String. An Array of
Strings would be "allow-list": ["https://sourcepage.example.com",].

Second, and more importantly, Section 4.1.5 of [RFC8006] is path-absolute from
[RFC3986] and a path-absolute is a URI *path component*, starting with a "/",
and not a full URI and therefore clearly can't hold the data you show in
Figures 1 and 2. I don't see any datatype in Section 4 of RFC 8006 that can
properly hold a CORS Origin of the form scheme::host[:port] because none of
them include a Scheme, which is super important to CORS security since in CORS
"http://example.com" is a different Origin and MUST NOT match
"https://example.com".

This bug leads me to wonder about the level of implementation experience that
this draft has, because I think the samples in Figure 1 and 2 should be
rejected by a compliant parser.

Fix: 1) either change "Array of Strings" to match what's in the samples, or
change the samples to be Arrays of Strings. 2) I suggest that you drop the
reference to RFC8006 and cite the WHATWG FETCH spec's definition of Origin
directly. Alternatively, you may need to define a new datatype for representing
Origin: scheme://host[:port].

6b.
case sensitivity of origin string matching
The WHATWG Fetch spec, normatively referenced by this draft, constrains
the Origin header to lower case and requires byte-exact comparison --
Section 3.2 (https://fetch.spec.whatwg.org/#origin-header)

However, RFC 8006 specifies the matching rules as:
"
Property: case-sensitive
Mandatory-to-Specify: No.  Default is case-insensitive match
"
Since this draft does not override the default, an implementation gets a
case-insensitive match.
In practice, maybe not a real problem since Fetch guarantees Origin is always
lower case. So a conforming UA always emits lower case; this draft over-accepts
only from clients that are not implementing CORS correctly.

Fix: in order to fix 6a, you'll probably need to define a new JSON object
anyway, so for its syntax, I would copy verbatim the Origin matching rules from
Section 3.2 of the Fetch standard.

7.
Section 3.3 says:
"
Property: allow-credentials

    Description: The value the dCDN will include in the
    Access-Control-Allow-Credentials response header to a CORS preflight
    request.
"
but https://fetch.spec.whatwg.org/#http-responses says that
Access-Control-Allow-Credentials can be on a response to any CORS request, not
only preflights. I suspect this is an easy-to-fix typo.

8.
Section 3.3 says:
"
 Property: expose-headers
▎
▎ - Description: A list of header names the dCDN will include in the
▎   Access-Control-Expose-Headers response header of a CORS preflight
▎   request.
"
But, to my reading, https://fetch.spec.whatwg.org/#http-responses says clearly
that Access-Control-Expose-Headers is only allowed on non-preflight requests. I
think 3.3 has it backwards. I suspect this is an easy-to-fix typo.

9.
Section 3.1 -- the paragraph defining CORS says "HTTP client application", but
in fact CORS is specific to browser-type user agents (UA) and does not have any
security value for curl-type HTTP clients. The document should probably draw
the reader's attention to the fact that CORS' security model specifically
protects against malicious javascript running in an honest browser (UA) since
the UA is the CORS enforcement point (which is different than most web security
models where the server is the enforcement point). Keeping that front-of-mind
is important for reading the sections that follow.

10.
Section 3.3:
"
Property: allow-credentials"
Type: Boolean
"
WHATWG FETCH says that the only allowed value for
Access-Control-Allow-Credentials is lower-case "true". Declaring this as a
Boolean allows the value "false", which will result in undefined behaviour.

12.
The term "synthetic response" appears 4 times in the document, for example,
Section 3.2 uses the term "synthetic response" in the context of the dCDN
responding to an OPTIONS request. But I (and Claude) couldn't find the term
"synthetic response" defined anywhere in this document or in its references, so
I'm flagging "synthetic response" as an undefined term that could lead to
interpretation ambiguity. To my reading, it could mean one of two things: A)
this is a CORS thing where a "synthetic response" means a response to an
OPTIONS request which is a "synthetic" version of the response to the
corresponding GET / POST, or B) this is a CDN thing where a dCDN generates a
"synthetic response" any time that it responds without consulting the uCDN
(Claude thinks this is the intended meaning).

11.
"Using this GenericMetadata object permits the uCDN to: :"
Typo, double ":"
More importantly, are you sure this is "uCDN"? The list that follows would make
more sense if that was "dCDN", or maybe "allows the uCDN to set policy for the
following dCDN behavors"? That would also address my comment above about
refusal to serve the content as a valid outcome, because I think that's what
the second bullet is getting at: " * Match the value of the Origin request
header with a list of valid values. If it is successful, the dCDN will inject
the appropriate CORS response headers. ~ Though, for clarity, that bullet
should maybe also have a "If unsuccessful, ..." sentence.

Nits
----

"To permit shared cross-origin network requests, the CORS protocol defines a
set of request and response headers that must be present to permit access to
these restricted resources."

That lower-case "must" is a bit distracting since my brain tried to pick apart
whether that was normative or not. I suggest a re-word: "To permit shared
cross-origin network requests, the CORS protocol defines a set of request and
response headers that serve to allow servers and UA clients to selectively
relax the Same-Origin-Policy and grant access to these restricted resources."

"CDNs can include logic to dynamically generate the required response headers
based on the UA request without contacting the CP servers." I think the word
"can" is unnecessary in this sentence.

Section 3.2:
"
If none of those parameters are set in the configuration, the dCDN MUST forward
every CORS preflight request (OPTIONS method) to the uCDN servers " except
except DoS flooding protection, blah blah. May or may not be worth mentioning.