Migrate documentation from 3.x

This commit is contained in:
Damian Mooyman 2018-06-13 14:50:02 +12:00
parent 6f32762268
commit 3ea98cdb13
No known key found for this signature in database
GPG Key ID: 78B823A10DE27D1A
4 changed files with 369 additions and 22 deletions

View File

@ -76,6 +76,17 @@ functionality is available as an additional [Spam Protection](https://github.com
module if required. The module provides an consistent API for allowing third-party spam protection handlers such as
[Recaptcha](http://www.google.com/recaptcha/intro/) and [Mollom](https://mollom.com/) to work within the `Form` API.
## Data disclosure through HTTP Caching (since 4.2.0)
Forms, and particularly their responses, can contain sensitive data (such as when data is pre-populated or re-posted due
to validation errors). This data can inadvertently be stored either in a user's browser cache or in an intermediary
cache such as a CDN or other caching-proxy. If incorrect `Cache-Conrol` headers are used, private data may be cached and
accessible publicly through the CDN.
To ensure this doesn't happen SilverStripe adds `Cache-Control: no-store, no-cache, must-revalidate` headers to any
forms that have validators or security tokens (all of them by default) applied to them; this ensures that CDNs
(and browsers) will not cache these pages.
## Related Documentation
* [Security](../security)

View File

@ -1,7 +1,188 @@
title: HTTP Cache Headers
summary: Set the correct HTTP cache headers for your responses.
# Caching Headers
# HTTP Cache Headers
## Overview
By default, SilverStripe sends headers which signal to HTTP caches
that the response should be not considered cacheable.
HTTP caches can either be intermediary caches (e.g. CDNs and proxies), or clients (e.g. browsers).
The cache headers sent are `Cache-Control: no-store, no-cache, must-revalidate`;
HTTP caching can be a great way to speed up your website, but needs to be properly applied.
Getting it wrong can accidentally expose draft pages or other protected content.
The [Google Web Fundamentals](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#public_vs_private)
are a great way to learn about HTTP caching.
## Cache Control Headers
### Overview
In order to support developers in making safe choices around HTTP caching,
we're using a `HTTPCacheControl` class to control if a response
should be considered public or private. This is an abstraction on existing
lowlevel APIs like `HTTP::add_cache_headers()` and `SS_HTTPResponse->addHeader()`.
The `HTTPCacheControl` API makes it easier to express your caching preferences
without running the risk of overriding essential core safety measures.
Most commonly, these APIs will prevent HTTP caching of draft content.
It will also prevent caching of content generated with an active session,
since the system can't tell whether session data was used to vary the output.
In this case, it's up to the developer to opt-in to caching,
after ensuring that certain execution paths are safe despite of using sessions.
The system behaviour does not guard against accidentally caching "private" content,
since there are too many variations under which output could be considered private
(e.g. a custom "approval" flag on a comment object). It is up to
the developer to ensure caching is used appropriately there.
The [api:SilverStripe\Control\Middleware\HTTPCacheControlMiddleware] class supplements the `HTTP` helper class.
It comes with methods which let developers safely interact with the `Cache-Control` header.
### disableCache()
Simple way to set cache control header to a non-cacheable state.
Use this method over `privateCache()` if you are unsure about caching details.
Takes precendence over unforced `enableCache()`, `privateCache()` or `publicCache()` calls.
Removes all state and replaces it with `no-cache, no-store, must-revalidate`. Although `no-store` is sufficient
the others are added under [recommendation from Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Examples)
Does not set `private` directive, use `privateCache()` if this is explicitly required
([details](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#public_vs_private))
### enableCache()
Simple way to set cache control header to a cacheable state.
Use this method over `publicCache()` if you are unsure about caching details.
Removes `no-store` and `no-cache` directives; other directives will remain in place.
Use alongside `setMaxAge()` to indicate caching.
Does not set `public` directive. Usually, `setMaxAge()` is sufficient. Use `publicCache()` if this is explicitly required
([details](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#public_vs_private))
### privateCache()
Advanced way to set cache control header to a non-cacheable state.
Indicates that the response is intended for a single user and must not be stored by a shared cache.
A private cache (e.g. Web Browser) may store the response. Also removes `public` as this is a contradictory directive.
### publicCache()
Advanced way to set cache control header to a cacheable state.
Indicates that the response may be cached by any cache. (eg: CDNs, Proxies, Web browsers)
Also removes `private` as this is a contradictory directive
### Priority
Each of these highlevel methods has a boolean `$force` parameter which determines
their application priority regardless of execution order.
The priority order is as followed, sorted in descending order
(earlier items will overrule later items):
* `disableCache($force=true)`
* `privateCache($force=true)`
* `publicCache($force=true)`
* `enableCache($force=true)`
* `disableCache()`
* `privateCache()`
* `publicCache()`
* `enableCache()`
## Cache Control Examples
### Global opt-in for page content
Enable caching for all page content (through `Page_Controller`).
```php
<?php
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
use SilverStripe\CMS\Controllers\ContentController;
class PageController extends ContentController
{
public function init()
{
HTTPCacheControlMiddleware::singleton()
->enableCache()
->setMaxAge(60); // 1 minute
parent::init();
}
}
```
Note: SilverStripe will still override this preference when a session is active,
a [CSRF token](/developer_guides/forms/form_security) token is present,
or draft content has been requested.
### Opt-out for a particular controller action
If a controller output relies on session data, cookies,
permission checks or other triggers for conditional output,
you can disable caching either on a controller level
(through `init()`) or for a particular action.
```php
<?php
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
use SilverStripe\CMS\Controllers\ContentController;
class PageController extends ContentController
{
public function myprivateaction($request)
{
HTTPCacheControlMiddleware::singleton()
->disableCache();
return $this->myPrivateResponse();;
}
}
```
Note: SilverStripe will still override this preference when a session is active,
a [CSRF token](/developer_guides/forms/form_security) token is present,
or draft content has been requested.
### Global opt-in, ignoring session (advanced)
This can be helpful in situations where forms are embedded on the website.
SilverStripe will still override this preference when draft content has been requested.
CAUTION: This mode relies on a developer examining each execution path to ensure
that no session data is used to vary output.
Use case: By default, forms include a [CSRF token](/developer_guides/forms/form_security)
which starts a session with a value that's unique to the visitor, which makes the output uncacheable.
But any subsequent requests by this visitor will also carry a session, leading to uncacheable output
for this visitor. This is the case even if the output does not contain any forms,
and does not vary for this particular visitor.
```php
<?php
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
use SilverStripe\CMS\Controllers\ContentController;
class PageController extends ContentController
{
public function init()
{
HTTPCacheControlMiddleware::inst()
->enableCache($force=true) // DANGER ZONE
->setMaxAge(60); // 1 minute
parent::init();
}
}
```
## Defaults
By default, PHP adds caching headers that make the page appear purely dynamic. This isn't usually appropriate for most
sites, even ones that are updated reasonably frequently. SilverStripe overrides the default settings with the following
@ -14,39 +195,41 @@ headers:
* Since a visitor cookie is set, the site won't be cached by proxies.
* Ajax requests are never cached.
## Customizing Cache Headers
## Max Age
### HTTP::set_cache_age
The cache age determines the lifetime of your cache, in seconds.
It only takes effect if you instruct the cache control
that your response is public in the first place (via `enableCache()` or via modifying the `HTTP.cache_control` defaults).
```php
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
HTTPCacheControlMiddleware::singleton()
->setMaxAge(60)
```
Note that `setMaxAge(0)` is NOT sufficient to disable caching in all cases.
### Last Modified
Used to set the modification date to something more recent than the default. [api:DataObject::__construct] calls
[api:HTTP::register_modification_date(] whenever a record comes from the database ensuring the newest date is present.
```php
use SilverStripe\Control\HTTP;
HTTP::set_cache_age(0);
```
Used to set the max-age component of the cache-control line, in seconds. Set it to 0 to disable caching; the "no-cache"
clause in `Cache-Control` and `Pragma` will be included.
### HTTP::register_modification_date
```php
HTTP::register_modification_date('2014-10-10');
```
Used to set the modification date to something more recent than the default. [DataObject::__construct](api:SilverStripe\ORM\DataObject::__construct) calls
[HTTP::register_modification_date(](api:SilverStripe\Control\HTTP::register_modification_date() whenever a record comes from the database ensuring the newest date is present.
### Vary
### Vary: cache header
By default, SilverStripe will output a `Vary` header (used by upstream caches for determining uniqueness)
that looks like
A `Vary` header tells caches which aspects of the response should be considered
when calculating a cache key, usually in addition to the full URL path.
By default, SilverStripe will output a `Vary` header with the following content:
```
Cookie, X-Forwarded-Protocol, User-Agent, Accept
Vary: X-Requested-With, X-Forwarded-Protocol
```
To change the value of the `Vary` header, you can change this value by specifying the header in configuration
To change the value of the `Vary` header, you can change this value by specifying the header in configuration.
```yml
SilverStripe\Control\HTTP:

View File

@ -770,6 +770,15 @@ class MySecureController extends Controller
}
```
## HTTP Caching Headers
Caching is hard. If you get it wrong, private or draft content might leak
to unauthenticated users. We have created an abstraction which allows you to express
your intent around HTTP caching without worrying too much about the details.
See [/developer_guides/performances/http_cache_headers](Developer Guides > Performance > HTTP Cache Headers)
for details on how to apply caching safely, and read Google's
[Web Fundamentals on Caching](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching).
## Related
* [http://silverstripe.org/security-releases/](http://silverstripe.org/security-releases/)

View File

@ -201,3 +201,147 @@ SilverStripe\Core\Injector\Injector:
args:
disable-container: true
```
### HTTP Cache Header changes
#### Overview
In order to support developers in making safe choices around HTTP caching,
we've introduced a `HTTPCacheControlMiddleware` class to control if a response
should be considered public or private. This is an abstraction on existing
lowlevel APIs like `HTTP::add_cache_headers()` and `SS_HTTPResponse->addHeader()`.
This change introduces smaller but necessary changes to HTTP caching headers
sent by SilverStripe. If you are relying on HTTP caching in your implementation,
or use modules such as [silverstripe/controllerpolicy](https://github.com/silverstripe/silverstripe-controllerpolicy),
please review the implications of these changes below.
In short, these APIs make it easier to express your caching preferences
without running the risk of overriding essential core safety measures.
Most commonly, these APIs will prevent HTTP caching of draft content.
It will also prevent caching of content generated with an active session,
since the system can't tell whether session data was used to vary the output.
In this case, it's up to the developer to opt-in to caching,
after ensuring that certain execution paths are safe despite of using sessions.
The system behaviour does not guard against accidentally caching "private" content,
since there are too many variations under which output could be considered private
(e.g. a custom "approval" flag on a comment object). It is up to
the developer to ensure caching is used appropriately there.
By default, SilverStripe sends headers which signal to HTTP caches
that the response should be considered not cacheable.
See [Developer Guide: Performance > HTTP Cache Headers](/developer_guide/performance/http_cache_headers)
for details on the new API.
#### Example Usage
##### Global opt-in for page content
Enable caching for all page content (through `Page_Controller`).
```diff
<?php
-use SilverStripe\Control\HTTP;
+use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
use SilverStripe\CMS\Controllers\ContentController;
class PageController extends ContentController
{
public function init()
{
- HTTP::set_cache_age(60);
+ HTTPCacheControlMiddleware::inst()
+ ->enableCache()
+ ->setMaxAge(60); // 1 minute
parent::init();
}
}
```
Note: SilverStripe will still override this preference when a session is active,
a [CSRF token](/developer_guides/forms/form_security) token is present,
or draft content has been requested.
##### Opt-out for a particular controller action
If a controller output relies on session data, cookies,
permission checks or other triggers for conditional output,
you can disable caching either on a controller level
(through `init()`) or for a particular action.
```diff
<?php
-use SilverStripe\Control\HTTP;
+use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
use SilverStripe\CMS\Controllers\ContentController;
class PageController extends ContentController
{
public function myprivateaction($request)
{
- HTTP::set_cache_age(0);
+ HTTPCacheControl::inst()
+ ->disableCache();
return $this->myPrivateResponse();
}
}
```
Note: SilverStripe will still override this preference when a session is active,
a [CSRF token](/developer_guides/forms/form_security) token is present,
or draft content has been requested.
##### Global opt-in, ignoring session (advanced)
This can be helpful in situations where forms are embedded on the website.
SilverStripe will still override this preference when draft content has been requested.
CAUTION: This mode relies on a developer examining each execution path to ensure
that no session data is used to vary output.
Use case: By default, forms include a [CSRF token](/developer_guides/forms/form_security)
which starts a session with a value that's unique to the visitor, which makes the output uncacheable.
But any subsequent requests by this visitor will also carry a session, leading to uncacheable output
for this visitor. This is the case even if the output does not contain any forms,
and does not vary for this particular visitor.
```diff
<?php
-use SilverStripe\Control\HTTP;
+use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
use SilverStripe\CMS\Controllers\ContentController;
class PageController extends ContentController
{
public function init()
{
- HTTP::set_cache_age(60);
+ HTTPCacheControl::inst()
+ ->enableCache($force=true) // DANGER ZONE
+ ->setMaxAge(60); // 1 minute
parent::init();
}
}
```
#### Detailed Cache-Control Changes:
* Added `Cache-Control: no-store` header to default responses,
to prevent intermediary HTTP proxies (e.g. CDNs) from caching unless developers opt-in
* Removed `Cache-Control: no-transform` header from default responses
* Removed `Vary: Cookie` as an unreliable cache buster,
rely on the existing `Cache-Control: no-store` defaults instead
* Removed `Vary: Accept`, since it's very uncommon to vary content on
the `Accept` headers submitted through the request,
and it can significantly decrease the likelyhood of a cache hit.
Note this is different from `Vary: Accept-Encoding`,
which is important for compression (e.g. gzip), and usually added by
other layers such as Apache's mod_gzip.