Security checklist for Optimizely or .NET website
So you have built a great website for your customer, but is it secure? Code review your solution for these top issues.
Added some levels to indicate in what order I would do them in.
- Level A - Most important fixes / low effort that all sites should have yesterday.
- Level AA - Important / medium effort
- Level AAA - If you complete these you are probably top 1% of the sites out there in terms of security.
Level A
- Use Https
Without https you are wide open for a whole bunch of man-in-the-middle (MITM) attacks. Don't release a site without it. Use it on the entire site and not only on the logged in part. Check that you are using at least TLS 1.2 and disallow older versions
https://docs.microsoft.com/en-us/configmgr/core/plan-design/security/enable-tls-1-2
You can also add the following to global.asax for older .NET versions (4.5) for application start to force all outgoing requests to use TLS 1.2ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
- Secure edit and admin mode
For the strongest security, use a separate editor server node. If the public server doesn't even have publishing capabilities, that part is much more secure. Check option 2 on the url below:
http://world.episerver.com/blogs/Chris-Bennett/Dates/2009/12/Server-Architecture-Options-for-EPiServer/Shut down edit mode on public web front
123456789<configuration>
...
<location path=
"episerver"
>
<system.web>
<authorization>
<deny users=
"*"
/>
</authorization>
</system.web>
</location>
If you can't afford it, at least choose hard-to-guess custom urls for util and cms and force your editors to use strong passwords.
Another cheaper option instead of a separate editor server is to use ip restrictions for the relevant directories to lock the server down.
1234567<system.webServer>
<security>
<ipSecurity allowUnlisted=
"false"
denyAction=
"NotFound"
>
<add allowed=
"true"
ipAddress=
"123.456.0.0"
subnetMast=
"255.255.0.0"
/>
</ipSecurity>
</security>
</system.webServer>
The default ip blocking will not work in DXP however, check this blog post for that
- Secure cookies
For an https site, your session cookies and authentication cookies should be marked as secure. Small fix to web.config required. Might need to have a few lines of code in the end request event in global.asax to handle the authentication cookie as well. See http://stackoverflow.com/questions/3428556/authcookie-not-secure-in-global-asax
<configuration> <system.web> <httpCookies requireSSL="true" />
- Turn off detailed errors and set compilation mode to release
Easily done in web.config. https://www.owasp.org/index.php/ASP.NET_Misconfigurations
- Avoid click jacking attacks
Add a response header for X-Frame-Options.
<system.webServer> <httpProtocol> <customHeaders> <add name="X-Frame-Options" value="SAMEORIGIN" /> </customHeaders> </httpProtocol>
- Restrict editors
Give them the least access rights needed and avoid making shared accounts. If something goes wrong, you want to know who did what. Use WebEditors to give access to 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
Especially all admin accounts to EPiServer and similar that was used during development needs to be deleted before launch.
- Use a service account for scheduled jobs and similar
It happens that the developer uses his own account instead of creating a separate service account to run scheduled jobs. Don't.
- Check that your search result page (SRP) never displays secured content.
Depending on search solution, this can be a problem. I've seen it a couple of times that excerps are shown on the SRP of secured content to the anonymous user. Double check that pages in waste basket isn't shown.
- Check that pages in waste basket is not shown / crashes site
If you list content in any way by using GetChildren / FindPagesByCriteria in the background, make sure you filter your list for access rights before displaying them. EPiServer controls do this out-of-the-box but if you use custom code to render your lists/menus you will have to solve this problem to avoid any issue. Test by sending some content to wastebasket and check that they are handled correctly. They should not be visible in menus or in search results for instance.
- Move your log files
Having your log files in web root is not a good idea from a security point of view. EPiServer has log4net as logging tool and by default they end up in webroot if you turn on logging. They should ideally be on a separate harddrive (since it happens that too large log files crash the website).
- IIS File access rights
Double check that your file access rights are correct and that noone added full access rights for everyone when they were troubleshooting something. Check this url if you are unsure:
- Email is not a secure channel
Never send passwords and similar on email. Send a temporary link instead.
- Remove unused membership providers (CMS 11 and earlier)
If you don't use your windows membership provider, remove it.
- Turn off detailed error messages for WCF
12345678<configuration>
…
<system.serviceModel>
...
<behaviors>
<serviceBehaviors>
...
<serviceDebug includeExceptionDetailInFaults=
"false"
/>
- Check vulnerabilities in used frameworks node modules / nuget
Easy to do with npm audit if you are using node modules. Running an audit using chrome will also show javascript vulnerabilities.
Locking external packages to a specific version to avoid automatic upgrades by build servers is a good idea. Using signed packages if possible is another, especially for secondary feeds like a private feed. Beware of private package high jacking detailed below if you are using multiple package sources. Visual studio 2022 has an excellent function in nuget package manager where it can display vulnerable packages in solution.
Include this in your development process regularly, read more
https://world.optimizely.com/blogs/Daniel-Ovaska/Dates/2024/6/keeping-the-website-secure-by-updating-external-packages/ - Avoid leaking user data to countries you do not trust with frontend scripts (GDPR)
Some scripts like google analytics and hotjar provide great functionality but is also a security risk that needs to be discussed. How much user data from your site are you willing to let someone else in another country control? You can easily see to which country you are sending user data to using running this script in your google chrome console window
https://github.com/tomper00/privacy-test-your-site/blob/main/scan-site.js - Require strong passwords
Long passwords beat complex passwords. A minimum of 9 characters is recommended. Preferably more if your users don't start screaming too loud. This is easy to configure on your membership provider in web.config. Make it possible to copy passwords to enable password managers to work well.
Level AA
- Use 2 factor authentication (2FA) for login
This will help secure your site against brute force attacks guessing passwords and also make it less sensitive if an email account is hacked. Many federated login services has support for 2FA. - Check SSL certificate
Old versions of protocols and algorithms are vulnerable to attacks. Use for instance:
https://www.ssllabs.com/ssltest/analyze.html or
https://cryptoreport.websecurity.symantec.com/checker/views/certCheck.jsp
to check if you are using the latest and greatest without known security holes. - Prevent cross site forgery
Do use the [ValidateAntiForgeryToken] attribute in MVC controller for post actions and @Html.AntiForgeryToken() in your views. Check out some nasty example on http://blog.stevensanderson.com/2008/09/01/prevent-cross-site-request-forgery-csrf-using-aspnet-mvcs-antiforgerytoken-helper/
- Have a company security feedback process
If Troy Hunt says jump the proper response is "How high?". Seriously though. You need to have a feedback function on your website where people can report security issues in a safe way. The proper response to someone reporting a security issue is "Thank you. We will look into it."
If you fail on this it might mean that someone decides to report the issue to the local paper instead to warn of an unsafe site. You don't want that - Double check your content caching strategies
Make sure you never cache non-public content. E.g. if you cache the menu (that is filtered on access rights) and you then surf around with an admin user followed by an anonymous user you might show menu items that only admins should see to the anonymous user. Normally you don't need to cache EPiServer content lists if you just use GetChildren. EPiServer does that for you.
- Secure your data layer
Make sure you use an ORM like EF that doesn't allow SQL injections and that you never ever string concat an SQL statement together. If you run SQL stored procedures it might be worth checking those for string concatenation as well. I've seen some ugly code hidden there as well...
- Secure your service layer
For websites that are more application oriented, it's wise to secure your service layer so that some functions are only available to certain roles e.g. delete user can only be run by someone logged in as administrator. It's easy to solve this by simply hiding the button for this in the presentation layer but a more secure way is to add it above your service layer. I use custom attributes and AOP for this but the exact implementation is less important.
- Lock down webservices and handlers
It's easy to get lazy when EPiServer is handling security for content out-of-the-box. Check through your solution for other access points that the solution uses like webservices, web api, handlers etc and secure them / validate all input.
- Lock down plugins and other .NET files
Check if you have admin/editor plugins, regular aspx files and similar and secure them. This can normally be done easily by adding a location tag in web.config and restrict it to a certain role
- Validate querystring parameters and api input
Similar to the one above but easier to forget but equally important. Let's avoid those pesky sql injections and similar.
If a user sends a customerId, does that user actually have access to that customerId? - Server side request forgery (SSRF)
With SSRF, the attacker can trick the backend itself to do requests to harmful urls. Example: Imagine you have a report generator that lists names of users in system, creates an html file and then generates a report based on that html. Now one user changes their name to <img src="http://badsite.com" />. That means that when server lists users and renders that html in backend it will try to get that image and send a request to that server. The user has tricked that backend to issue a request to a url it really shouldn't.
Clean all input from users and avoid letting users use html if possible.
Whitelist allowed characters. If possible also have a whitelist in firewall of allowed outgoing domains and ips. - Html encode all output (XSS)
Especially important for all data that comes from other systems / user input to avoid javascript cross site scripting (XSS) attacks.
For webforms, check that you are using webcontrols that support htmlencoding or the
<span class="label label-info"><%#: Item.QuestionNumber %></span>
If you are using MVC, use the standard Model. syntax and stay away from Html.Raw as much as possible.
- Reverse tab nabbing
For external links that open in a new window, also set the rel attribute to noopener to protect against reverse tab nabbing attack
<a href="some-external-link" rel="noopener" target="_blank">Some External Website</a>
- Use my performance checklist to protect vs DDoS attacks
Link to performance checklist
Level AAA
- Enable HSTS
This is used to secure https one step furter to disallow any http requests whatsoever. If you only use a http => https redirect it leaves a security hole where a man-in-the-middle can hijack the request.
https://www.troyhunt.com/understanding-http-strict-transport/
https://hstspreload.org/ - Protect vs XXE (Xml Extended Elements)
Read more here. Part of OWASP top 10 list.
Avoid using XmlTextReader at all and make sure you have .NET version 4.6 or higher and you should be pretty safe.
If you have to use XmlTextReader use
XmlTextReader myReader = new XmlTextReader(new StringReader(xml)); myReader.DtdProcessing = DtdProcessing.Prohibit;
- Use Content Security Policy, CSP to protect against XSS attacks
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP - Use a single package source for nuget / node packages
If you don't you will likely be vulnerable to supply chain attacks. If you decide to use multiple sources like an additional private package source you will need to add extra security measures. This is because it's possible to create a new version of your private package on the public package source which will then be downloaded instead. Having a single package source protects against this. Signed packages and locking down versions to a specific version to avoid automatic upgrades on CI server also helps reducing risk.
https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-24105 - Use external PEN test to test security on your site
A bit expensive but worth it for sites that need higher security - Use a code scanner like Azure Advanced security in build pipeline
Good to set up as separate pipeline since it's slow. For huge projects you might need dedicated azure build agents with more than usual disk space I've noticied. This should be rare though. License cost is per active developer in team.
If you went through this checklist you will hopefully have a more secure site than you had before. Do check out the OWASP top 10 for official recommendations as well. Nothing is unbreakable of course but let's avoid the obvious security holes at least. Ending up in the news because your website got hacked is not fun.
Great list Daniel! This covers many of the OWASP points.
I'd add to this to remember to secure any plugin URLs you add for custom plugins. If you have a folder called Plugins for example simply add another Location node in your web.config to secure ~/Plugins
Where did the "Test this websites security" button go? ;-)
They got the message Oerjan :)
Lets leave it at that, sorry for my poor sense of developer humor.
I added your suggestion to the list Janaka. I've seen some admin plugins that wasn't secured correctly so it's definitely something to think about.
Neat list, but regarding validating user input: I'd rather do things properly and protect myself against SQL attacks in other ways than not letting "Dr. Ing. John-Peter Mc'Doe" register with his proper name. And for email-adresses, you can be pretty sure to miss out on something that's actually allowed by using one of the thousands of "email regexes" out there. I know 99% of people will do just fine with just [a-Z] in the name, but for those pesky exceptions, it's going to be a nightmare to use your site.
Protect against SQL injections by using properly protected existing APIs, or parameterized SQL if you're doing that part yourself.
So to sum up Arve:
Yes to using existing API and parameterized SQL
No to storing any user input as name or email or any text field without server validation vs white list of characters. Too risky for XSS if nothing else.
I will also add the possibility to secure the editing part with ip-restrictions so you are only alowing users that are logged in to your own network to access it.
https://www.stokia.com/support/misc/web-config-ip-address-restriction.aspx
Nice list, thanks a lot!
That's a decent cheap solution instead of having a separate server for editors. Definitely a useful option that I'll add to the list, thanks for the input!
Yes, message received.
I've blocked script inclusions in blog posts, at least the most obvious way of doing it. If you used some other trick please don't blog about it - drop us a line at epw@episerver.com. :)
Updated post with a few more items and some examples...
Updated post after my latest PEN test and OWASP test of a new website with some SSL suggestions...PEN test went great btw :)
Updated with some more tools from mr Jones
Hmm, wonder why it was dated to 1st of december 2016 again just because I updated it. Oh well...nm :)
I'd just say "Use HTTPS" regardless of anything. It's cheap and ensures the integrity of your site, even if you have no log-in-functionality. Ever used In flight-WIFI that puts ads on your site? This is in practice a man-in-the-middle-attack, which is not possible if running on HTTPS.
Chrome will get you warnings in your face when site is not running on HTTPS. I would say - that's not recommendation, but demand instead.
Here two pages that can test your site security.
https://www.ssllabs.com/ssltest
https://securityheaders.io
However, verify the implementation on a dev/test/stage environment first, before it is placed at a production environment.
Updating with information about high jacking private package sources if you are using multiple sources for nuget / node etc.