diff --git a/docs/en/02_Developer_Guides/00_Model/12_Indexes.md b/docs/en/02_Developer_Guides/00_Model/12_Indexes.md index a3e2c2e72..783577f8c 100644 --- a/docs/en/02_Developer_Guides/00_Model/12_Indexes.md +++ b/docs/en/02_Developer_Guides/00_Model/12_Indexes.md @@ -2,10 +2,27 @@ title: Indexes summary: Add Indexes to your Data Model to optimize database queries. # Indexes +Indexes are a great way to improve performance in your application, especially as it grows. By adding indexes to your +data model you can reduce the time taken for the framework to find and filter data objects. -It is sometimes desirable to add indexes to your data model, whether to optimize queries or add a uniqueness constraint -to a field. This is done through the `DataObject::$indexes` map, which maps index names to descriptor arrays that -represent each index. There're several supported notations: +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 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: +- add indexes on columns which are frequently used in `filter`, `where` or `orderBy` statements +- for these, only include indexes for columns which are the most restrictive (return the least number of rows) + +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 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 `DataObject` through the `DataObject::$indexes` array which maps index names to a +descriptor. There are several supported notations: :::php ' => true, - '' => array('type' => '', 'value' => '""'), '' => 'unique("")' + '' => array( + 'type' => '', + 'value' => '""' + ), ); } - + +The `` 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 `` can be an arbitrary identifier in order to allow for more than one index on a specific database column. The "advanced" notation supports more `` notations. These vary between database drivers, but all of them support the following: - * `index`: Standard index + * `index`: Standard non unique index. * `unique`: Index plus uniqueness constraint on the value * `fulltext`: Fulltext content index -In order to use more database specific or complex index notations, we also support raw SQL as a value in the -`$indexes` definition. Keep in mind that using raw SQL is likely to make your code less portable between DBMSs. - **mysite/code/MyTestObject.php** :::php @@ -50,6 +70,25 @@ In order to use more database specific or complex index notations, we also suppo ); } +## Complex/Composite Indexes +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 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 = ?)` +- index (col1, col2, col3) = `WHERE (col1 = ? AND col2 = ? AND col3 = ?)` + +The index would not be used for a query `WHERE col2 = ?` or for `WHERE col1 = ? OR col2 = ?` + +As an alternative to a composite index, you can also create a hashed column which is a combination of information from +other columns. If this is indexed, smaller and reasonably unique it might be faster that an index on the whole column. + +## Index Creation/Destruction +Indexes are generated and removed automatically during a `dev/build`. Caution if you're working with large tables and +modify an index as the next `dev/build` will `DROP` the index, and then `ADD` it. + ## API Documentation * [api:DataObject] diff --git a/docs/en/02_Developer_Guides/09_Security/04_Secure_Coding.md b/docs/en/02_Developer_Guides/09_Security/04_Secure_Coding.md index 9dc949973..0fad9f2b4 100644 --- a/docs/en/02_Developer_Guides/09_Security/04_Secure_Coding.md +++ b/docs/en/02_Developer_Guides/09_Security/04_Secure_Coding.md @@ -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: ` +and `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/)