This page provides an overview of signed cookies and instructions for using them with Cloud CDN. Signed cookies give time-limited resource access to a set of files, regardless of whether the users have Google Accounts.
Signed cookies are an alternative to signed URLs. Signed cookies protect access when separately signing tens or hundreds of URLs for each user isn't feasible in your application.
Signed cookies let you do the following:
- Authorize a user and provide them with a time-limited token for accessing your protected content (instead of signing each URL).
- Scope the user's access to a specific URL prefix, such as
https://media.example.com/videos/
, and grant the authorized user access to protected content within that URL prefix only. - Keep your URLs and media manifests unchanged, simplifying your packaging pipeline and improving cacheability.
If you want to scope access to specific URLs instead, consider using signed URLs.
Before you begin
Before you use signed cookies, do the following:
Ensure that Cloud CDN is enabled; for instructions, see Using Cloud CDN. You can configure signed cookies on a backend before enabling Cloud CDN, but there is no effect until Cloud CDN is enabled.
If necessary, update to the latest version of the Google Cloud CLI:
gcloud components update
For an overview, see Signed URLs and signed cookies.
Configuring signed request keys
Creating keys for your signed URLs or signed cookies requires several steps, which are described in the following sections.
Security considerations
Cloud CDN does not validate requests in the following circumstances:
- The request is not signed.
- The backend service or backend bucket for the request doesn't have Cloud CDN enabled.
Signed requests must always be validated at the origin before serving the response. This is because origins can be used for serving a mixture of signed and unsigned content and because a client might access the origin directly.
- Cloud CDN doesn't block requests without a
Signature
query parameter orCloud-CDN-Cookie
HTTP cookie. It rejects requests with invalid (or otherwise malformed) request parameters. - When your application detects an invalid signature, make sure that your
application responds with an
HTTP 403 (Unauthorized)
response code.HTTP 403
response codes aren't cacheable. - Responses to signed and unsigned requests are cached separately, so a successful response to a valid signed request is never used to serve an unsigned request.
- If your application sends a cacheable response code to an invalid request, valid future requests might be incorrectly rejected.
For Cloud Storage backends, make sure to remove public access, so that Cloud Storage can reject requests that are missing a valid signature.
The following table summarizes the behavior.
Request has signature | Cache hit | Behavior |
---|---|---|
No | No | Forward to backend origin. |
No | Yes | Serve from cache. |
Yes | No | Validate signature. If valid, forward to backend origin. |
Yes | Yes | Validate signature. If valid, serve from cache. |
Create signed request keys
You enable support for Cloud CDN signed URLs and signed cookies by creating one or more keys on a Cloud CDN-enabled backend service, backend bucket, or both.
For each backend service or backend bucket, you can create and delete keys as your security needs dictate. Each backend can have up to three keys configured at a time. We suggest periodically rotating your keys by deleting the oldest, adding a new key, and using the new key when signing URLs or cookies.
You can use the same key name in multiple backend services and backend buckets because each set of keys is independent of the others. Key names can be up to 63 characters. To name your keys, use the characters A-Z, a-z, 0-9, _ (underscore), and - (hyphen).
When you create keys, be sure to keep them secure because anyone who has one of your keys can create signed URLs or signed cookies that Cloud CDN accepts until the key is deleted from Cloud CDN. The keys are stored on the computer where you generate the signed URLs or signed cookies. Cloud CDN also stores the keys to verify request signatures.
To keep the keys secret, the key values aren't included in responses to any API requests. If you lose a key, you must create a new one.
To create a signed request key, follow these steps.
Console
- In the Google Cloud console, go to the Cloud CDN page.
- Click the name of the origin that you want to add the key to.
- On the Origin details page, click the Edit button.
- In the Origin basics section, click Next to open the Host and path rules section.
- In the Host and path rules section, click Next to open the Cache performance section.
- In the Restricted content section, select Restrict access using signed URLs and signed cookies.
Click Add signing key.
- Specify a unique name for the new signing key.
In the Key creation method section, select Automatically generate. Alternatively, click Let me enter, and then specify a signing key value.
For the former option, copy the automatically generated signing key value to a private file, which you can use to create signed URLs.
Click Done.
In the Cache entry maximum age section, enter a value, and then select a unit of time.
Click Done.
gcloud
The gcloud
command-line tool reads keys from a local file that
you specify. The key file must be created by generating strongly random 128
bits, encoding them with base64, and then replacing the character +
with
-
and replacing the character /
with _
. For more information, see
RFC 4648.
It is vital that the key is strongly random. On a UNIX-like system, you can
generate a strongly random key and store it in the key file with the
following command:
head -c 16 /dev/urandom | base64 | tr +/ -_ > KEY_FILE_NAME
To add the key to a backend service:
gcloud compute backend-services \ add-signed-url-key BACKEND_NAME \ --key-name KEY_NAME \ --key-file KEY_FILE_NAME
To add the key to a backend bucket:
gcloud compute backend-buckets \ add-signed-url-key BACKEND_NAME \ --key-name KEY_NAME \ --key-file KEY_FILE_NAME
Configure Cloud Storage permissions
If you use Cloud Storage and you have restricted who can read the objects, you must give Cloud CDN permission to read the objects by adding the Cloud CDN service account to Cloud Storage ACLs.
You don't need to create the service account. The service account is created automatically the first time that you add a key to a backend bucket in a project.
Before you run the following command, add at least one key to a backend bucket in your project. Otherwise, the command fails with an error because the Cloud CDN cache fill service account is not created until you add one or more keys for the project.
gcloud storage buckets add-iam-policy-binding gs://BUCKET \ --member=serviceAccount:service-PROJECT_NUM@cloud-cdn-fill.iam.gserviceaccount.com \ --role=roles/storage.objectViewer
Replace PROJECT_NUM
with your project number and
BUCKET
with your storage bucket.
The Cloud CDN service account
service-PROJECT_NUM@cloud-cdn-fill.iam.gserviceaccount.com
doesn't appear in the list of service accounts in your project. This is because
the Cloud CDN service account is owned by Cloud CDN, not your
project.
For more information about project numbers, see Locate the project ID and project number in the Google Cloud console Help documentation.
Customize the maximum cache time
Cloud CDN caches responses for signed requests regardless of the
backend's Cache-Control
header. The maximum time that responses can be cached
without revalidation is set by the signed-url-cache-max-age
flag, which
defaults to one hour and can be modified as shown here.
To set the maximum cache time for a backend service or backend bucket, run one of the following commands:
gcloud compute backend-services update BACKEND_NAME --signed-url-cache-max-age MAX_AGE
gcloud compute backend-buckets update BACKEND_NAME --signed-url-cache-max-age MAX_AGE
List signed request key names
To list the keys on a backend service or backend bucket, run one of the following commands:
gcloud compute backend-services describe BACKEND_NAME
gcloud compute backend-buckets describe BACKEND_NAME
Delete signed request keys
When URLs signed by a particular key should no longer be honored, run one of the following commands to delete that key from the backend service or backend bucket:
gcloud compute backend-services \ delete-signed-url-key BACKEND_NAME --key-name KEY_NAME
gcloud compute backend-buckets \ delete-signed-url-key BACKEND_NAME --key-name KEY_NAME
Creating a policy
Signed cookie policies are a series of key-value
pairs (delimited by the :
character), similar to the query parameters used in a signed URL.
For examples, see Issuing cookies to users.
Policies represent the parameters for which a request is valid. Policies are signed by using a hash-based message authentication code (HMAC) that Cloud CDN validates on each request.
Defining policy format and fields
There are four required fields that you must define in the following order:
URLPrefix
Expires
KeyName
Signature
The key-value
pairs in a signed cookie policy are case-sensitive.
URLPrefix
URLPrefix
denotes a URL-safe base64 encoded URL prefix that encompasses all
paths that the signature should be valid for.
A URLPrefix
encodes a scheme (either http://
or https://
), FQDN, and an
optional path. Ending the path with a /
is optional but recommended. The
prefix shouldn't include query parameters or fragments such as ?
or #
.
For example, https://media.example.com/videos
matches requests to both of the
following:
https://media.example.com/videos?video_id=138183&user_id=138138
https://media.example.com/videos/137138595?quality=low
The prefix's path is used as a text substring, not strictly a directory path.
For example, the prefix https://example.com/data
grants access to both of the
following:
/data/file1
/database
To avoid this mistake, we recommend ending all prefixes with /
unless you
intentionally choose to end the prefix with a partial filename such as
https://media.example.com/videos/123
to grant access to the following:
/videos/123_chunk1
/videos/123_chunk2
/videos/123_chunkN
If the requested URL doesn't match the URLPrefix
, Cloud CDN
rejects the request and returns an HTTP 403
error to the client.
Expires
Expires
must be a Unix timestamp (the number of seconds since January 1, 1970).
KeyName
KeyName
is the key name for a key created against the backend bucket or
backend service. Key names are case-sensitive.
Signature
Signature
is the URL-safe base64 encoded HMAC-SHA-1 signature of the fields
that make up the cookie policy. This is validated on each request; requests
with an invalid signature are rejected with an HTTP 403
error.
Programmatically creating signed cookies
The following code samples demonstrate how to programmatically create signed cookies.
Go
Java
Python
Validating signed cookies
The process of validating a signed cookie is essentially the same as generating a signed cookie. For example, suppose you want to validate the following signed cookie header:
Cookie: Cloud-CDN-Cookie=URLPrefix=URL_PREFIX:Expires=EXPIRATION:KeyName=KEY_NAME:Signature=SIGNATURE; Domain=media.example.com; Path=/; Expires=Tue, 20 Aug 2019 02:26:49 GMT; HttpOnly
You can use the secret key named by KEY_NAME
to
independently generate the signature and then validate that it matches
SIGNATURE
.
Issuing cookies to users
Your application must generate and issue each user (client) a single HTTP cookie containing a correctly signed policy:
Create an HMAC-SHA-1 signer in your application code.
Sign the policy by using the chosen key, taking note of the key name that you added to the backend, such as
mySigningKey
.Create a cookie policy with the following format, noting that both the name and value are case-sensitive:
Name: Cloud-CDN-Cookie Value: URLPrefix=$BASE64URLECNODEDURLORPREFIX:Expires=$TIMESTAMP:KeyName=$KEYNAME:Signature=$BASE64URLENCODEDHMAC
Example
Set-Cookie
header:Set-Cookie: Cloud-CDN-Cookie=URLPrefix=aHR0cHM6Ly9tZWRpYS5leGFtcGxlLmNvbS92aWRlb3Mv:Expires=1566268009:KeyName=mySigningKey:Signature=0W2xlMlQykL2TG59UZnnHzkxoaw=; Domain=media.example.com; Path=/; Expires=Tue, 20 Aug 2019 02:26:49 GMT; HttpOnly
The
Domain
andPath
attributes in the cookie determine whether the client sends the cookie to Cloud CDN.
Recommendations and requirements
Explicitly set the
Domain
andPath
attributes to match the domain and path prefix from which you intend to serve your protected content, which might differ from the domain and path where the cookie is issued (example.com
versusmedia.example.com
or/browse
versus/videos
).Make sure that you have only one cookie with a given name for the same
Domain
andPath
.Ensure that you aren't issuing conflicting cookies because this might prevent access to content in other browser sessions (windows or tabs).
Set the
Secure
andHttpOnly
flags where applicable.Secure
ensures that the cookie is sent over HTTPS connections only.HttpOnly
prevents making the cookie available to JavaScript.The cookie attributes
Expires
andMax-Age
are optional. If you omit them, the cookie exists while the browser session (tab, window) exists.On a cache fill or cache miss, the signed cookie is passed through to the origin that is defined in the backend service. Ensure that you are validating the signed cookie value on each request before serving content.