mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Secure Coding - Security Headers, Force HTTPS and Cookies
- Amending best practices for secure coding to enforce HTTPS - Add security headers to enforce HTTPS - Ensure secure cookies are used. - Added links for testing, changed documentation as part of peer review. - Arrange headers to work with HTTP interface. - fixed Cache-Control case - Added reference to Secure Sessions. - Replaced Cardinality with unique - Fixed innacurate reference to decendant. - Consistent spelling - Databases over DBMSs
This commit is contained in:
parent
8d2a1ba8be
commit
5f82997690
@ -6,7 +6,8 @@ Indexes are a great way to improve performance in your application, especially a
|
||||
data model you can reduce the time taken for the framework to find and filter data objects.
|
||||
|
||||
The addition of an indexes should be carefully evaluated as they can also increase the cost of other operations such as
|
||||
`UPDATE`/`INSERT` and `DELETE`. An index which has the same cardinality as the table will actually cost you performance.
|
||||
`UPDATE`/`INSERT` and `DELETE`. An index on a column whose data is non unique will actually cost you performance.
|
||||
E.g. In most cases an index on `boolean` status flag, or `ENUM` state will not increase query performance.
|
||||
|
||||
It's important to find the right balance to achieve fast queries using the optimal set of indexes; For SilverStripe
|
||||
applications it's a good practice to:
|
||||
@ -15,12 +16,12 @@ applications it's a good practice to:
|
||||
|
||||
The SilverStripe framework already places certain indexes for you by default:
|
||||
- The primary key for each model has a `PRIMARY KEY` unique index
|
||||
- The `ClassName` column if your model is a direct decedent from `DataObject`
|
||||
- The `ClassName` column if your model inherits from `DataObject`
|
||||
- All relationships defined in the model have indexes for their `has_one` entity (for `many_many` relationships
|
||||
this index is present on the associative entity).
|
||||
|
||||
## Defining an index
|
||||
Indexes are represented on a data object through the `DataObject::$indexes` array which maps index names to a
|
||||
Indexes are represented on a `DataObject` through the `DataObject::$indexes` array which maps index names to a
|
||||
descriptor. There are several supported notations:
|
||||
|
||||
:::php
|
||||
@ -40,7 +41,7 @@ descriptor. There are several supported notations:
|
||||
|
||||
The `<column-name>` is used to put a standard non-unique index on the column specified. For complex or large tables
|
||||
we recommend building the index to suite the requirements of your data.
|
||||
|
||||
|
||||
The `<index-name>` can be an arbitrary identifier in order to allow for more than one index on a specific database
|
||||
column. The "advanced" notation supports more `<type>` notations. These vary between database drivers, but all of them
|
||||
support the following:
|
||||
@ -73,7 +74,7 @@ support the following:
|
||||
For complex queries it may be necessary to define a complex or composite index on the supporting object. To create a
|
||||
composite index, define the fields in the index order as a comma separated list.
|
||||
|
||||
*Note* Most DBMSs only use the leftmost prefix to optimise the query, try to ensure the order of the index and your
|
||||
*Note* Most databases only use the leftmost prefix to optimise the query, try to ensure the order of the index and your
|
||||
query parameters are the same. e.g.
|
||||
- index (col1) - `WHERE col1 = ?`
|
||||
- index (col1, col2) = `WHERE (col1 = ? AND col2 = ?)`
|
||||
|
@ -426,8 +426,6 @@ Note that there is also a 'SilverStripe' way of casting fields on a class, this
|
||||
standard PHP way. See [casting](/developer_guides/model/data_types_and_casting).
|
||||
|
||||
|
||||
|
||||
|
||||
## Filesystem
|
||||
|
||||
### Don't script-execution in /assets
|
||||
@ -585,6 +583,88 @@ In a future release this behaviour will be changed to be on by default, and this
|
||||
variable will be no longer necessary, thus it will be necessary to always set
|
||||
`SS_TRUSTED_PROXY_IPS` if using a proxy.
|
||||
|
||||
## Secure Sessions, Cookies and TLS (HTTPS)
|
||||
|
||||
SilverStripe recommends the use of TLS(HTTPS) for your application, and you can easily force the use through the
|
||||
director function `forceSSL()`
|
||||
|
||||
:::php
|
||||
|
||||
if (!Director::isDev()) {
|
||||
Director::forceSSL();
|
||||
}
|
||||
|
||||
Forcing HTTPS so requires a certificate to be purchased or obtained through a vendor such as
|
||||
[lets encrypt](https://letsencrypt.org/) and configured on your web server.
|
||||
|
||||
We also want to ensure cookies are not shared between secure and non-secure sessions, so we must tell SilverStripe to
|
||||
use a [secure session](https://docs.silverstripe.org/en/3/developer_guides/cookies_and_sessions/sessions/#secure-session-cookie).
|
||||
To do this, you may set the `cookie_secure` parameter to `true` in your `config.yml` for `Session`
|
||||
|
||||
:::yml
|
||||
Session:
|
||||
cookie_secure: true
|
||||
|
||||
For other cookies set by your application we should also ensure the users are provided with secure cookies by setting
|
||||
the "Secure" and "HTTPOnly" flags. These flags prevent them from being stolen by an attacker through javascript.
|
||||
|
||||
- The `Secure` cookie flag instructs the browser not to send the cookie over an insecure HTTP connection. If this
|
||||
flag is not present, the browser will send the cookie even if HTTPS is not in use, which means it is transmitted in
|
||||
clear text and can be intercepted and stolen by an attacker who is listening on the network.
|
||||
|
||||
- The `HTTPOnly` flag lets the browser know whether or not a cookie should be accessible by client-side JavaScript
|
||||
code. It is best practice to set this flag unless the application is known to use JavaScript to access these cookies
|
||||
as this prevents an attacker who achieves cross-site scripting from accessing these cookies.
|
||||
|
||||
|
||||
:::php
|
||||
|
||||
Cookie::set('cookie-name', 'chocolate-chip', $expiry = 30, $path = null, $domain = null, $secure = true,
|
||||
$httpOnly = false
|
||||
);
|
||||
|
||||
## Security Headers
|
||||
|
||||
In addition to forcing HTTPS browsers can support additional security headers which can only allow access to a website
|
||||
via a secure connection. As browsers increasingly provide negative feedback regarding unencrypted HTTP connections,
|
||||
ensuring an HTTPS connection will provide a better and more secure user experience.
|
||||
|
||||
- The `Strict-Transport-Security` header instructs the browser to record that the website and assets on that website
|
||||
MUST use a secure connection. This prevents websites from becoming insecure in the future from stray absolute links
|
||||
or references without https from external sites. Check if your browser supports [HSTS](https://hsts.badssl.com/)
|
||||
- `max-age` can be configured to anything in seconds: `max-age=31536000` (1 year), for roll out, consider something
|
||||
lower
|
||||
- `includeSubDomains` to ensure all present and future sub domains will also be HTTPS
|
||||
|
||||
For sensitive pages, such as members areas, or places where sensitive information is present, adding cache control
|
||||
headers can explicitly instruct browsers not to keep a local cached copy of content and can prevent content from
|
||||
being cached throughout the infrastructure (e.g. Proxy, caching layers, WAF etc).
|
||||
|
||||
- The headers `Cache-control: no-store` and `Pragma: no-cache` along with expiry headers of `Expires: <current date>`
|
||||
and `Date: <current date>` will ensure that sensitive content is not stored locally or able to be retrieved by
|
||||
unauthorised local persons. SilverStripe adds the current date for every request, and we can add the other cache
|
||||
headers to the request for our secure controllers:
|
||||
|
||||
|
||||
:::php
|
||||
|
||||
class MySecureController extends Controller {
|
||||
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
// Add cache headers to ensure sensitive content isn't cached.
|
||||
$this->response->addHeader('Cache-Control', 'max-age=0, must-revalidate, no-transform');
|
||||
$this->response->addHeader('Pragma', 'no-cache'); // for HTTP 1.0 support
|
||||
|
||||
HTTP::set_cache_age(0);
|
||||
HTTP::add_cache_headers($this->response);
|
||||
|
||||
// Add HSTS header to force TLS for document content
|
||||
$this->response->addHeader('Strict-Transport-Security', 'max-age=86400; includeSubDomains');
|
||||
}
|
||||
}
|
||||
|
||||
## Related
|
||||
|
||||
* [http://silverstripe.org/security-releases/](http://silverstripe.org/security-releases/)
|
||||
|
Loading…
Reference in New Issue
Block a user