title |
---|
5.1.0 (unreleased) |
This release includes several security fixes. Review the individual vulnerability disclosure for more detailed descriptions of each security fix. We highly encourage upgrading your project to include the latest security patches.
We have provided a severity rating of the vulnerabilities below based on the CVSS score. Note that the impact of each vulnerability could vary based on the specifics of each project. You can read the severity rating definitions in the Silverstripe CMS release process.
- CVE-2023-32302 - Members with no password can be created and bypass custom login forms Severity: Low
When a new
Member
record was created in the cms it was possible to set a blank password. If an attacker knows the email address of the user with the blank password then they can attempt to log in using an empty password. The default member authenticator, login form and basic auth all require a non-empty password, however if a custom authentication method is used it may allow a successful login with the empty password. Starting with this release, blank passwords are no no longer allowed when members are created in the CMS. Programatically createdMember
records, such as those used in unit tests, still allow blank passwords. You may have someMember
records in your system already which have empty passwords. To detect these, you can loop over allMember
records withMember::get()
and pass each record into the below method. It might be sensible to create aBuildTask
for this purpose.Once you have identified the records with empty passwords, it's up to you how to handle this. The most sensible way to resolve this is probably to generate a new secure password for each of these members, mark it as immediately expired, and email each affected member (assuming they have a valid email address in the system).private function memberHasBlankPassword(Member $member): bool { // skip default admin as this is created programatically if ($member->isDefaultAdmin()) { return false; } // return true if a blank password is valid for this member $authenticator = new MemberAuthenticator(); return $authenticator->checkPassword($member, '')->isValid(); }
When looping over nested relationships the ORM is prone to the N + 1 query problem where excessive database calls are made. Eager loading has been introduced via the new DataList::eagerLoad()
method which alleviates the N + 1 problem by querying the nested relationship tables before they are needed using a single large WHERE ID in ($ids)
SQL query instead of many WHERE RelationID = $id
queries.
Imagine the following example where there is a Team
model with 20 records, with a has_many
relation "Players"
// Regular ORM usage without eager loading
// This would result in 21 SQL SELECT queries, 1 for Teams and 20 for Players
$teams = Team::get();
// Using the `eagerLoad()` method to eager load data from nested models (up to 3 relations deep)
// This will result in only 2 SQL SELECT queries, 1 for Teams and 1 for Players
$teams = Team::get()->eagerLoad('Players');
foreach ($teams as $team) {
foreach ($team->Players() as $player) {
echo $player->FirstName;
}
}
In a test setup with looping through 100 DataObjects each with 100 related DataObjects for a total of 10,000 records per test run, the following performance improvements were observed for different types of relations (eager-loading vs not eager-loading):
- HasOne - 3227% faster (0.0078s vs 0.2595s)
- HasMany - 25% faster (0.1453s vs 0.1819s)
- ManyMany - 25% faster (0.1664s vs 0.2083s)
- ManyManyThrough - 16% faster (0.6586s vs 0.7681s)
Read more about eager loading including its limitations in the developer docs.
- The CMS search has been optimised to reduce the number of database queries made when searching for pages with elemental content blocks. This has resulted in a small performance improvement. In our test environment, with 1,000 pages each with 5 content blocks we observed a 9% performance improvement. Performance will vary with your environment.
- A new opt-in behaviour is available that makes a very large difference to performance when using elemental content blocks. In testing, this behaviour was more than 100% faster (i.e. halving the response time) of the sitetree search request. The opt-in feature disables the default behaviour of rendering all content blocks for CMS search. Instead, it simply extracts the database contents of the elements from its text and html fields. There is a downside to consider which is that any related content not directly on the element will not be matched against the search query. Note this does not use the
$searchable_fields
config. To opt-in to this behaviour, use the following config:
DNADesign\Elemental\Controllers\ElementSiteTreeFilterSearch:
render_elements: false
If render_elements
is set to false
then individual fields on elements can be excluded from search by adding them to a config array:
App\MyElement:
fields_excluded_from_cms_search:
- MyFieldToExclude
- AnotherFieldToExclude
Applying the InheritedPermissionsExtension
to a DataObject
class gives you the ability to declare that only users in certain groups can view or edit those records. This extension is applied by default to the File
and SiteTree
classes.
A new permission has been added to InheritedPermissions
, which powers that extension. The new permission (InheritedPermissions::ONLY_THESE_MEMBERS
) allows you to define which specific Member
records should have access to your records, regardless of which groups those members belong to.
In the CMS, this new permission is available for files and pages by setting "Who can view/edit this page/file" to "Only these users".
DataList
queries filtering against a list of IDs have been optimised when all of the following criteria are met:
- the column being filtered is a
DBPrimarykey
or aDBForiegnKey
- the values being filtered are all either integers or valid integer strings
- using placeholders for integer ids has been configured off, which is the default config value.
If you want to disable this optimisation you can do so with this configuration:
SilverStripe\ORM\DataList:
use_placeholders_for_integer_ids: true
The following performance improvements were measured in a test setup where 10,000 record IDs were passed in:
- DataList::byIDs() - 198% faster - (0.0608s vs 0.1812s)
- RelationList::foreignIDFilter()
- HasManyList::foreignIDFilter() - 108% faster (0.1584s vs 0.3304s)
- ManyManyList::foreignIDFilter() - 108% faster (0.1529s vs 0.3119s)
- ManyManyThroughList::foreignIDFilter() - 27% faster (0.6901s vs (0.8766s)
A configuration option has been added to Session Manager to anonymize stored IP addresses for enhanced privacy and compliance.
If you want to anonymize stored IP addresses then use the following configuration:
SilverStripe\SessionManager\Models\LoginSession:
anonymize_ip: true
- You can now exclude specific
DataObject
models from the check and repair step ofdev/build
- see ORM Performance for more information. - You can change what
SearchFilter
theTreeDropdownField
uses with yaml configuration - see ORM Performance for more information. - The
i18nTextCollector
now collects strings for ORM properties (e.g.$db
fields) inDataObject
andExtension
classes, and from themes. See i18n - collecting text for more details. - Extensions which modify permissions for
Group
records which returntrue
will be respected, the same as when modifying permissions for any otherDataObject
record. - The
ListboxField
now has a react component, and can be used in react-powered contexts such as within elemental blocks - A new
FieldsValidator
class has been added, which simply callsvalidate()
on all data fields in the form to ensure fields have valid values. Functionally equivalent to an emptyRequiredFields
validator. - A configuration option has been added to
GarbageCollectionService
to limit the number of items it removes each time you run garbage collection on session data. See garbage collection for more details. - In your GraphQL schemas, you can now define the pagination limit at a schema level. See limiting pagination in the GraphQL documentation for more details.
BuildTask
now has booleanis_enabled
configuration option which has precedence over the existingBuildTask::enabled
protected class property. TheBuildTask::enabled
property has been marked as deprecated and will be removed in CMS 6 if favour of usingis_enabled
instead.- Passing an argument for
$limit
that is notarray|string|null
inSilverStripe\ORM\Search\SearchContext::getQuery()
will throw a deprecation warning. In CMS 6 the parameter type will be changed from dynamic toarray|string|null
.
- The
FileBlock::getSummaryThumbnail()
method has been marked as deprecated and will be removed in CMS 6 without equivalent functionality to replace it, as it is no longer required for the elemental block's preview summary.
- The unsupported modules
silverstripe/widgets
andsilverstripe/content-widget
were removed fromsilverstripe/recipe-blog
. They were accidentally included in the2.0.0
release ofsilverstripe/recipe-blog
. Thesilverstripe/widgets
andsilverstripe/content-widget
modules are CMS-5-compatible though unsupported. If your project relies onsilverstripe/widgets
orsilverstripe/content-widget
, manually update your project'scomposer.json
file to explicitly require these modules.
DataList::filterAny()
queries on many-many relations that use an aggregateHAVING
clause now correctly use anOR
conjunction rather than an incorrectAND
conjunction.- At some point shortly before the release of Silverstripe CMS 4.0.0, SSL support for database connections was accidentally removed. This has now been reinstated - see Using SSL in database connections for more information.
- The
cascade_duplicates
property was added to theInheritedPermissionsExtension
class so that now when duplicating any object that has theInheritedPermissionsExtension
applied, theGroupID
values in theViewerGroups
andEditGroups
mapping tables will also be duplicated so that new object retains the same viewer and editor groups as the original. - Any fields added to a model's
$summary_fields
configuration which are not backed by database fields (such as method calls) will no longer be pulled through whensearchableFields()
calls back on it (i.e. because$searchable_fields
configuration has not been explicitly declared). This means you do not need to explicitly declare$searchable_fields
for models which should only use the summary fields to filter by.
This release includes a number of bug fixes to improve a broad range of areas. Check the change logs for full details of these fixes split by module. Thank you to the community members that helped contribute these fixes as part of the release!