Executive Summary
This blog post explores a significant data exposure issue within Microsoft Power Pages, a low-code SaaS platform, due to misconfigured access controls. It highlights how sensitive PII can be inadvertently exposed to unauthorized users when organizations grant excessive permissions to the Anonymous role . The key points include understanding Power Pages’ Role-Based Access Control (RBAC) model, mismanagement of authentication and column-level security, and the risks posed by over-permissioned access to databases. The post emphasizes the importance of continuous monitoring to identify misconfigurations and protect against data breaches.
In September 2024, I uncovered significant amounts of data being exposed to the public internet as a result of misconfigured access controls in Microsoft Power Page websites. Power Pages is a low-code SaaS platform, built on top of the Power Platform, which allows individuals to build externally facing websites on Microsoft’s infrastructure. The main benefits of Power Pages over traditional custom web development include out-of-the-box (OOB) role based access control (RBAC), the automatic ability to use Microsoft’s Dataverse as a database, and a drag-and-drop interface using pre-built components which greatly reduces the need for custom code.
However, the ability for Microsoft customers to easily deploy these data-driven web applications can come at a great cost to security if mismanaged from a security perspective. During my research, I’ve uncovered several million records of sensitive data being exposed to the public internet from authorized testing alone. The primary nature of this data are internal organization files and sensitive PII belonging to both internal organization users and other users registered on the website. In the majority of these cases, the PII uncovered included full names, email addresses, phone numbers, and home addresses.
In one case, a large shared business service provider for the NHS was leaking the information of over 1.1 million NHS employees, with large portions of the data including email addresses, telephone numbers, and even home addresses of the employees. This particular finding was responsibly disclosed and has since been resolved.
These data exposures are occurring due to a misunderstanding of access controls within Power Pages, and insecure custom code implementations. By granting unauthenticated users excessive permissions, anyone may have the ability to extract records from the database using readily-available Power Page APIs.
For AppOmni customers monitoring Microsoft365 products, I had already developed and made available an AppOmni Insight that assists with detecting these kinds of exposures and provides subsequent remediation guidance in the event an exposure is found.
To better understand how these exposures can occur, it is best to first have a firm grasp on basic Power Page architecture and the platform’s RBAC model.
This section does not provide a complete view of the Power Pages architecture, only the necessary components for which comprehension of this article requires.
The above diagram is a simplistic representation of a user’s interactions with a Power Pages site. Generally speaking, a regular user will interact with a page, which leverages one of three different API specifications to perform CRUD access on data stored within Dataverse, a cloud-based relational database. These API specifications behave differently, and each one must be explicitly enabled to use them. For the purposes of exploitation, I used the Web API, which is the Power Platform’s newest public API implementation. This was for a number of reasons:
In this section, I will explain the specifics of Power Pages’ RBAC functionality and their impact.
Each Power Pages site has three out-of-the-box (OOB) roles that may not be deleted or deactivated. Every individual who accesses the site has a role associated with them, regardless of if they are logged in or not. Furthermore, each role will be assigned varying levels of access to data; external roles generally being less powerful than roles representing internal users. For the scope of this article, I will be focusing on the ‘Anonymous Users’ and ‘Authenticated Users’ roles specifically. Anonymous Users represents all individuals who have not yet authenticated to the site, and as such needs no further explanation as to its importance in this article.
The reason that ‘Authenticated Users’, a role which represents anyone logged into the site, is important, is because many organizations have public registration enabled. This allows any individual to make an account on the site. Therefore, once an individual creates an account, the user will receive an ‘Authenticated Users’ role and all of the superior permissions that may come with it. In these cases, it can effectively be treated as an ‘external role’ as opposed to an internal one. This is of key significance for this piece, as organizations are far more likely to grant excessive permissions to a role that they believe is internal in nature.
As is typically the case with powerful SaaS platforms, Power Pages has a layered approach to access controls that allows for finely tuned permissions to be provisioned to particular roles and users. The four levels that these access controls exist for a ‘read’ operation are:
Within the following subsections, I will delve further into the key elements of each level, explaining further their individual intricacies and capabilities with respect to implementing access controls.
At the foundation of the diagram are site-level settings, or as in this case, site-level access controls. And each Power Pages site that an organization has deployed may have different configurations. These are managed within a site’s ‘Power Pages Management’ section and may be created, modified, and deleted. With respect to authentication and authorization, below are a list of key controls that are particularly relevant to this article:
Setting Name | Category | Description | Must Also Be Enabled | Default Value |
---|---|---|---|---|
Authentication/Registration/Enabled | Authentication | Enables or disables all forms of user registration. Registration must be enabled for the other settings in this table in order for this to take effect. | – | true |
*Authentication/Registration/OpenRegistrationEnabled | Authentication | Enables or disables the sign-up registration form. The sign-up form allows any unauthenticated visitor to create a user account. | Authentication/Registration/Enabled | true |
Authentication/Registration/ExternalLoginEnabled | Authentication | Enables or disables sign-in for external users with an account. | – | true |
*Authentication/Registration/LocalLoginEnabled | Authentication | Allows for login using username / email and password. | Authentication/Registration/LocalLoginDeprecated | true |
*Authentication/Registration/LocalLoginDeprecated | Authentication | Disables login using username / email and password, even if ‘LocalLoginEnabled’ is enabled. | – | false |
Webapi/ | Authorization | Allows a table to be accessible via the Web API. Requires a corresponding ‘Webapi/<object>/fields’ setting to exist for the table. | – | – |
Webapi/<object>/fields | Authorization | Comma-separated list of columns that belong to a Web API enabled table which are to be accessible. Each table will have its own setting. | Webapi/<object>/enabled | – |
Settings in the above table that are categorized as dealing with ‘Authentication’ are crucial for the sole reason that they will define the scope of what a security practitioner or administrator will be auditing. From what was briefly discussed in the Roles section, we know that if an unauthenticated user may self-register, then the ‘Authenticated User’ role should be treated as an external one alongside the ‘Anonymous Users’ role when it comes to an access control audit.
In addition, there are two Webapi-prefixed settings that contain placeholders. These settings are always created as a pair, and must be created for every table and its respective columns that an organization wishes to expose via the public Web API. Otherwise they will not be available to be queried or used by the site through that mechanism. For that reason, platform administrators must take note of any tables and columns that are marked as Web API accessible through these settings, as they are the ones explicitly at risk of being leaked to unintended audiences.
Once a table and its columns have been exposed to the Web API, access to the table must be granted to the desired roles. These permissions, along with record-level permissions, are granted within the ‘Table Permissions’ section of an individual site’s Power Pages management portal. Below are a subset of the key columns that an administrator can define the following key columns when assigning permissions:
Taking the above template into account, nearly all data exposures I’ve come across have been the result of the following inclusions within a table’s access control definition:
Once a user satisfies table-level access controls, any existing column access controls are then applied. Unlike table-level access controls, Microsoft’s column-level access control implementation relies on what is called ‘Masking’. Masks are customer-configurable regexes that allow customers to obfuscate certain columns that match a regex pattern, from users without the correct roles, that are deemed to be sensitive in nature. This is done by replacing content within the column which matches the regex, with a value of the customer’s choice such as ‘*’. A real example of this, provided by Microsoft is the following regex, \d(?=\d{2}-\d{2}-\d{4}\|\d-\d{2}-\d{4}\|-\d{2}-\d{4}\|\d-\d{4}\|-\d{4}), which can be used to hide social security numbers from users.
Unfortunately, the setup process is quite a number of steps for something that is typically solved within a single page on other platforms. Let’s create a scenario in which an organization wishes to block an unauthenticated user from seeing a column that stores home addresses. To do so, they would need to do the following:
Throughout the entirety of my testing, not a single implementation of column-level security was present to prevent access to sensitive columns. Whether this is due to the initially tedious setup duration, or the fact that creating your own regexes is a pre-release feature ‘not intended for production’, this security feature is widely slept upon by organizations.
Not implementing masks for sensitive columns: Throughout my authorized testing, I did not come across the use of obfuscation for sensitive columns. If an organization does not wish to leverage column security profiles, it may be wise to apply masks to PII related columns exclusively for external users, without hindering site functionality.
A prerequisite for testing is a proxy tool such as Burp Suite, which can sniff HTTP(s) traffic and modify / replay requests.
In this section, I’ll explain how these access control misconfigurations can be exploited, at a technical level. All queries seen in this section were tested against a personal public site of Power Pages, which has been intentionally misconfigured for the purpose of this demonstration. The configuration of this vulnerable instance is as follows:
To make it a more ‘realistic’ exercise, I’ll also include requests to tables and columns which are deliberately not exposed. This gives the benefit of differentiating between the various API errors, and how to pivot around them. Additionally, in a legitimate red team exercise, it is recommended to export the entire Power Platform schema, both tables and their columns, into a wordlist-style format to be used in a brute-force fashion.
2. Once a valid request has been identified, it can be sent to Burp’s Repeater tab to be modified. Once done, the only modification that needs to be made is changing the URL path to ‘/_api/<object_name>’. For the first example, we will attempt to access a column which has not yet been exposed, ‘sharepointdocuments’.
In the above, there is a distinctive error message that is uniquely associated with an object which has not been exposed to the Web API. Notably, if a valid table name is provided in the query, it is the only case in which a 404 HTTP status code is returned.
3. Now, we’ll replace ‘sharepointdocuments’ with an object that we know is exposed, ‘accounts’, and re-send the request.
A successful response like the above will return a list of maximum 5000 records at a time, with all columns returned. From an external perspective, having a maximum record count of 5000 is less than ideal when trying to assess the total impact of the exposure. Luckily, there are additional GET API parameters that can be played with to attain a more desirable result, detailed below:
4. Knowing what a successful response looks like, let’s replace ‘account’ with the ‘contacts’ object, then send the request.
In the above, querying all columns returned an error since not all columns for this exposed object have been made available to the Web API. It’s important to take note of the error code in the response also, as “90040101” is only generated when an unsupported column, or in this case an attribute, is not supported or enabled.
5. To resolve this issue, we must manually determine which columns have been exposed publicly and this can be done using the $select parameter. This is an exercise in trial-and-error, so it is recommended that the schema is exported for easy automated testing.
Based on the error message, it can be determined that at least one column is not exposed, in this case it’s the ‘fullname’ column. If this column was removed from the $select parameter, it could generate another error of the same format in the event that the ‘telephone1’ parameter was also not exposed.
6. Removing the ‘fullname’ column, we will resend the request:
By removing the inaccessible field, I extracted the exposed data by ‘guessing’ the other columns. One last behavior worth mentioning not covered above is if an object is queried that is Web API accessible but not to the ‘Anonymous User’ role, a HTTP 403 response with the message, “You don’t have permission to read the X table.” will be returned. This is a good case for self-registering if possible and re-sending the request authenticated, as the ‘Authenticated Users’ role may have the required access.
Microsoft has helpfully included a number of warnings in the backend of the Power Pages and Power Platform applications when a potentially dangerous configuration is detected, such as public data. These include:
If any of the above messages or icons are displaying on configuration pages related to permissions, it is advisable that organizations review their access controls to ensure that any public data that may be exposed is not sensitive in nature.
The most effective way to resolve this issue in its entirety is to remove excessive levels of access to external users. Personally, I recommend assessing access levels from the ground up. This starts with the analysis of site settings, then table / record permissions, and finishing with column permissions. The following are configuration items that are essential to cover:
In certain cases, organizations that rely heavily on the Web API for custom functionality may not always be able to resolve these problems through the above, without breaking site functionality. For example, one common implementation that I’ve noticed is an organization leveraging the Web API on custom authentication forms to validate the existence of a user submitted employee number, or similar, exists. The common misconfiguration here was that there were often excessive privileges granted at both the record level and column level, resulting in complete exposure of the data set. In that case, leveraging a custom API endpoint for validation of user-supplied information would be recommended.
Microsoft’s Power Pages platform is an excellent low-code solution for the development of Dataverse-integrated applications such as web portals. However, the customizable nature of the product can result in organizations with poor security hygiene accidentally exposing their most sensitive data to the public internet. Microsoft has done an excellent job thoroughly documenting the dangers of this API both in-product and throughout its documentation. Additionally, Microsoft’s security scan feature is an excellent tool to quickly diagnose potential access control misconfigurations. Yet, it is a point-in-time tool.
In today’s rapidly evolving threat landscape, maintaining SaaS security requires a proactive approach. Implementing continuous monitoring is not just a best practice—it’s essential for identifying vulnerabilities, detecting suspicious activity, and ensuring compliance with security standards. Leveraging a continuous monitoring tool enables businesses to maintain a secure state by providing real-time insights and automated alerts, allowing security teams to respond to threats swiftly and effectively. By staying vigilant and utilizing advanced monitoring solutions, organizations can confidently safeguard their critical SaaS environments against emerging risks and ensure long-term data protection.
For more on Microsoft, read our solution overview and register to attend our SaaS Security 101 Workshop | Microsoft 365.
Secure your Microsoft 365 instance and ensure that critical business data stays protected while you continue to scale.
The post Microsoft Power Pages: Data Exposure Reviewed appeared first on AppOmni.
*** This is a Security Bloggers Network syndicated blog from AppOmni authored by Aaron Costello, Chief of SaaS Security Research, AppOmni. Read the original post at: https://appomni.com/ao-labs/microsoft-power-pages-data-exposure-reviewed/