Silverfort
Use the Silverfort integration to get and update Silverfort risk severity.
Authentication & Identity Management · Silverfort
Details
| ID | Silverfort |
|---|---|
| Provider | Silverfort |
| Category | Authentication & Identity Management |
| From Version | 5.0.0 |
| Docker Image | demisto/auth-utils:1.0.0.10133006 |
| Supported Modules | Agentix XSIAM |
README
Silverfort protects organizations from data breaches by delivering strong authentication across entire corporate networks and cloud environments, without requiring any modifications to endpoints or servers. Using patent-pending technology, Silverfort’s agentless approach enables multi-factor authentication and AI-driven adaptive authentication even for systems that don’t support it today, including proprietary systems, critical infrastructure, shared folders, IoT devices, and more.
Use Silverfort integration to get & update Silverfort risk severity.
This integration was integrated and tested with Silverfort version 5.2.
Silverfort Playbook
- Get risk information and block the user if the risk is ‘high’ or ‘critical’
- Update the Silverfort user risk level
Use Cases
- Consume Silverfort user and server risk levels
- Enrich the Silverfort risk engine and trigger MFA on risky entities
Configure Silverfort in Cortex
| Parameter | Description | Required |
|---|---|---|
| Name | a textual name for the integration instance | True |
| url | Server URL | True |
| apikey | APIKEY | True |
| insecure | Trust any certificate (not secure) | False |
- To generate an API token for external access:
- On the Silverfort Admin Console, navigate to the SETTINGS page, and then select Silverfort API.
- Enable the Allow 3rd party risk updates switch.
- Copy the Risk API Key (External Access) value - this will be your API key.
- For the URL, use one of the following based on your Silverfort region:
- Global region: https://raven.silverfort.io
- EU region: https://eu.raven.silverfort.io
- Singapore region: https://sg.raven.silverfort.io
// End of Selection
For more information, see the Silverfort documentation.
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook. After you successfully execute a command, a DBot message appears in the War Room with the command details. DBot messages provide a structured summary of the command execution, including the inputs, outputs, and any relevant indicators of compromise (IOCs) or risk levels.
silverfort-get-user-risk
User risk commands - get the user entity risk.
Base Command
silverfort-get-user-risk
Input
| Argument Name | Description | Required |
|---|---|---|
| upn | The user principal name. | Optional |
| The email address. | Optional | |
| sam_account | The sam account. | Optional |
| domain | The domain. | Optional |
Specify one of the following:
- upn
- email address and domain
- sam account and domain
Context Output
| Path | Type | Description |
|---|---|---|
| Silverfort.UserRisk.Risk | String | The risk level. |
| Silverfort.UserRisk.Reasons | Unknown | The reasons for the risk. |
| Silverfort.UserRisk.UPN | String | The user principal name. |
Command Example
!silverfort-get-user-risk upn="sfuser@silverfort.io"
Human Readable Output
Silverfort User Risk
| UPN | Risk | Reasons |
|---|---|---|
| sfuser@silverfort.io | Medium | Suspicious activity, Password never expires |
silverfort-get-resource-risk
Gets the resource entity risk information.
Base Command
silverfort-get-resource-risk
Input
| Argument Name | Description | Required |
|---|---|---|
| resource_name | The hostname. | Required |
| domain_name | The domain. | Required |
Command Example
!silverfort-get-resource-risk resource_name="SF-DC-1" domain_name="silverfort.io"
Human Readable Output
Silverfort Resource Risk
| ResourceName | Risk | Reasons |
|---|---|---|
| SF-DC-1 | Low | Unconstrained Delegation |
silverfort-update-user-risk
Updates the user entity risk.
Base Command
silverfort-update-user-risk
Input
| Argument Name | Description | Required |
|---|---|---|
| upn | The user principal name. | Optional |
| risk_name | The risk name. | Required |
| severity | The severity. | Required |
| valid_for | The number of hours that the risk will be valid for. | Required |
| description | The risk description. | Required |
Command Example
!silverfort-update-user-risk upn="sfuser@silverfort.io" risk_name="activity_risk" severity=medium valid_for=1 description="Suspicious activity"
Human Readable Output
ok
silverfort-update-resource-risk
Update the resource entity risk.
Base Command
silverfort-update-resource-risk
Input
| Argument Name | Description | Required |
|---|---|---|
| resource_name | The hostname. | Required |
| domain_name | The domain name. | Required |
| risk_name | The risk name. | Required |
| severity | The severity. | Required |
| valid_for | The number of hours the severity will be relevant for. | Required |
| description | A short description about the risk. | Required |
Command Example
!silverfort-update-resource-risk resource_name="SF-DC-1" domain_name="silverfort.io" risk_name="malware_risk" severity="high" valid_for=1 description="Malware detected"
Human Readable Output
ok
Configuration parameters
url— Server URL (required)apikey— APIKEY (required)insecure— Trust any certificate (not secure)operationalApiKey— Operational API KeyexternalApiKey— External API Key
Commands (4)
-
silverfort-get-resource-riskGets the resource entity risk information.
-
silverfort-get-user-riskUser risk commands - get the user entity risk.
-
silverfort-update-resource-riskUpdate the resource entity risk.
-
silverfort-update-user-riskUpdates the user entity risk.
import time import jwt import urllib3 from CommonServerPython import * # Disable insecure warnings urllib3.disable_warnings() # CONSTANTS UPDATE_REQ_RESPONSE = {"result": "updated successfully!"} def get_jwt_token(app_user_id: str, app_user_secret: str, current_time=None, expire_time_sec: int = 60): if current_time is None: current_time = int(time.time()) payload = { "iss": app_user_id, "iat": current_time, "exp": current_time + expire_time_sec, } token = jwt.encode(payload, app_user_secret, algorithm="HS256") if isinstance(token, bytes): token = token.decode("utf-8") return token class Client(BaseClient): def __init__( self, app_user_id, app_user_secret, operational_user_id, operational_user_secret, external_api_key, *args, **kwargs ): self.app_user_id = app_user_id self.app_user_secret = app_user_secret self.operational_user_id = operational_user_id self.operational_user_secret = operational_user_secret self.external_api_key = external_api_key super().__init__(*args, **kwargs) def build_headers(self): headers = {"Authorization": f"Bearer {get_jwt_token(self.app_user_id, self.app_user_secret)}"} if self.external_api_key: headers["X-Console-API-Key"] = self.external_api_key return headers def build_operational_headers(self): """Build headers using operational API key for getUPN endpoint""" if self.operational_user_id and self.operational_user_secret: headers = {"Authorization": f"Bearer {get_jwt_token(self.operational_user_id, self.operational_user_secret)}"} else: # Fall back to main credentials if operational key not provided headers = {"Authorization": f"Bearer {get_jwt_token(self.app_user_id, self.app_user_secret)}"} if self.external_api_key: headers["X-Console-API-Key"] = self.external_api_key return headers def get_status_http_request(self): """ initiates an http request to get the service status """ response = self._http_request( method="GET", url_suffix="getBootStatus", headers=self.build_headers(), ) return response def get_upn_by_email_or_sam_account_http_request(self, domain=None, email=None, sam_account=None): """ initiates an http request to get the upn by email or sam account """ params = {"domain": domain} if email: params["email"] = email else: params["sam_account"] = sam_account response = self._http_request( method="GET", url_suffix="getUPN", params=params, headers=self.build_operational_headers(), ) return response["user_principal_name"] def get_user_entity_risk_http_request(self, upn): """ initiates an http request to get the user entity's risk from Silverfort DB """ response = self._http_request( method="GET", url_suffix="getEntityRisk", params={"user_principal_name": upn}, headers=self.build_headers(), ) return response def get_resource_entity_risk_http_request(self, resource_name, domain_name): """ initiates an http request to get the resource entity's risk from Silverfort DB """ response = self._http_request( method="GET", url_suffix="getEntityRisk", params={"resource_name": resource_name, "domain_name": domain_name}, headers=self.build_headers(), ) return response def update_user_entity_risk_http_request(self, upn, risks): """ initiates an http request to update the user entity's risk in Silverfort DB """ response = self._http_request( method="POST", url_suffix="updateEntityRisk", headers=self.build_headers(), json_data={"user_principal_name": upn, "risks": risks}, ) return response def update_resource_entity_risk_http_request(self, resource_name, domain_name, risks): """ initiates an http request to update resource entity's risk in Silverfort DB """ response = self._http_request( method="POST", url_suffix="updateEntityRisk", json_data={"resource_name": resource_name, "domain_name": domain_name, "risks": risks}, headers=self.build_headers(), ) return response def create_risk_json(args): try: valid_for = args.get("valid_for") valid_for = int(valid_for) except Exception: raise Exception("valid_for must be a positive number greater than 1") risk_name = args.get("risk_name") severity = args.get("severity") description = args.get("description") return {risk_name: {"severity": severity, "valid_for": valid_for, "description": description}} def get_upn(client, args): email = args.get("email") sam_account = args.get("sam_account") domain = args.get("domain") return client.get_upn_by_email_or_sam_account_http_request(domain, email, sam_account) def test_module(client): """ Use getEntityRisk to validate credentials. Return 'ok' on 200, 400 or 404 (endpoint reached + auth ok). Fail on 401/403 and any unexpected error. """ test_upn = "sfuser" # any UPN is fine; 400/404 is acceptable for a creds-only test try: client._http_request( method="GET", url_suffix="getEntityRisk", params={"user_principal_name": test_upn}, headers=client.build_headers(), ok_codes=(200, 400, 404), ) return "ok" except Exception as e: # Try to extract an HTTP status (when available) status = None try: # DemistoException often carries the original Response in e.res status = getattr(getattr(e, "res", None), "status_code", None) except Exception: pass if status in (401, 403): return_error( f"Authentication failed (HTTP {status}). " "Check the App User ID:Secret, server URL, JWT clock skew, and permissions." ) elif status: return_error(f"Unexpected response from getEntityRisk (HTTP {status}): {str(e)}") else: # No HTTP status available; surface the error return_error(f"Failed to call getEntityRisk: {str(e)}") def get_user_entity_risk_command(client, args): upn = args.get("upn") if not upn: upn = get_upn(client, args) result = client.get_user_entity_risk_http_request(upn) outputs = {"UPN": upn, "Risk": result.get("risk"), "Reasons": result.get("reasons")} name = "Silverfort User Risk" headers = ["UPN", "Risk", "Reasons"] readable_output = tableToMarkdown(name, outputs, headers) return ( readable_output, {"Silverfort.UserRisk(val.UPN && val.UPN == obj.UPN)": outputs}, result, # raw response - the original response ) def get_resource_entity_risk_command(client, args): resource_name = args.get("resource_name") domain_name = args.get("domain_name") result = client.get_resource_entity_risk_http_request(resource_name, domain_name) outputs = {"ResourceName": resource_name, "Risk": result.get("risk"), "Reasons": result.get("reasons")} name = "Silverfort Resource Risk" headers = ["ResourceName", "Risk", "Reasons"] readable_output = tableToMarkdown(name, outputs, headers) return ( readable_output, {"Silverfort.ResourceRisk(val.ResourceName && val.ResourceName == obj.ResourceName)": outputs}, result, # raw response - the original response ) def update_user_entity_risk_command(client, args): upn = args.get("upn") if not upn: upn = get_upn(client, args) risks = create_risk_json(args) result = client.update_user_entity_risk_http_request(upn, risks) if result == UPDATE_REQ_RESPONSE: return "updated successfully!" else: return "Couldn't update the user entity's risk" def update_resource_entity_risk_command(client, args): resource_name = args.get("resource_name") domain_name = args.get("domain_name") risks = create_risk_json(args) result = client.update_resource_entity_risk_http_request(resource_name, domain_name, risks) if result == UPDATE_REQ_RESPONSE: return "updated successfully!" else: return "Couldn't update the resource entity's risk" def main(): # pragma: no cover """ PARSE AND VALIDATE INTEGRATION PARAMS """ # get the service API url base_url = urljoin(demisto.params().get("url"), "/v1/public") verify_certificate = not demisto.params().get("insecure", False) api_key = demisto.params().get("apikey") app_user_id, app_user_secret = api_key.split(":") operational_api_key = demisto.params().get("operationalApiKey") operational_user_id = None operational_user_secret = None if operational_api_key: operational_user_id, operational_user_secret = operational_api_key.split(":") external_api_key = demisto.params().get("externalApiKey") if not app_user_id or not app_user_secret: return_error("Verify the API KEY parameter is correct") demisto.debug(f"Command being called is {demisto.command()}") try: client = Client( app_user_id=app_user_id, app_user_secret=app_user_secret, operational_user_id=operational_user_id, operational_user_secret=operational_user_secret, base_url=base_url, verify=verify_certificate, external_api_key=external_api_key, ) if demisto.command() == "test-module": # This is the call made when pressing the integration Test button. result = test_module(client) demisto.results(result) elif demisto.command() == "silverfort-get-user-risk": return_outputs(*get_user_entity_risk_command(client, demisto.args())) elif demisto.command() == "silverfort-get-resource-risk": return_outputs(*get_resource_entity_risk_command(client, demisto.args())) elif demisto.command() == "silverfort-update-user-risk": result = update_user_entity_risk_command(client, demisto.args()) demisto.results(result) elif demisto.command() == "silverfort-update-resource-risk": result = update_resource_entity_risk_command(client, demisto.args()) demisto.results(result) # Log exceptions except Exception as e: error_message = f"Failed to execute {demisto.command()} command. Error: " if "Failed to parse" in e.args[0]: return_error(message=error_message + "Verify the URL parameter is correct") elif "riskapi" not in e.args[0]: return_error(message=error_message + str(e.args[0])) else: return_error(error_message + "Something went wrong") if __name__ in ("__main__", "__builtin__", "builtins"): main()