mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-09-17 23:16:04 +02:00
554 lines
22 KiB
Markdown
554 lines
22 KiB
Markdown
# Security
|
|
|
|
## Introduction
|
|
|
|
This page details notes on how to ensure that we develop secure SilverStripe applications.
|
|
See our "[Release Process](/misc/release-process#security-releases) on how to report security issues.
|
|
|
|
## SQL Injection
|
|
|
|
The [coding-conventions](/misc/coding-conventions) help guard against SQL injection attacks but still require developer
|
|
diligence: ensure that any variable you insert into a filter / sort / join clause is either parameterised, or has been
|
|
escaped.
|
|
|
|
See [http://shiflett.org/articles/sql-injection](http://shiflett.org/articles/sql-injection).
|
|
|
|
### Parameterised queries
|
|
|
|
Parameterised queries, or prepared statements, allow the logic around the query and its structure to be separated from
|
|
the parameters passed in to be executed. Many DB adaptors support these as standard including [PDO](http://php.net/manual/en/pdo.prepare.php),
|
|
[MySQL](http://php.net/manual/en/mysqli.prepare.php), [SQL Server](http://php.net/manual/en/function.sqlsrv-prepare.php),
|
|
[SQLite](http://php.net/manual/en/sqlite3.prepare.php), and [PostgreSQL](http://php.net/manual/en/function.pg-prepare.php).
|
|
|
|
The use of parameterised queries whenever possible will safeguard your code in most cases, but care
|
|
must still be taken when working with literal values or table/column identifiers that may
|
|
come from user input.
|
|
|
|
Example:
|
|
|
|
:::php
|
|
$records = DB::preparedQuery('SELECT * FROM "MyClass" WHERE "ID" = ?', array(3));
|
|
$records = MyClass::get()->where(array('"ID" = ?' => 3));
|
|
$records = MyClass::get()->where(array('"ID"' => 3));
|
|
$records = DataObject::get_by_id('MyClass', 3);
|
|
$records = DataObject::get_one('MyClass', array('"ID" = ?' => 3));
|
|
$records = MyClass::get()->byID(3);
|
|
$records = SQLSelect::create()->addWhere(array('"ID"' => 3))->execute();
|
|
|
|
Parameterised updates and inserts are also supported, but the syntax is a little different
|
|
|
|
:::php
|
|
SQLInsert::create('"MyClass"')
|
|
->assign('"Name"', 'Daniel')
|
|
->addAssignments(array(
|
|
'"Position"' => 'Accountant',
|
|
'"Age"' => array(
|
|
'GREATEST(0,?,?)' => array(24, 28)
|
|
)
|
|
))
|
|
->assignSQL('"Created"', 'NOW()')
|
|
->execute();
|
|
DB::preparedQuery(
|
|
'INSERT INTO "MyClass" ("Name", "Position", "Age", "Created") VALUES(?, ?, GREATEST(0,?,?), NOW())'
|
|
array('Daniel', 'Accountant', 24, 28)
|
|
);
|
|
|
|
|
|
### Automatic escaping
|
|
|
|
SilverStripe internally will use parameterised queries in SQL statements wherever possible.
|
|
|
|
If necessary Silverstripe performs any required escaping through database-specific methods (see `[api:Database->addslashes()]`).
|
|
For `[api:MySQLDatabase]`, this will be `[mysql_real_escape_string()](http://de3.php.net/mysql_real_escape_string)`.
|
|
|
|
* Most `[api:DataList]` accessors (see escaping note in method documentation)
|
|
* DataObject::get_by_id()
|
|
* DataObject::update()
|
|
* DataObject::castedUpdate()
|
|
* DataObject->Property = 'val', DataObject->setField('Property','val')
|
|
* DataObject::write()
|
|
* DataList->byID()
|
|
* Form->saveInto()
|
|
* FormField->saveInto()
|
|
* DBField->saveInto()
|
|
|
|
Data is not escaped when writing to object-properties, as inserts and updates are normally
|
|
handled via prepared statements.
|
|
|
|
Example:
|
|
|
|
:::php
|
|
// automatically escaped/quoted
|
|
$members = Member::get()->filter('Name', $_GET['name']);
|
|
// automatically escaped/quoted
|
|
$members = Member::get()->filter(array('Name' => $_GET['name']));
|
|
// parameterised condition
|
|
$members = Member::get()->where(array('"Name" = ?' => $_GET['name']));
|
|
// needs to be escaped and quoted manually (note raw2sql called with the $quote parameter set to true)
|
|
$members = Member::get()->where(sprintf('"Name" = %s', Convert::raw2sql($_GET['name'], true)));
|
|
|
|
<div class="warning" markdown='1'>
|
|
It is NOT good practice to "be sure" and convert the data passed to the functions above manually. This might
|
|
result in *double escaping* and alters the actually saved data (e.g. by adding slashes to your content).
|
|
</div>
|
|
|
|
### Manual escaping
|
|
|
|
As a rule of thumb, whenever you're creating SQL queries (or just chunks of SQL) you should use parameterisation,
|
|
but there may be cases where you need to take care of escaping yourself. See [coding-conventions](/misc/coding-conventions)
|
|
and [datamodel](/topics/datamodel) for ways to parameterise, cast, and convert your data.
|
|
|
|
* `SQLSelect`
|
|
* `DB::query()`
|
|
* `DB::preparedQuery()`
|
|
* `Director::urlParams()`
|
|
* `Controller->requestParams`, `Controller->urlParams`
|
|
* `SS_HTTPRequest` data
|
|
* GET/POST data passed to a form method
|
|
|
|
Example:
|
|
|
|
:::php
|
|
class MyForm extends Form {
|
|
public function save($RAW_data, $form) {
|
|
// Pass true as the second parameter of raw2sql to quote the value safely
|
|
$SQL_data = Convert::raw2sql($RAW_data, true); // works recursively on an array
|
|
$objs = Player::get()->where("Name = " . $SQL_data['name']);
|
|
// ...
|
|
}
|
|
}
|
|
|
|
|
|
* `FormField->Value()`
|
|
* URLParams passed to a Controller-method
|
|
|
|
Example:
|
|
|
|
:::php
|
|
class MyController extends Controller {
|
|
private static $allowed_actions = array('myurlaction');
|
|
public function myurlaction($RAW_urlParams) {
|
|
// Pass true as the second parameter of raw2sql to quote the value safely
|
|
$SQL_urlParams = Convert::raw2sql($RAW_urlParams, true); // works recursively on an array
|
|
$objs = Player::get()->where("Name = " . $SQL_data['OtherID']);
|
|
// ...
|
|
}
|
|
}
|
|
|
|
|
|
As a rule of thumb, you should escape your data **as close to querying as possible**
|
|
(or preferably, use parameterised queries). This means if you've got a chain of functions
|
|
passing data through, escaping should happen at the end of the chain.
|
|
|
|
:::php
|
|
class MyController extends Controller {
|
|
/**
|
|
* @param array $RAW_data All names in an indexed array (not SQL-safe)
|
|
*/
|
|
public function saveAllNames($RAW_data) {
|
|
// $SQL_data = Convert::raw2sql($RAW_data); // premature escaping
|
|
foreach($RAW_data as $item) $this->saveName($item);
|
|
}
|
|
|
|
public function saveName($RAW_name) {
|
|
$SQL_name = Convert::raw2sql($RAW_name, true);
|
|
DB::query("UPDATE Player SET Name = {$SQL_name}");
|
|
}
|
|
}
|
|
|
|
This might not be applicable in all cases - especially if you are building an API thats likely to be customized. If
|
|
you're passing unescaped data, make sure to be explicit about it by writing *phpdoc*-documentation and *prefixing* your
|
|
variables ($RAW_data instead of $data).
|
|
|
|
|
|
## XSS (Cross-Site-Scripting)
|
|
|
|
SilverStripe helps you guard any output against clientside attacks initiated by malicious user input, commonly known as
|
|
XSS (Cross-Site-Scripting). With some basic guidelines, you can ensure your output is safe for a specific use case (e.g.
|
|
displaying a blog post in HTML from a trusted author, or escaping a search parameter from an untrusted visitor before
|
|
redisplaying it).
|
|
|
|
<div class="notice" markdown='1'>
|
|
Note: SilverStripe templates do not remove tags, please use [strip_tags()](http://php.net/strip_tags) for this purpose
|
|
or [sanitize](http://htmlpurifier.org/) it correctly.
|
|
</div>
|
|
|
|
See [http://shiflett.org/articles/foiling-cross-site-attacks](http://shiflett.org/articles/foiling-cross-site-attacks)
|
|
for in-depth information about "Cross-Site-Scripting".
|
|
|
|
### What if I can't trust my editors?
|
|
|
|
The default configuration of SilverStripe assumes some level of trust is given to your editors who have access
|
|
to the CMS. Though the HTML WYSIWYG editor is configured to provide some control over the HTML an editor provides,
|
|
this is not enforced server side, and so can be bypassed by a malicious editor. A editor that does so can use an
|
|
XSS attack against an admin to perform any administrative action.
|
|
|
|
If you can't trust your editors, SilverStripe must be configured to filter the content so that any javascript is
|
|
stripped out
|
|
|
|
To enable filtering, set the HtmlEditorField::$sanitise_server_side [configuration](/topics/configuration) property to
|
|
true, e.g.
|
|
|
|
HtmlEditorField::config()->sanitise_server_side = true
|
|
|
|
The built in sanitiser enforces the TinyMCE whitelist rules on the server side, and is sufficient to eliminate the
|
|
most common XSS vectors.
|
|
|
|
However some subtle XSS attacks that exploit HTML parsing bugs need heavier filtering. For greater protection
|
|
you can install the [htmlpurifier](https://github.com/silverstripe-labs/silverstripe-htmlpurifier) module which
|
|
will replace the built in sanitiser with one that uses the [HTML Purifier](http://htmlpurifier.org/) library.
|
|
In both cases, you must ensure that you have not configured TinyMCE to explicitly allow script elements or other
|
|
javascript-specific attributes.
|
|
|
|
For `HTMLText` database fields which aren't edited through `HtmlEditorField`, you also
|
|
have the option to explicitly whitelist allowed tags in the field definition, e.g. `"MyField" => "HTMLText('meta','link')"`.
|
|
The `SiteTree.ExtraMeta` property uses this to limit allowed input.
|
|
|
|
##### But I also need my editors to provide javascript
|
|
|
|
It is not currently possible to allow editors to provide javascript content and yet still protect other users
|
|
from any malicious code within that javascript.
|
|
|
|
We recommend configuring [shortcodes](/reference/shortcodes) that can be used by editors in place of using javascript directly.
|
|
|
|
### Escaping model properties
|
|
|
|
`[api:SSViewer]` (the SilverStripe template engine) automatically takes care of escaping HTML tags from specific
|
|
object-properties by [casting](/topics/datamodel#casting) its string value into a `[api:DBField]` object.
|
|
|
|
PHP:
|
|
|
|
:::php
|
|
class MyObject extends DataObject {
|
|
private static $db = array(
|
|
'MyEscapedValue' => 'Text', // Example value: <b>not bold</b>
|
|
'MyUnescapedValue' => 'HTMLText' // Example value: <b>bold</b>
|
|
);
|
|
}
|
|
|
|
|
|
Template:
|
|
|
|
:::php
|
|
<ul>
|
|
<li>$MyEscapedValue</li> // output: <b>not bold<b>
|
|
<li>$MyUnescapedValue</li> // output: <b>bold</b>
|
|
</ul>
|
|
|
|
|
|
The example below assumes that data wasn't properly filtered when saving to the database, but are escaped before
|
|
outputting through SSViewer.
|
|
|
|
### Overriding default escaping in templates
|
|
|
|
You can force escaping on a casted value/object by using an [escape type](/topics/datamodel) method in your template, e.g.
|
|
"XML" or "ATT".
|
|
|
|
Template (see above):
|
|
|
|
:::php
|
|
<ul>
|
|
// output: <a href="#" title="foo & &#quot;bar"">foo & "bar"</a>
|
|
<li><a href="#" title="$Title.ATT">$Title</a></li>
|
|
<li>$MyEscapedValue</li> // output: <b>not bold<b>
|
|
<li>$MyUnescapedValue</li> // output: <b>bold</b>
|
|
<li>$MyUnescapedValue.XML</li> // output: <b>bold<b>
|
|
</ul>
|
|
|
|
|
|
### Escaping custom attributes and getters
|
|
|
|
Every object attribute or getter method used for template purposes should have its escape type defined through the
|
|
static *$casting* array. Caution: Casting only applies when using values in a template, not in PHP.
|
|
|
|
PHP:
|
|
|
|
:::php
|
|
class MyObject extends DataObject {
|
|
public $Title = '<b>not bold</b>'; // will be escaped due to Text casting
|
|
|
|
$casting = array(
|
|
"Title" => "Text", // forcing a casting
|
|
'TitleWithHTMLSuffix' => 'HTMLText' // optional, as HTMLText is the default casting
|
|
);
|
|
|
|
public function TitleWithHTMLSuffix($suffix) {
|
|
// $this->Title is not casted in PHP
|
|
return $this->Title . '<small>(' . $suffix. ')</small>';
|
|
}
|
|
}
|
|
|
|
|
|
Template:
|
|
|
|
:::php
|
|
<ul>
|
|
<li>$Title</li> // output: <b>not bold<b>
|
|
<li>$Title.RAW</li> // output: <b>not bold</b>
|
|
<li>$TitleWithHTMLSuffix</li> // output: <b>not bold</b>: <small>(...)</small>
|
|
</ul>
|
|
|
|
|
|
Note: Avoid generating HTML by string concatenation in PHP wherever possible to minimize risk and separate your
|
|
presentation from business logic.
|
|
|
|
### Manual escaping in PHP
|
|
|
|
When using *customise()* or *renderWith()* calls in your controller, or otherwise forcing a custom context for your
|
|
template, you'll need to take care of casting and escaping yourself in PHP.
|
|
|
|
The `[api:Convert]` class has utilities for this, mainly *Convert::raw2xml()* and *Convert::raw2att()* (which is
|
|
also used by *XML* and *ATT* in template code).
|
|
|
|
PHP:
|
|
|
|
:::php
|
|
class MyController extends Controller {
|
|
private static $allowed_actions = array('search');
|
|
public function search($request) {
|
|
$htmlTitle = '<p>Your results for:' . Convert::raw2xml($request->getVar('Query')) . '</p>';
|
|
return $this->customise(array(
|
|
'Query' => Text::create($request->getVar('Query')),
|
|
'HTMLTitle' => HTMLText::create($htmlTitle)
|
|
));
|
|
}
|
|
}
|
|
|
|
|
|
Template:
|
|
|
|
:::php
|
|
<h2 title="Searching for $Query.ATT">$HTMLTitle</h2>
|
|
|
|
|
|
Whenever you insert a variable into an HTML attribute within a template, use $VarName.ATT, no not $VarName.
|
|
|
|
You can also use the built-in casting in PHP by using the *obj()* wrapper, see [datamodel](/topics/datamodel) .
|
|
|
|
### Escaping URLs
|
|
|
|
Whenever you are generating a URL that contains querystring components based on user data, use urlencode() to escape the
|
|
user data, not *Convert::raw2att()*. Use raw ampersands in your URL, and cast the URL as a "Text" DBField:
|
|
|
|
PHP:
|
|
|
|
:::php
|
|
class MyController extends Controller {
|
|
private static $allowed_actions = array('search');
|
|
public function search($request) {
|
|
$rssRelativeLink = "/rss?Query=" . urlencode($_REQUEST['query']) . "&sortOrder=asc";
|
|
$rssLink = Controller::join_links($this->Link(), $rssRelativeLink);
|
|
return $this->customise(array(
|
|
"RSSLink" => Text::create($rssLink),
|
|
));
|
|
}
|
|
}
|
|
|
|
|
|
Template:
|
|
|
|
:::php
|
|
<a href="$RSSLink.ATT">RSS feed</a>
|
|
|
|
|
|
Some rules of thumb:
|
|
|
|
* Don't concatenate URLs in a template. It only works in extremely simple cases that usually contain bugs.
|
|
* Use *Controller::join_links()* to concatenate URLs. It deals with query strings and other such edge cases.
|
|
|
|
### Filtering incoming HTML from TinyMCE
|
|
|
|
In some cases you may be particularly concerned about which HTML elements are addable to Content via the CMS.
|
|
By default, although TinyMCE is configured to restrict some dangerous tags (such as `script` tags), this restriction
|
|
is not enforced server-side. A malicious user with write access to the CMS might create a specific request to avoid
|
|
these restrictions.
|
|
|
|
To enable server side filtering using the same whitelisting controls as TinyMCE, set the
|
|
HtmlEditorField::$sanitise_server_side config property to true.
|
|
|
|
## Cross-Site Request Forgery (CSRF)
|
|
|
|
SilverStripe has built-in countermeasures against [CSRF](http://shiflett.org/articles/cross-site-request-forgeries) identity theft for all form submissions. A form object
|
|
will automatically contain a `SecurityID` parameter which is generated as a secure hash on the server, connected to the
|
|
currently active session of the user. If this form is submitted without this parameter, or if the parameter doesn't
|
|
match the hash stored in the users session, the request is discarded.
|
|
You can disable this behaviour through `[api:Form->disableSecurityToken()]`.
|
|
|
|
It is also recommended to limit form submissions to the intended HTTP verb (mostly `GET` or `POST`)
|
|
through `[api:Form->setStrictFormMethodCheck()]`.
|
|
|
|
Sometimes you need to handle state-changing HTTP submissions which aren't handled through
|
|
SilverStripe's form system. In this case, you can also check the current HTTP request
|
|
for a valid token through `[api:SecurityToken::checkRequest()]`.
|
|
|
|
## Casting user input
|
|
|
|
When working with `$_GET`, `$_POST` or `Director::urlParams` variables, and you know your variable has to be of a
|
|
certain type, like an integer, then it's essential to cast it as one. *Why?* To be sure that any processing of your
|
|
given variable is done safely, with the assumption that it's an integer.
|
|
|
|
To cast the variable as an integer, place `(int)` or `(integer)` before the variable.
|
|
|
|
For example: a page with the URL paramaters *mysite.com/home/add/1* requires that ''Director::urlParams['ID']'' be an
|
|
integer. We cast it by adding `(int)` - ''(int)Director::urlParams['ID']''. If a value other than an integer is
|
|
passed, such as *mysite.com/home/add/dfsdfdsfd*, then it returns 0.
|
|
|
|
Below is an example with different ways you would use this casting technique:
|
|
|
|
:::php
|
|
public function CaseStudies() {
|
|
|
|
// cast an ID from URL parameters e.g. (mysite.com/home/action/ID)
|
|
$anotherID = (int)Director::urlParam['ID'];
|
|
|
|
// perform a calculation, the prerequisite being $anotherID must be an integer
|
|
$calc = $anotherID + (5 - 2) / 2;
|
|
|
|
// cast the 'category' GET variable as an integer
|
|
$categoryID = (int)$_GET['category'];
|
|
|
|
// perform a byID(), which ensures the ID is an integer before querying
|
|
return CaseStudy::get()->byID($categoryID);
|
|
}
|
|
|
|
|
|
The same technique can be employed anywhere in your PHP code you know something must be of a certain type. A list of PHP
|
|
cast types can be found here:
|
|
|
|
* `(int)`, `(integer)` - cast to integer
|
|
* `(bool)`, `(boolean)` - cast to boolean
|
|
* `(float)`, `(double)`, `(real)` - cast to float
|
|
* `(string)` - cast to string
|
|
* `(array)` - cast to array
|
|
* `(object)` - cast to object
|
|
|
|
Note that there is also a 'SilverStripe' way of casting fields on a class, this is a different type of casting to the
|
|
standard PHP way. See [casting](/topics/datamodel#casting).
|
|
|
|
|
|
|
|
|
|
## Filesystem
|
|
|
|
### Don't allow script-execution in /assets
|
|
|
|
As all uploaded files are stored by default on the /assets-directory, you should disallow script-execution for this
|
|
folder. This is just an additional security-measure to making sure you avoid directory-traversal, check for filesize and
|
|
disallow certain filetypes.
|
|
|
|
Example configuration for Apache2:
|
|
|
|
<VirtualHost *:80>
|
|
<LocationMatch assets/>
|
|
php_flag engine off
|
|
Options -ExecCGI -Includes -Indexes
|
|
</LocationMatch>
|
|
</VirtualHost>
|
|
|
|
|
|
If you are using shared hosting or in a situation where you cannot alter your Vhost definitions, you can use a .htaccess
|
|
file in the assets directory. This requires PHP to be loaded as an Apache module (not CGI or FastCGI).
|
|
|
|
**/assets/.htaccess**
|
|
|
|
php_flag engine off
|
|
Options -ExecCGI -Includes -Indexes
|
|
|
|
### Don't allow access to .yml files
|
|
|
|
Yaml files are often used to store sensitive or semi-sensitive data for use by SilverStripe framework (for instance,
|
|
configuration and test fixtures).
|
|
|
|
You should therefore block access to all yaml files (extension .yml) by default, and white list only yaml files
|
|
you need to serve directly.
|
|
|
|
See [Apache](/installation/webserver) and [Nginx](/installation/nginx) installation documentation for details
|
|
specific to your web server
|
|
|
|
### User uploaded files
|
|
|
|
Certain file types are by default excluded from user upload. html, xhtml, htm, and xml files may have embedded,
|
|
or contain links to, external resources or scripts that may hijack browser sessions and impersonate that user.
|
|
Even if the uploader of this content may be a trusted user, there is no safeguard against these users being
|
|
deceived by the content source.
|
|
|
|
Flash files (swf) are also prone to a variety of security vulnerabilities of their own, and thus by default are
|
|
disabled from file upload. As a standard practice, any users wishing to allow flash upload to their sites should
|
|
take the following precautions:
|
|
|
|
* Only allow flash uploads from trusted sources, preferably those with available source.
|
|
* Make use of the [AllowScriptAccess](http://helpx.adobe.com/flash/kb/control-access-scripts-host-web.html)
|
|
parameter to ensure that any embedded Flash file is isolated from its environments scripts. In an ideal
|
|
situation, all flash content would be served from another domain, and this value is set to "sameDomain". If this
|
|
is not feasible, this should be set to "never". For trusted flash files you may set this to "sameDomain" without
|
|
an isolated domain name, but do so at your own risk.
|
|
* Take note of any regional cookie legislation that may affect your users. See
|
|
[Cookie Law and Flash Cookies](http://eucookiedirective.com/cookie-law-and-flash-cookies/).
|
|
|
|
See [the Adobe Flash security page](http://www.adobe.com/devnet/flashplayer/security.html) for more information.
|
|
|
|
ADMIN privileged users may be allowed to override the above upload restrictions if the
|
|
`File.apply_restrictions_to_admin` config is set to false. By default this is true, which enforces these
|
|
restrictions globally.
|
|
|
|
Additionally, if certain file uploads should be made available to non-privileged users, you can add them to the
|
|
list of allowed extensions by adding these to the `File.allowed_extensions` config.
|
|
|
|
## Passwords
|
|
|
|
SilverStripe stores passwords with a strong hashing algorithm (blowfish) by default
|
|
(see [api:PasswordEncryptor]). It adds randomness to these hashes via
|
|
salt values generated with the strongest entropy generators available on the platform
|
|
(see [api:RandomGenerator]). This prevents brute force attacks with
|
|
[Rainbow tables](http://en.wikipedia.org/wiki/Rainbow_table).
|
|
|
|
Strong passwords are a crucial part of any system security.
|
|
So in addition to storing the password in a secure fashion,
|
|
you can also enforce specific password policies by configuring
|
|
a [api:PasswordValidator]:
|
|
|
|
:::php
|
|
$validator = new PasswordValidator();
|
|
$validator->minLength(7);
|
|
$validator->checkHistoricalPasswords(6);
|
|
$validator->characterStrength(3, array("lowercase", "uppercase", "digits", "punctuation"));
|
|
Member::set_password_validator($validator);
|
|
|
|
In addition, you can tighten password security with the following configuration settings:
|
|
|
|
* `Member.password_expiry_days`: Set the number of days that a password should be valid for.
|
|
* `Member.lock_out_after_incorrect_logins`: Number of incorrect logins after which
|
|
the user is blocked from further attempts for the timespan defined in `$lock_out_delay_mins`
|
|
* `Member.lock_out_delay_mins`: Minutes of enforced lockout after incorrect password attempts.
|
|
Only applies if `lock_out_after_incorrect_logins` is greater than 0.
|
|
* `Security.remember_username`: Set to false to disable autocomplete on login form
|
|
|
|
## Clickjacking: Prevent iframe Inclusion
|
|
|
|
"[Clickjacking](http://en.wikipedia.org/wiki/Clickjacking)" is a malicious technique
|
|
where a web user is tricked into clicking on hidden interface elements, which can
|
|
lead to the attacker gaining access to user data or taking control of the website behaviour.
|
|
|
|
You can signal to browsers that the current response isn't allowed to be
|
|
included in HTML "frame" or "iframe" elements, and thereby prevent the most common
|
|
attack vector. This is done through a HTTP header, which is usually added in your
|
|
controller's `init()` method:
|
|
|
|
:::php
|
|
class MyController extends Controller {
|
|
public function init() {
|
|
parent::init();
|
|
$this->response->addHeader('X-Frame-Options', 'SAMEORIGIN');
|
|
}
|
|
}
|
|
|
|
|
|
This is a recommended option to secure any controller which displays
|
|
or submits sensitive user input, and is enabled by default in all CMS controllers,
|
|
as well as the login form.
|
|
|
|
## Related
|
|
|
|
* [http://silverstripe.org/security-releases/](http://silverstripe.org/security-releases/)
|
|
* [Best-practices for securing MySQL (securityfocus.com)](http://www.securityfocus.com/infocus/1726)
|