The fundamentals of the access control system in Webnodes and setting up a secure website.
The Access Control System
Every query for data in Webnodes is done in the context of a user session, and it is the access rights of the user related to the session that determines what you are able to do. This is deeply rooted in the API and applies to both the code you write and the edit interface. For example, if you by mistake try to update or access content in your code that the user related to the current session does not have access to, it will result in an exception.
All content objects have three properties you can set that control who has access to them:
- ReadGroupId: This setting controls who is able to see the content. This applies to pageviews, AQL or LINQ querying, and direct access via a node id. The result of a query is always filtered according to the users read access.
- EditGroupId: This controls who is able to edit a content. It applies to both the API and the UI. Being a member of this groups does not give you rights to perform any changes that will affect live content, but you are able to create preliminary revisions that can be publish by initiating a "request for publication" workflow.
- PublishGroupId: This controls who is able to change the published and live revisions. If you have not enabled the revision system, a user must be a member of this group to make any changes to the content.
Access rights in Webnodes are controlled by referring to a usergroup. A session is granted access if the user associated with the session is directly or indirectly a member of a usergroup. There are three built-in usergroups:
- Everyone: This includes any user or session. By default, all visitors are "Anonymous" when the session is created on the first request. In practice, Everyone means Anonymous users.
- System Users: This is any user that registered in the system. In practice, it means any user that is logged in.
- Administrators: Users that are flagged as an admin by the system. Each user has a check-box called admin that control this setting.
In addition to these fixed system groups, you can create your own set of usergroups. Users can be made members of groups through the "DirectUserMembers" property of each group. You can also make groups members of group through the "DirectGroupMembers" and create a hierarchy of groups. Users can through a group's group-memberships be indirect members of other groups. In terms of access, an indirect membership is equivalent to a direct group membership.
Webnodes has 3 types of users:
- Anonymous: Represent unautenticated users. These users are assigned a user ID less than -10.
- User: Users that are registered in the system but without the admin flag. These users have a user ID of more than 0 and the ID refers to the Node ID of the content representing the user.
- Administrator: Users with access to everything and in principle a member of all usergroups related to access. It can be users registered in the system but with the admin flag set, but also the special masteradmin user found in the web.config file.
- System: This is a special purpose built-in user, that in terms of access has the same rights as an admin, but unlike and admin it is not stored in the database. You can access the session of this user with WAFRuntime.SystemSession
To determine the type of user a session is associated with you can call this method: WAFContext.Session.Access.UserType that returns an enum.
Webnodes comes out of the box with a series of protection mechanisms that makes it very difficult for unauthorized users to gain access to protected data, and make changes to data or use your site as a gateway to gain access to other systems. The first line of defense relies to a large extent on the security of ASP.Net and IIS. In a hosting environment you should follow the recommendations from Microsoft like using a firewall and making sure the operating system is always up to date with the latest patches and fixes. For setting up IIS, you should see: Configuring IIS. The web.config file that comes with the Webnodes installation program contains our recommended configurations for the website.
To date, we have yet to experience any that a hacker has gained unauthorized access to data in Webnodes. Our system have been tested and hardened through several audits from third party companies in various client projects. Below we will go through common website vulnerabilities and the measures we have taken to protect against them:
SQL injection attacks
In a SQL injection attack a hacker is able to execute SQL directly on the a systems database. The hacker will typically try to construct a HTTP request that will allow him or her to inject SQL commands by inserting SQL within forms fields. This is only possible if there is a direct link between the submitted string and the SQL string sent to the database. In Webnodes all data access is done through the API, so a Webnodes programmer never compose SQL in code. The SQL is constructured internally by the ORM in CMS. Any string data passed on from a HTTP request and set to a property of a content is passed on as a parameter in the SQL command object and is not a direct part of the SQL statement. Because of this, all Webnodes sites are immune to SQL injection attacks as long as the programmer is using the Webnodes API to read and write data.
Protecting user credentials
Most users today have many user accounts in various websites. It is not uncommon that users use the same username and password across multiple sites to make it easier to remember. It is therefore extremely important that a hacker never gains access to your list of usernames and passwords in the solution. Such a list would not only allow the hacker to access accounts in your system, but most likely be able to access a variety of other external services like gmail, paypal etc.
A well established way of preventing this is to store the passwords using a "hashing" algorithm. A hashing algorithm produce a unique string for each password. This string does not actually contain the password and is comparable to a check sum. It is not an encryption algorithm, and the information in the original string is lost in the process and it is therefore mathematically impossible to regenerate a password directly from a hashed string.
However, the generation of the hash involves a secret key value. With the same key, identical passwords will produce the same hashed string. So, by looking at the hashed value of commonly used passwords and compare this to a large list of hashed passwords a hacker might be able to guess the secret key, and start to identify some of the users that use a very common password. The process is difficult and without access to the secret key it requires access to a large list of hashed passwords. A common solution is to "salt" the password, before it is hashed. This basically means adding an individual and random string to the password before it is hashed. This reduces the value of using lists of commonly used passwords. For more info see this article.
Passwords in Webnodes can be stored in 3 modes:
- Hashed and Salted (following all the recommendations found here ). This is the default way of storing passwords and it means that should a hacker for some reason get access to the database, he will not be able to get hold of the original passwords. The disadvantage is that the original password cannot be retrieved, so if a user forget his or her password, a new one must be created.
- Encrypted. In this mode each password is stored in full, but as an encrypted string. This means a hacker would not get direct access to user passwords should he have access to the database, but if he or her had access to the secret encryption key in Webnodes, then it would be possible to retrieve all passwords. This options offers lower security than hashed mode, and if you choose this mode, you must take extra measures to make sure your database is well protected. The advantage with this mode is that the password is retrievable, so a user can be reminded of it without the need to create a new one. The fact that the password can be retrieved may also in some circumstances make integration into other systems easier.
- UnEncrypted. In this mode the password is stored as clear text in the database. In general we do not recommend this mode, but it might be required if other systems need access to user password through direct access to the Webnodes database.
For most websites, and in particular websites that involves a high number of users or any form of e-commerce we strongly recommend the hashed mode.
Brute force attacks
Brute force attacks, are basically automated attempts at guessing a user's username and password. Webnodes comes built-in with these mechanism to protect against brute force attacks:
- Password requirememts: You can specify various requirements to passwords in the ontology module that makes it really hard to guess. By default the system will only accept new passwords if they contain more than 5 characters, upper and lower casing, digits and letters, and that the password is not found in a built-in list of the worlds 500 000 most common passwords.
- Limited login attempts per username: By default each username has a limit of 10 failed attempts at logging in before the system locks the user. Only admins can unlock users.
- Limited login attempts per client IP: By default each remote host IP is only allowed 30 failed logins within 5 minutes, before the IP is blocked.
Cross Site scripting
The entire /edit UI of Webnodes is tested and hardened to make sure it is not vulnerable to cross site scripting. And since the entire interface requires a valid login with a user that have access to the edit interface, there is a limited "surface area" available to direct attacks. However, protecting the pages a normal website visitor visits relies on the developer of the template code. Here we refer to recommended practices from Microsoft and others that applies to the ASP.Net platform. Here is a useful resource for further reading. A few basic advices:
- Never thrust and data received from a HTTP post or request. Always have code on the server (not the client) that ensure no damage can be achieved for any posted data. Be careful if you take any posted data directly into your stored data, returned HTML, email, mms etc.
- Use HttpUtility.HtmlEncode() to all string data unless you specifically want it to be interpreted as .HTML by the client.
- If you want to output data as HTML, be careful and make sure you filter it for potential unwanted tags (typically <script> tags). Webnodes comes with built in functions to simplify this. Use the HtmlNode object for filtering and parsing HTML.
- Never perform a severer.redirect directly from a request parameter (typically login.aspx?ReturnUrl=www...) without validating the URL
Webnodes comes with built-in protection against session hijacking:
- Webnode Session IDs are stored in cookies with the HttpOnly flag set to true.
- Sessions in Webnodes contains the IP of the user at login and will automaticlly self destruct if a request is made with the same session ID from a different IP.
- You can restrict the IPs a user can login from through the SytsemUser property : RestrictLoginsByIPs
- You can restring access to the Edit UI individually on each user with the "AllowEditLogin" property.
- You can force the redirect to a SSL connection for the entire Edit UI in the installation settings, found in the System module. See the security tab.
- In general we recommend you use SSL for all website areas that has a login.
- You can restrict access to the Edit UI globally in the installation settings, found in the System module. See the security tab.
- You can control who sees verbose error messages through the "developer IPs" in the installation settings, found in the System module. See the security tab.