---
title: Security checklist
slug: cms-12/docs/security-checklist
docTags: 
createdAt: 2026-08-10T15:55:58.000Z
---

(Thanks to Daniel Ovaska for providing the foundation for this checklist.)

## Background

Optimizely Content Management System (CMS) provides a flexible and granular **user/role-based authorization security model**, which reflects best practice approaches widely employed by enterprise-level platforms. Individual access can be controlled through Optimizely's standard authentication and authorization mechanisms.

**User and role permissions** can be enforced on any section of the website and on all levels of content, including products, pages, and content blocks. Securing standard CMS navigation and UI elements is also possible based on user/group permissions.

Using the CMS user interface, administrators can create users, groups, and roles to grant permissions to content items, pages, types of pages, blocks, media, files, folders, and language variations.

**Access to editing** is administered by CMS admin users, who can then define what groups are given access to editing.

For properties and tabs used within content types, access can be restricted via code only. CMS Admin UI does not support this yet.

Optimizely enables **templates** to be shared across sites, including styles and branding if desired. When created, templates can be applied with permissions only to be visible to certain users/groups of the CMS. See [Security](docId\:rOYV_JmScrD0OnwwgIpUO).

## Checklist

Consider the suggestions below to make your site as secure as possible.

### High priority

- **Use HTTPS**
  Without HTTPS you are wide open for a bunch of man-in-the-middle (MITM) attacks. Do not release a site without it. Use it on the entire site and not only on the logged-in part. See [Enforce HTTPS in ASP.NET Core.](https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-8.0\&tabs=visual-studio)
- **Secure cookies**
  For an HTTPS site, your application cookies should be marked as secure. To ensure that the application cookie is secure in production, you can configure like:

```csharp
services.ConfigureApplicationCookie(c => c.Cookie.SecurePolicy = _webHostingEnvironment.IsDevelopment() ?
  Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest :
  Microsoft.AspNetCore.Http.CookieSecurePolicy.Always);
```

You can use `CookiePolicyMiddleware` and configure `CookiePolicyOptions` to specify policies that should apply to all cookies for the application.

- **Make sure the application is configured with the environment set to production**
  To do so, make sure the environment variable `ASPNETCORE_ENVIRONMENT` or `DOTNET_ENVIRONMENT` is set to *Production*.
- **Avoid click-jacking attacks**
  Add a response header for X-Frame-Options. This could be done, for example, from a middleware component or a global action filter.
- **Restrict editors**
  Give them the least access rights needed and avoid creating shared accounts. If something goes wrong, you want to know who did what. Use WebEditors to access edit mode only and a separate role to give access to the part of the content tree "Editors\_Sweden, Editors\_Norway" or similar.
- **Remove test users**
  Admin accounts used during development must be deleted before launch.
- **Use a service account for scheduled jobs and similar**
  The developer uses his account instead of creating a separate service account to run scheduled jobs, using a specific service account for tasks like these instead.
- **Check that your search result page (SRP) never displays secured content**
  Ensure that excerpts of secured content are not displayed to anonymous users on the search result page.
- **Check that pages in wastebasket are not shown / crashes site**
  If you list content in any way by using `GetChildren`  or  `FindPagesByCriteria` in the background, filter your list for access rights before displaying them. EPiServer controls do this, but if you use custom code to render your lists or menus, you must solve this problem to avoid any issues. Test by sending some content to wastebasket to ensure it is handled correctly. They should not be visible in menus or search results, for instance.
- **Validate user input**
  Validate forms of user input against an allow list (with valid characters) on the server side. You can, for example, use a regular expression to check that the string from the users only contains normal characters. If you are using user profile pages, double-check that these validate input correctly because they may be targeted. If you need country-specific allow lists, store the regular expression as a setting on a multi-site.
- **Validate querystring parameters**
  Similar to the one above; however, easier to forget but no less important.
- **HTML encode all output**
  This is especially important for data from other systems/user input to avoid JavaScript cross-site scripting (XSS) attacks.
  If you are using MVC, use the standard **Model.** syntax and avoid `Html.Raw`.
- **Lock down webapi and custom endpoints**
  Optimizely handles security for content out-of-the-box, but you should check your solution for other access points, such as web API, and custom endpoints, and secure them or validate all input.
- **Lock down addons**
  Check if you have admin/editor add-ons and similar, and secure them. You can add the attribute `authorizationPolicy` to the modules [module.config](docId\:RZVSXXKVRj26ep5OsAUB-).
- **Filter your lists for access rights**
  If you list content in any way by using `GetChildren` or `FindPagesByCriteria` in the background, filter your list for access rights before displaying them. The Optimizely controls do this, but if you use custom code to render your lists or menus, you must solve this problem to avoid showing restricted content.
- **Move your log files**
  Having your log files in the web root is not a good idea from a security point of view. Ideally, they should be on a separate hard drive (because too large log files may crash the website).
- **File access rights**
  Double-check that your file access rights are correct and that no one has added full access rights for everyone when troubleshooting. Check this URL if you are unsure: [Understand Built-In User and Group Accounts in IIS 7](https://learn.microsoft.com/en-us/iis/get-started/planning-for-security/understanding-built-in-user-and-group-accounts-in-iis).
- **Secure your service layer**
  For websites that are more application-oriented, it is wise to secure your service layer so that some functions are only available to certain roles, for example, delete user can only be run by someone logged in as administrator. It is easy to hide the button for this in the presentation layer, but it is more secure to add it above your service layer. One way of implementing this is using custom attributes and AOP.
- **Secure your data layer**
  Make sure you use an Object/Relational Mapping (ORM) framework like Entity Framework (EF) that does not allow SQL injections and that you never string concatenate an SQL statement together. It might be worth checking those for string concatenation if you run SQL-stored procedures.
  - **Double-check your caching strategy**
    Make sure you never cache non-public content. For example, suppose you cache a menu (filtered on access rights) and go to the site with an admin user followed by an anonymous user. In that case, you may display menu items to the anonymous user that only admins should see. Normally, you do not need to cache Optimizely content lists if you use `GetChildren`; Optimizely does that for you.
- **Secure your custom file providers**
  If you use custom file providers, do you check access rights on folders you want?
- **Secure your scheduled jobs**
  Suppose your scheduled jobs do not work on your production site. In that case, it may be because scheduled jobs run as your current user when you press **Start manually** in the admin view, but as an anonymous user when you run them automatically. If the anonymous access rights are insufficient, you must log in programmatically to execute the job as a specific user.
  See Magnus Rahl's blog post [Run a scheduled job as a specific role](https://world.optimizely.com/blogs/Magnus-Rahl/Dates/2010/12/Run-a-scheduled-job-as-a-specific-role/).
  Just ensure you know what you are doing if you run the job as admin. You might end up sending out restricted information if you are not careful.
- **Performance**
  Although performance is a separate issue, bad performance can also be used to kill a website. Check your logs to find all slow pages, optimize them, and add load balancing if needed. Remember that this performance issue applies to all components in your solution including the SQL server, DNS, SSO, and so on.
- **Email is not a secure channel**
  Never send passwords or other sensitive information in email. Send a temporary link instead.
- **Prevent cross-site forgery**
  ASP.NET Core applications use anti-forgery by default for most usages. You might need to handle it yourself in some limited use cases. See [Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core](http://blog.stevensanderson.com/2008/09/01/prevent-cross-site-request-forgery-csrf-using-aspnet-mvcs-antiforgerytoken-helper/).
- **Require strong passwords**
  Long passwords beat complex passwords. A minimum of 9 characters is recommended. Preferably more if your users do not start screaming too loud.
- **Check your SSL certificate**
  Old versions of protocols and algorithms are vulnerable to attacks. Use [SSL Server Test](https://www.ssllabs.com/ssltest/analyze.html) to check if you use the latest versions without known security holes.
- **Consider file extensions allowed to be uploaded**
  Decide on which file extensions you would allow to be uploaded into the CMS. Limit allowed file extensions using the [MediaDescriptor](docId\:PoSRfEHIRA17AO374yEjP) and explicitly specify file extensions in the `UploadOptions.AllowedFile` configuration [AllowedFileExtensions](docId\:goGbeSqsnaW9avt7BpeSx)
