The Web Framework provides access control at view level. By default, users need to perform a login before they can access anything within the application. This is configured using the peLoginMode (cWebApp) and pbLoginModeEnforced (cWebWindow) properties. Further customization can be done by implementing the AllowAccess (cWebWindow) and AllowViewAccess (cWebSessionManager) event hooks. When doing this the framework will make sure that all access to the views is blocked at the lowest level.
It is also common practice to hide or disable UI elements within a view or a menu based on user rights. In prior revisions, doing this only using pbRender / pbVisible or pbEnabled was not secure. These properties were Client web properties which means that they are implemented in JavaScript and their value is also stored within the browser. Unfortunately, that allows the value to be changed using the developer tools of the browser. This can be exploited by users to access data they shouldn’t have had access too or to execute functionality that shouldn’t have been available to them.
While developers can program around this vulnerability, the framework lacked the hooks to do so easily. In DataFlex 2021, we enhanced security by adding validations based on pbRender, pbVisible and pbEnabled. This makes applications more secure without requiring code changes.
The vulnerability can manifest in two main ways; though manipulating “hidden” data entry objects and triggering events on the server.
When hiding a data entry object using pbRender the control is hidden from JavaScript using CSS. If the development tools are used to unhide this control it shows the current field value. In addition, changes made to this value will automatically be saved if the DD allows this (creating DD level protections is one of the techniques currently used by developers).
Even if a control is hidden or disabled, the server will accept server actions for it. For instance, if a button is hidden or disabled it is still possible to trigger OnClick using the developer tools.
The Web Framework controls in DataFlex 2021 are more secure by default, though improvements to the pbRender, pbVisible and pbEnabled properties. This means without code changes, Web Framework applications are more secure.
This web property type is sent to the client, but its value is also stored on the server (like a Server web property). Performing a WebGet on these properties will always return the server value. Performing a WebSet will update both the server and the client-side value. The class library uses the ClientProtected properties to perform extra security checks.
The properties pbRender, pbVisible and pbEnabled for all controls are ClientProtected properties. This gives us a reliable value on the server to create additional checks in the framework classes. Because disabling or hiding a container will disable or hide all its children, just checking these properties for the current object is not enough. A new function, IsControlAccessible, checks if a control is visible and / or enabled. This function also looks at the state of parent objects, and will check if the view in which the control is located is synchronized (when necessary). A caching system is built in to make this call as efficient as possible (in some cases it will be called a lot).
Before a published function or procedure is called (from the client), AllowServerAction is called. This provides the basic hook for controlling server actions based on user rights. If the bAllow parameter is set to false, the call is not processed, and an error is returned to the client. The cWebBaseUIObject class (the base class for all UI objects) overrides this procedure and checks if a control is accessible using IsControlAccessible. This means that only controls that are enabled and visible on the client will now process events.
This procedure can be overridden to customize the behavior. Note that not doing a forward send will make it skip the new security check. Some controls might override this procedure to enable specific server actions when a control is disabled.
This procedure is called whenever a data aware control loads a value from the Data Dictionary. For a cWebForm, this procedure is called when a record is found and the Refresh message is received from the Data Dictionary. For a cWebColumn, this procedure is called for every list row that is loaded. If bAllow is set to false, the control does not load the value. The default is that cWebBaseDEO calls IsControlAccessible to verify if the control is visible (not hidden).
Note that this does not check pbEnabled since disabled controls can still display values. The property pbNoFillIfHidden can be set to false to make a hidden DEO still receive a value. This is used when the control is hidden for a different reason that the user not being allowed to see the value.
Also note that when unhiding a data entry object it will not contain the right value. Send Request_Assign to the data dictionary to trigger an explicit update.
This procedure is called whenever a data aware control updates the data dictionary with a changed value. This happens as part of the DDO synchronization that is done for each request. It will validate that the update is allowed and if not (bAllow is false) the Set Field_Changed_Value is not performed. By default, IsControlAccessible is called to verify if the control is visible and enabled. The property pbNoUpdateIfHidden can be set to allow hidden controls to update the DD.
To make the security checks function properly it is important that WebSet is used when setting these properties based on the user rights. IN addition, any time Client web properties are altered in OnLoad using Set (to prevent them from becoming synchronized), these ClientProtected web properties must be WebSet. For example, this is the proper way to hide a control based on user rights:
Object oCustomerCredit_Limit is a cWebForm
Entry_Item Customer.Credit_Limit
Set piColumnSpan to 6
Set piColumnIndex to 0
Set psLabel to "Credit Limit:"
Set peLabelAlign to alignRight
Procedure OnLoad
Integer iRights
Get piUserRights of ghoWebSessionManager to iRights
If (iRights < 1) Begin
WebSet pbRender to False
End
End_Procedure
End_Object
Note that for controls outside a view (like menu items) you must update this state after login. The OnRightsChange event is available on menu items and the cWebApp class to do this:
Object oOrderListMenuItem is a cWebMenuItem
Set psCaption to "Order List"
WebRegisterPath ntNavigateBegin oOrderListSample
Procedure OnClick
Send NavigatePath
End_Procedure
Procedure OnChangeRights
Integer iRights
Get piUserRights of ghoWebSessionManager to iRights
WebSet pbRender to (iRights = 1)
End_Procedure
End_Object
Previous Topic: Session Management and Login
Next Topic: The JavaScript Engine