Subject: [CDNi] URI Signing — Feedback from an Implementer

From: Chris Lemmons <[email-masked]>
Date: Wed, 4 Oct 2017 15:42:35 -0600

I recently implemented (most of) URI Signing as an Apache Traffic
Server plugin. I used Draft 12 as my reference and I found a few rough
edges that could be polished a bit. I apologize in advance for the
wall of text, but I think it made more sense to collect my thoughts
than it did to create a dozen emails. It's worth noting, I've only
implemented it in a single-CDN context, so I haven't fully exercised
some of the pieces.

sub claim URIs
============
According to RFC 7519 § 4.1.2, the sub claim is of StringOrURI type,
which specifies that while it may contain any value, if the value
contains a colon, it must be a URI as specified in RFC 3986. But
although all valid sub claims will contain a colon, very few are valid
URIs. Even the example given doesn't appear to be a valid URI:

uri-pattern:*://*/folder/content-83112371/quality_*/segment????.mp4

It contains several ?s and *s that would not be legal in a URI, as
near as I can tell. Maybe I missed something in the URI spec, but this
claim really doesn't feel like a URI to me.

sub claim Simple Container
=====================
The URI includes the query string and path parameters, which includes
the JWT. The JWT cannot include a copy of itself. The only way this
claim could match something is if it were presented as part of a token
renewal via cookie. And that isn't terribly useful, since token
renewal relies on regex/pattern matches to match future requests.

And simply dropping the query string entirely isn't a great option,
because the query string may contain other material that is important
to match. And that doesn't help the path-parameter case either.

As near as I can tell, there is no useful use-case for the simple container.

sub claim Hash Container
====================
This has the same problem as the simple container, just hashed in the
middle. The JWT can't contain a hashed copy of itself for
chicken-and-egg reasons. (Technically, proving that mathematically
feels hard, since it might be possible to contrive a JWT that contains
a hash that matches a signed copy of itself. That's sufficiently
impractical to call impossible, though, I think.)

sub claim Pattern Container
=====================
The Pattern Container is strictly less powerful than the regex
container. And it uses a bespoke glob format that doesn't appear
anywhere else, so each implementation will have to implement it from
scratch. (This task isn't terribly hard, but it's really easy to
accidentally do it exponentially and there are a few unobvious
gotchas.)

And in practice, I'm not sure how useful the globbing is. For the most
part, unexpected query parameters are simply ignored. That means the
example pattern:

*://*/folder/content-83112371/quality_*/segment????.mp4

... matches this URI:

https://server.example.com/secret/file.xml?URISigningPackage=JWTGOESHERE&hackery=/folder/content-83112371/quality_7/segmentcode.mp4

... but not this URI:

https://cdn.example.com/folder/content-83112371/quality_high/segment1234.mp4?URISigningPackage=JWTGOESHERE

Given the widespread behaviour of handling query strings, few, if any,
issuers would be able to meaningfully use the glob anywhere but in the
last place. You definitely don't want people trying to use it like it
is described in the examples.

sub claim Regex Container
=====================
This container is the most powerful of all the containers. Everything
expressible in one of the other containers can be expressed here as
well. The examples here are likely to lead people astray though. The
example regex:

.*\\://.*/folder/content-83112371/quality_.*/segment.{3}\\.mp4

... should probably be:

[^:]*\\://[^/]*/folder/content-83112371/quality_[^/]*/segment.{3}\\.mp4(\?.*)?

Universal globs aren't terribly useful since they're very vulnerable
to URL manipulation. The examples should use exclusionary patterns
that end on the delimiting character. Also, the query is part of the
pattern, so it needs to be part of the regex as well.

The regex and pattern containers both don't specify that the patterns
are anchored (no extra stuff is allowed on either end), but it seems
implied and if it weren't anchored, it would be largely useless
because of URI manipulation. The sub containers should probably
specify that the pattern must match the entire URI.

aud claim
=======
According to RFC 7519 § 4.1.3, the aud claim is either a StringOrURI
or an array thereof. The draft describes what to do with a single
string (which helpfully cannot contain a colon), but doesn't discuss
the array case. I don't know that there's a strong reason to include
the array format as legal, but I also don't know that there's a strong
reason contrariwise. Either way, though, the draft should probably
pick a side and document it.

Additionally, that section specifies that:

> If the principal processing the claim does not identify itself with a value in the "aud" claim when this claim is present, then the JWT MUST be rejected.

The interpretation of the value is generally application specific, but
it would be very strange to say that the "principal processing the
claim" (the CDN) identifies itself with the value in the aud claim
(the Client's IP address).

The aud claim seems to be designed to limit who will accept the token.
It feels somewhat co-opted in this situation.

cdniets default
===========
The cdniets field does not have a default value. It's legal for an
issuer to elide this value even when providing a cdnistt of 1. It
should either have a non-zero default value or be required when
cdnistt is 1.

Additionally, all other time values are real, this one is integer. The
precision is probably not critical, but consistency is probably
better.

cdniets claim
==========
This should be added to the iat, not the exp. A crafty malefactor who
wishes to share his url for all to use could simply repeatedly request
the same small file more frequently than it expires. As specified in
the draft, this would cause the exp to continuously extend. This could
easily be used to create a "renewed" token that lasts for years.

A CDN renewing a token should always add the the extension time to
current time, instead, to get the expiration time.

nbf leap seconds
=============
If issuers set nbf claims to the iat time, this will cause conforming
implementations to incorrectly reject JWTs during leap seconds. This
is an issue inherited from the JWT specification, but it's more
serious in a token-renewal context, since it can cause a large number
of streaming video clients to simultaneously stutter or disconnect.

According to RFC 7419 and the POSIX specification it references, a
leap second is required to "repeat" at the beginning of the next day.
This will cause all issued tokens to be invalid for about a second.
(If implementations "smear" the leap second in contravention of the
POSIX spec, this will not be an issue, so long as the issuer and CDN
are synchronized.)

Since the issue is largely inherited from the JWT RFC, I don't know
that it makes sense to attempt to fix it here. I just happened to run
into it here. But a cautionary note that one should not set nbf to the
current time might be valuable.

key identification
=============
When verifying a JWT, I need to know which key should be used to
verify it. In my implementation, I use a combination of the iss claim
and the kid in the JOSE header. But neither of those values are
required. And as near as I can tell, the draft does not allow me to
require them. It says I MAY use it to identify the key, but doesn't
say I may reject an otherwise valid JWT that doesn't have an iss
claim.

Likewise, the JOSE kid header (RFC 7515 § 4.1.4) is necessary to
identify the specific key.

As written, I don't believe I can deny an otherwise valid JWT for
missing these fields. But the only way I can determine that it's valid
is to try all the keys that could potentially be valid, which may be a
very large number of keys (issuers can use many keys and a CDN may
easily have multitudinous valid issuers). The CDN has a trust
relationship with the issuers, so I can be sure that a signed JWT
won't abuse my resources, but key identification by necessity must
occur before verification. So, a malefactor who presents an invalidly
signed JWT that lacks an iss claim and kid header can cause
significant resource waste by forcing me to check all possible keys.

A CDN should be able to reject a claim that doesn't present a valid
issuer/kid combination. (It shouldn't be required to, of course, since
it might only support a single key and it might be a non-issue.)

A CDN that requires iss claims should probably be able to advertise it
via the CDNI Metadata Interface as well. It might even be
straightforward enough to say that if the CDNI Metadata Interface
Property Issuers is not empty, the iss claim is required and if it's
empty, it may be elided. That does limit some situations, though.

URI attribute
==========
The URI attribute can be encoded in either a query parameter or a path
parameter. The draft implies, but does not clearly state, that a
conforming implementation must accept a valid JWT in either location.
This works, but could probably be a bit clearer. Also, any
implementation that does what I'm given to understand is now called
"Token Renewal" needs to accept JWTs via whatever mechanism they use
as well. Specifically, the section on cdnistt=1 specifies that cookies
should be used to send and receive the renewed token, but nothing
seems to give the CDN permission to look anywhere but the query and
path parameters for the JWT.

In addition, the draft doesn't describe how the CDN should behave when
it is given multiple cookies. In a token-renewal situation, this isn't
hypothetical. A manifest signed with an expired JWT is quite likely to
be presented with a valid recently-renewed cookie.

renewal rules
==========
There isn't much said about the values in renewed tokens. Most of the
rules that apply to CDNI redirection make sense for the exact same
reasons for renewed tokens. Right now, a "renewed token" can contain
different values for nearly anything. This should be fleshed out, I
think, mostly by adding "or Signed Token Renewal" after "CDNI
redirection" in most of the claims.

jti claim
======
I'm not entirely clear on how this would work in a CDN environment.
The draft indicates that if it contains a nonce, the CDN can verify
that it hasn't been used before. But that makes nonce-checking a
synchronous event across the entire CDN. For every signed request, it
would have to lock a global list of "used nonces", check it, add the
new item or reject the JWT, the unlock. This feels slow, but different
use cases might warrant it.

This section also doesn't include either MUST or MAY language for a
CDN that supports nonces. Instead, it just makes a statement about
what a nonce might be useful for if a CDN did some probably slow
things. I think this claim should be more specific about what a CDN
MUST or MAY do if it wants to allow nonces to be used.

---

From: Francois Le Faucheur <[email-masked]>
Date: Thu, 5 Oct 2017 09:19:50 +0200

Thank you Chris. Such implementation feedback is very useful.

François

> On 4 Oct 2017, at 23:42, Chris Lemmons <[email-masked]> wrote:
> 
> _______________________________________________
> CDNi mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/cdni

---

From: "Kent Leung (kleung)" <[email-masked]>
Date: Fri, 27 Oct 2017 22:56:03 +0000

Hi Chris. Thanks for the review comments. Plenty of items to parse through, so sorry for the delay. Definitely, it's valuable to have implementer's feedback. See my comments below.

-----Original Message-----
From: CDNi [mailto:[email-masked]] On Behalf Of Chris Lemmons
Sent: Wednesday, October 4, 2017 2:43 PM
To: [email-masked]
Subject: [CDNi] URI Signing — Feedback from an Implementer

I recently implemented (most of) URI Signing as an Apache Traffic Server plugin. I used Draft 12 as my reference and I found a few rough edges that could be polished a bit. I apologize in advance for the wall of text, but I think it made more sense to collect my thoughts than it did to create a dozen emails. It's worth noting, I've only implemented it in a single-CDN context, so I haven't fully exercised some of the pieces.

sub claim URIs
============
According to RFC 7519 § 4.1.2, the sub claim is of StringOrURI type, which specifies that while it may contain any value, if the value contains a colon, it must be a URI as specified in RFC 3986. But although all valid sub claims will contain a colon, very few are valid URIs. Even the example given doesn't appear to be a valid URI:

It contains several ?s and *s that would not be legal in a URI, as near as I can tell. Maybe I missed something in the URI spec, but this claim really doesn't feel like a URI to me.

KL> The “?” and “*” are special literals in the URI pattern for matching to an valid URI. I may be missing your point. But the objective of uri-pattern is to specify the pattern match format and not the exact URI. The URI Container is meant to represent the URI in the forms described in section 2.1.11.

sub claim Simple Container
=====================
The URI includes the query string and path parameters, which includes the JWT. The JWT cannot include a copy of itself. The only way this claim could match something is if it were presented as part of a token renewal via cookie. And that isn't terribly useful, since token renewal relies on regex/pattern matches to match future requests.

And simply dropping the query string entirely isn't a great option, because the query string may contain other material that is important to match. And that doesn't help the path-parameter case either.

KL> I agree that if the JWT is carried in the query string and path parameters, the JWT needs to be omitted part. For consistency, the entire URI except the part containing the value for URISignPackage attribute is validated by match operation. This should happen regardless if cookies, query string and path, etc. are used.

sub claim Hash Container
====================
This has the same problem as the simple container, just hashed in the middle. The JWT can't contain a hashed copy of itself for chicken-and-egg reasons. (Technically, proving that mathematically feels hard, since it might be possible to contrive a JWT that contains a hash that matches a signed copy of itself. That's sufficiently impractical to call impossible, though, I think.)

KL> This should be treated the same as above, by excluding JWT to avoid the chicken-and-egg situation.

sub claim Pattern Container
=====================
The Pattern Container is strictly less powerful than the regex container. And it uses a bespoke glob format that doesn't appear anywhere else, so each implementation will have to implement it from scratch. (This task isn't terribly hard, but it's really easy to accidentally do it exponentially and there are a few unobvious
gotchas.)

And in practice, I'm not sure how useful the globbing is. For the most part, unexpected query parameters are simply ignored. That means the example pattern:

... matches this URI:

... but not this URI:

Given the widespread behaviour of handling query strings, few, if any, issuers would be able to meaningfully use the glob anywhere but in the last place. You definitely don't want people trying to use it like it is described in the examples.

KL> I know that was Ray’s proposal. I sense the example was targeted for HAS case where this URI was for the video segment which would use signed token chain with cookie to carry the JWT. But that's just my assumption as that would fit with the example. Maybe the manifest file is likely to have signed URL without using signed token chain. E.g., http://*/folder/content-83112371/manifest/*.xml(\?.*).

sub claim Regex Container
=====================
This container is the most powerful of all the containers. Everything expressible in one of the other containers can be expressed here as well. The examples here are likely to lead people astray though. The example regex:

... should probably be:

Universal globs aren't terribly useful since they're very vulnerable to URL manipulation. The examples should use exclusionary patterns that end on the delimiting character. Also, the query is part of the pattern, so it needs to be part of the regex as well.

The regex and pattern containers both don't specify that the patterns are anchored (no extra stuff is allowed on either end), but it seems implied and if it weren't anchored, it would be largely useless because of URI manipulation. The sub containers should probably specify that the pattern must match the entire URI.

KL> I'm OK with your suggested example string.

aud claim
=======
According to RFC 7519 § 4.1.3, the aud claim is either a StringOrURI or an array thereof. The draft describes what to do with a single string (which helpfully cannot contain a colon), but doesn't discuss the array case. I don't know that there's a strong reason to include the array format as legal, but I also don't know that there's a strong reason contrariwise. Either way, though, the draft should probably pick a side and document it.

KL> Since “aud” is used for Client IP/IP prefix, the IPv6 address has colon-based representation. That may be an issue? I don’t see a need for array. Though the language in RFC 7519 covers the case of a single StringOrURI case already (“In the special case when the JWT has one audience, the "aud" value MAY be a single case-sensitive string containing a StringOrURI value.“)

> If the principal processing the claim does not identify itself with a value in the "aud" claim when this claim is present, then the JWT MUST be rejected.

The interpretation of the value is generally application specific, but it would be very strange to say that the "principal processing the claim" (the CDN) identifies itself with the value in the aud claim (the Client's IP address).

KL> I get your point. The draft uses “aud” as the intended receiver (i.e. UA as identified by Client IP) of the requested content. It’s a bit convoluted since I believe that “aud” in JWT is for the intended recipient of the token (CDN). Perhaps we should introduce a Client IP claim for CDN to match the source IP against the value?

cdniets default
===========
The cdniets field does not have a default value. It's legal for an issuer to elide this value even when providing a cdnistt of 1. It should either have a non-zero default value or be required when cdnistt is 1.

Additionally, all other time values are real, this one is integer. The precision is probably not critical, but consistency is probably better.

KL> I think cdniets should be required when cdnistt is 1. Draft should be explicit. I'm OK with using NumericDate (real) type for consistency. But your next point makes me think it's better to be an integer to indicate the time delta and not absolute time that is requested. So cdniets is not a time value, but a positive integer value that is used to increase the expiration time of the matched URI. Would that be OK?

cdniets claim
==========
This should be added to the iat, not the exp. A crafty malefactor who wishes to share his url for all to use could simply repeatedly request the same small file more frequently than it expires. As specified in the draft, this would cause the exp to continuously extend. This could easily be used to create a "renewed" token that lasts for years.

A CDN renewing a token should always add the the extension time to current time, instead, to get the expiration time.

KL> Good point. The current time should be used as reference for increasing the time delta for expiration to prevent additive extension.

nbf leap seconds
=============
If issuers set nbf claims to the iat time, this will cause conforming implementations to incorrectly reject JWTs during leap seconds. This is an issue inherited from the JWT specification, but it's more serious in a token-renewal context, since it can cause a large number of streaming video clients to simultaneously stutter or disconnect.

According to RFC 7419 and the POSIX specification it references, a leap second is required to "repeat" at the beginning of the next day.
This will cause all issued tokens to be invalid for about a second.
(If implementations "smear" the leap second in contravention of the POSIX spec, this will not be an issue, so long as the issuer and CDN are synchronized.)

Since the issue is largely inherited from the JWT RFC, I don't know that it makes sense to attempt to fix it here. I just happened to run into it here. But a cautionary note that one should not set nbf to the current time might be valuable.

key identification
=============
When verifying a JWT, I need to know which key should be used to verify it. In my implementation, I use a combination of the iss claim and the kid in the JOSE header. But neither of those values are required. And as near as I can tell, the draft does not allow me to require them. It says I MAY use it to identify the key, but doesn't say I may reject an otherwise valid JWT that doesn't have an iss claim.

Likewise, the JOSE kid header (RFC 7515 § 4.1.4) is necessary to identify the specific key.

As written, I don't believe I can deny an otherwise valid JWT for missing these fields. But the only way I can determine that it's valid is to try all the keys that could potentially be valid, which may be a very large number of keys (issuers can use many keys and a CDN may easily have multitudinous valid issuers). The CDN has a trust relationship with the issuers, so I can be sure that a signed JWT won't abuse my resources, but key identification by necessity must occur before verification. So, a malefactor who presents an invalidly signed JWT that lacks an iss claim and kid header can cause significant resource waste by forcing me to check all possible keys.

A CDN should be able to reject a claim that doesn't present a valid issuer/kid combination. (It shouldn't be required to, of course, since it might only support a single key and it might be a non-issue.)

A CDN that requires iss claims should probably be able to advertise it via the CDNI Metadata Interface as well. It might even be straightforward enough to say that if the CDNI Metadata Interface Property Issuers is not empty, the iss claim is required and if it's empty, it may be elided. That does limit some situations, though.

KL> I’m not sure if this was discussed before when draft changed to JWT method. I think multiple keys should be supported so kid can be used to identify the right key. Your last two sentences should be incorporated into the draft.

URI attribute
==========
The URI attribute can be encoded in either a query parameter or a path parameter. The draft implies, but does not clearly state, that a conforming implementation must accept a valid JWT in either location.
This works, but could probably be a bit clearer. Also, any implementation that does what I'm given to understand is now called "Token Renewal" needs to accept JWTs via whatever mechanism they use as well. Specifically, the section on cdnistt=1 specifies that cookies should be used to send and receive the renewed token, but nothing seems to give the CDN permission to look anywhere but the query and path parameters for the JWT.

KL> Good point. I think when content requires URI Signing enforcement, then CDN should look for URISigningPackage attribute. If attributed doesn't exists, then CDN looks for URISigningPackage cookie. Is this sufficient? Or does this add too much overhead?

In addition, the draft doesn't describe how the CDN should behave when it is given multiple cookies. In a token-renewal situation, this isn't hypothetical. A manifest signed with an expired JWT is quite likely to be presented with a valid recently-renewed cookie.

KL> Maybe I'm missing something. Shouldn't the renewed cookie overwrite the old one? Or the expiration time for the cookie can be used?

renewal rules
==========
There isn't much said about the values in renewed tokens. Most of the rules that apply to CDNI redirection make sense for the exact same reasons for renewed tokens. Right now, a "renewed token" can contain different values for nearly anything. This should be fleshed out, I think, mostly by adding "or Signed Token Renewal" after "CDNI redirection" in most of the claims.

KL> OK.

jti claim
======
I'm not entirely clear on how this would work in a CDN environment.
The draft indicates that if it contains a nonce, the CDN can verify that it hasn't been used before. But that makes nonce-checking a synchronous event across the entire CDN. For every signed request, it would have to lock a global list of "used nonces", check it, add the new item or reject the JWT, the unlock. This feels slow, but different use cases might warrant it.

KL> I think this depends on implementation. It's possible that that nonce space is partitioned across Surrogates serving CDN. So checking nonce can be localized to each Surrogate. Or other methods may be implemented to address the use of nonce.

This section also doesn't include either MUST or MAY language for a CDN that supports nonces. Instead, it just makes a statement about what a nonce might be useful for if a CDN did some probably slow things. I think this claim should be more specific about what a CDN MUST or MAY do if it wants to allow nonces to be used.

KL> I'm not quite sure what's needed in the draft. There's no intent to be prescriptive in text for support of nonce in CDN. 

Thanks again!

Kent

---

From: Chris Lemmons <[email-masked]>
Date: Fri, 27 Oct 2017 23:45:11 -0600

> The URI Container is meant to represent the URI in the forms described in section 2.1.11.

Right, but JWT requires that the sub claim be a "StringOrURI", with
the specification "must be a URI" if it has a colon, which it always
will. The definition of "URI" is as defined in RFC 3986. That
definition does not allow the following URL to be valid, if I
understand it correctly: uri-pattern:http://???.example.com/???/foo. I
believe it was previously allowed in RFC 2396, but that's been
obsoleted by 3986 which is what's referenced by the JWT spec.

The intent of the sub claim is mostly clear here, but I think there's
a technical conflict with RFC 3986 as included by RFC 7519. A simple
fix would be to rename it "cdnisub" or something similar. You could
leave the plain uri case in the "sub" claim with no prefix, if
desired, though that might complicate things more than it simplifies
them.

> I agree that if the JWT is carried in the query string and path parameters, the JWT needs to be omitted part.

Yeah, that was my gut reaction, too, though. It gets kinda tricky in
the corners, though. The query string, as near as I can tell, is not a
standardized component. It's typical to delimit key-value pairs with
ampersands, but I don't think it's required. For about 15 years,
splitting on semicolons instead was recommended, but not required. The
current guidance is a recommendation by the w3c, I think:
https://www.w3.org/TR/2014/REC-html5-20141028/forms.html#url-encoded-form-data
. That means that things like this are "legal":
http://example.org/secret?foo&URISigningPackage=JWTGOESHERE& . Should
the query here map to http://example.org/secret?foo& ? What if the
parameter is the only one:
http://example.org/secret?URISigningPackage=JWTGOESHERE =>
http://example.org/secret . I think it's possible to define this, but
processing it correctly can be tricky.

A similar problem occurs with "path parameters". There's no RFC that
defines them, so we'll probably need to define them as well. Removing
them for matching is also rather tricky, for similar reasons. I can
think of four solutions:

1. Rather than saying "query or path parameter", say "delimiter or
sub-delimiter followed by parameter name, then JWT, terminated by a
delimiter, sub-delimiter, or the end of the query. For evaluating
matches, the parameter name, JWT, and terminating delimiter or
sub-delimiter will be removed, unless the JWT is terminated by the end
of the URI, in which case the preceding delimiter or sub-delimiter,
parameter name, and JWT will be removed". Then, rather than having a
parameter name of "URISigningPackage", you have one of
"URISigningPackage=". For the purposes at hand, leaving the equals in
there is mostly fine, but we've got a use case where we basically need
to encode the token as a path element directly, because some poorly
behaving clients become deeply confused at the sight of semicolons
(and, probably, any other sub-delimiters) in the path. Providing a
place to "override" the equals would accomplish this. Clarify that the
URI will be processed left-to-right and only the first match will be
evaluated. (There's no point in allowing multiple JWTs anyway.)

2. Carefully document the current recommendations on query strings and
path parameters and encode them into the spec. Write rules similar to
option 1 that clarify how and when the surrounding delimiters should
be removed for matching with the parameter and its value.

3. Carefully document the current recommendations on query strings and
path parameters and encode them into the spec. For matching purposes,
remove all path parameters and the entire query string.

4. Carefully document the current recommendations on query strings and
path parameters and encode them into the spec. For matching purposes,
remove either the path parameters or the query string, depending on
where the JWT is found.

I like option 1, because it dodges the question of how to format all
your query strings and path parameters, and allows it to adapt to
reasonable changes in the future. It does, however mean that if you,
for some reason, had an actual path element named
"URISigningPackage=", you'd need to percent-encode some or all of the
name, probably the equals. I'm not super-keen on documenting query
strings or path parameters, but maybe we can find an appropriate
reference to cite?

Option 4 isn't great because it means that a url that matches when you
pass the JWT in a path parameter is going to fail on the same path if
you stick it in a cookie. In option 3, it's a blunt instrument, but it
does keep the same matches when you switch to a cookie in token
renewal.

> I sense the example was targeted for HAS case where this URI was for the video segment which would use signed token chain with cookie to carry the JWT. But that's just my assumption as that would fit with the example. Maybe the manifest file is likely to have signed URL without using signed token chain. E.g., http://*/folder/content-83112371/manifest/*.xml(\?.*).

Unfortunately, because the glob matches all things, even the example
you gave is vulnerable to manipulation. What if you replaced the glob
format with a "uri-path-prefix" container. It would match any
container with a path prefix that matches exactly. For example:
"uri-path-prefix:/folder/content-83112371/manifest/" would match
"http://example.com/folder/content-83112371/manifest/important.xml?foo=bar".
You'd still have regexes for the fancy stuff. Bonus points: it's
really fast and dead simple to implement. Very low risk of a poor
implementation going accidentally exponential.

The other option is to make the glob and hook not match delimiters.
Delimiters are pretty important, so people probably want to match
those specifically. Again, if you want to match an arbitrary number of
path elements, there are regexes.

> Since “aud” is used for Client IP/IP prefix, the IPv6 address has colon-based representation. That may be an issue?

Since the value must be encrypted and encoded as a JWE, you dodge the
colon problem here. The JWE can't have fancy chars in its
representation.

> I get your point. The draft uses “aud” as the intended receiver (i.e. UA as identified by Client IP) of the requested content. It’s a bit convoluted since I believe that “aud” in JWT is for the intended recipient of the token (CDN). Perhaps we should introduce a Client IP claim for CDN to match the source IP against the value?

Renaming the field to "cdniip" makes the problem go away. For bonus
points, you can put aud to its intended purpose and allow signers to
specify which CDN is intended to accept a particular JWT, if that's
desirable.

> I don’t see a need for array.

If you rename it, you get to re-pick the format. But I do see some
uses for specifying multiple IPs. Dual stack boxes may need to be
authorized to get data on either of their interfaces. An ISP might
simply limit IPs to the range they provide to their customers, but not
specify the exact customer's IP independently, which could involve a
couple of distinct CIDRs. A very, very clever signer might know that a
given mobile device has a mobile IP and a wifi IP and sign the ticket
for both to allow some movement. Allowing multiple IPs provides some
flexibility without increasing complexity that much.

> So cdniets is not a time value, but a positive integer value that is used to increase the expiration time of the matched URI.

I would probably use a real number for consistency. There's no
compelling implementation reason to limit it, since the implementation
must already be using real values for time already. And it allows
sub-second timeouts if a CDN required them. Maybe in a scenario where
a CDN is serving a signed html page and using token renewal to allow
the page to load javascript and image assets? Maybe in a few years
when the Internet is so fast that 30ms seems like a lifetime?

> Maybe I'm missing something. Shouldn't the renewed cookie overwrite the old one?

If the user requests http://example.com/content, they'll store the
renewal cookie at /. If the user uses that ticket to request
http://example.com/content/frag-1, they'll get another cookie and
store it at /content. Then, when they request
http://example.com/content/frag-2, they'll submit the cookie they got
at / and the one they got at /content, in no particular order. The
/content cookie keeps getting updated, but the / cookie eventually
expires.

Likewise, a client might be given a manifest with a token in the path:
http://example.com/content;URISigningPackage=JWTGOESHERE/manifest.xml.
It then follows that to
http://example.com/content;URISigningPackage=JWTGOESHERE/frag-01.
After a few requests, the original parameter in the path is no longer
valid, but the client simply treats it as part of the path, so it
doesn't know to remove it. There's still a valid renewed token in the
cookie, though.

> It's possible that that nonce space is partitioned across Surrogates serving CDN.

You're right. If a CDN can know which system will service a signed
request, it can fairly easily localize nonce-checking. Either way,
it's an implementation detail.

>  I'm not quite sure what's needed in the draft. There's no intent to be prescriptive in text for support of nonce in CDN.

I was thinking a sentence like this: "If the signed JWT contains a
Nonce claim and that Nonce has already been used, then the CDN MAY
reject the request." I'd consider upgrading that to MUST, though.
There's weasel room in the definition of "used", if an implementation
really wants it.