DAST browser-based analyzer: Support multi-step login forms ("Keep me signed in" workflow for Azure)
Hello, The current implementation is not able to handle the default Azure SSO workflow as described [here](https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/active-directory-users-profile-azure-portal#learn-about-the-stay-signed-in-prompt). The KMSI workflow requires a **third** interaction as can be seen in Microsoft's workflow description. ![image](/uploads/629aa9b1dd20bb07fc568c9aa6b86e17/image.png) Unfortunately there is no way to bypass this by setting a cookie or similar and there also does not seem to be an easy way to extend/script the DAST analyzer to perform the necessary step. #### current non-working setup ```yaml include: template: DAST.gitlab-ci.yml variables: DAST_WEBSITE: "https://some-sso-website.com" DAST_AUTH_URL: "https://some-sso-website.com" DAST_USERNAME_FIELD: "css:[id=i0116]" # The ID of the AAD "Username" field DAST_FIRST_SUBMIT_FIELD: "css:[id=idSIButton9]" # The ID of the "Next" Button DAST_PASSWORD_FIELD: "css:[id=i0118]" # The ID of the AAD "Password" field DAST_SUBMIT_FIELD: "css:[id=idSIButton9]" # The ID of the "Sign In" Button DAST_BROWSER_SCAN: "true" DAST_USERNAME: "some-user" DAST_PASSWORD: "some-user-password" DAST_BROWSER_LOG: "auth:debug" ``` ### Proposal It would be great to extend the implementation to allow for the **three-step-login** as defined by Microsoft. An easy solution might be to add `DAST_KMSI_FIELD`. That field could then be assigned to either - the `Yes`-Button (`css:[id=i0116]`) - the `No`-Button (`css:[id=idBtn_Back]`) The proposed `.gitlab-ci.yml` file would then look like this ```yaml include: template: DAST.gitlab-ci.yml variables: DAST_WEBSITE: "https://some-sso-website.com" DAST_AUTH_URL: "https://some-sso-website.com" DAST_USERNAME_FIELD: "css:[id=i0116]" # The ID of the AAD "Username" field DAST_FIRST_SUBMIT_FIELD: "css:[id=idSIButton9]" # The ID of the "Next" Button DAST_PASSWORD_FIELD: "css:[id=i0118]" # The ID of the AAD "Password" field DAST_SUBMIT_FIELD: "css:[id=idSIButton9]" # The ID of the "Sign In" Button DAST_KMSI_FIELD: "css:[id=idSIButton9]" # The ID of the "Yes" Button DAST_BROWSER_SCAN: "true" DAST_USERNAME: "some-user" DAST_PASSWORD: "some-user-password" DAST_BROWSER_LOG: "auth:debug" ``` #### Implementation Plan - `DAST_AFTER_LOGIN_ACTIONS` will be a list of comma separated actions to be executed after login. Example: "click\[on=css:.remember-me\],click\[on=id:stay-signed-in\]" . - Add new field to configuration - Add AfterLoginActions \[\]string to tomlAuthConfig (toml_auth_config.go). - Add AfterLoginActions browserk.LoginAction to AuthDetails (auth.go). Parse (see new point) and assign this property in toml_auth_config.go. - Parse `DAST_AFTER_LOGIN_ACTIONS` - In `scanner/auth`, create a new package `actions`. Create a new struct, `LoginAction`. - In `browserk/auth.go`, create a new interface called `LoginAction`. - In the `actions` package, create a `ClickAction`. Ensure that the `click[on=xxxx]` can be parsed into a click login action. - In the `actions` package, create a `LoginActions` (similar to `selector.QuerySelectors`) - In the `actions` package, create a `parser.go` with a Parse function. Parse should convert a series of login action strings into `actions.LoginActions`, and the Parse function should return `browser.LoginAction` (similar to `scanner/browser/selector/parser.go#Parse`) - Parse should return an error with an appropriate error message if unable to be parsed. - If there is no after login steps, Parse should return an empty `actions.LoginActions`. This way, the service that uses the steps won't need to worry about it being nil. - In `auth.Service.processLoginSteps`, after attempting login and before navigating to about:blank, ask the login actions to execute. - Cookies recorded during this process should be captured as part of the `AuthContext` - Add `Execute(loginCtx *browserk.Context, crawler browserk.Crawler, browser browserk.Browser) ([]*browserk.Navigation, error)` to each browserk.LoginAction. - The click action will need to create a new click navigation (see how `PathToLoginForm` works in `authenticator.LoginFormFinder`). The crawler will process the navigation. - Update an end-to-end test to use this new field. Avoid writing a new test if possible to minimize the the number of end-to-end tests required to run in CI. - In DAST - Add configuration DAST_AFTER_LOGIN_ACTIONS and pass it on to Browserker. - Upgrade Browserker #### Out of scope - The after login action items will not create separate entries on the auth report (please make sure PM is aware of this, we can create a new issue if required)
epic