{"id":3474,"date":"2026-08-30T10:31:58","date_gmt":"2026-08-30T15:31:58","guid":{"rendered":"https:\/\/xlinesoft.com\/blog\/?p=3474"},"modified":"2026-08-30T10:43:04","modified_gmt":"2026-08-30T15:43:04","slug":"block-compromised-passwords-and-high-risk-ip-addresses","status":"publish","type":"post","link":"https:\/\/xlinesoft.com\/blog\/2026\/08\/30\/block-compromised-passwords-and-high-risk-ip-addresses\/","title":{"rendered":"Block Compromised Passwords and High-Risk IP Addresses"},"content":{"rendered":"<p>PHPRunner and ASPRunner.NET already provide password policies, encrypted password storage, CAPTCHA, two-factor authentication, and user permissions. In this tutorial, we will add two more checks: reject passwords found in known data breaches and block visitors whose IP addresses have a very high abuse score.<\/p>\n<p>During registration, <a href=\"https:\/\/haveibeenpwned.com\/Passwords\">haveibeenpwned.com<\/a> checks whether the password has appeared in known data breaches. The password itself is never sent to the service, and this API does not require a key.<\/p>\n<p><a href=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/password_compromised.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/password_compromised.png\" alt=\"\" width=\"997\" height=\"576\" class=\"alignnone size-full wp-image-3478\" srcset=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/password_compromised.png 997w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/password_compromised-600x347.png 600w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/08\/password_compromised-768x444.png 768w\" sizes=\"auto, (max-width: 997px) 100vw, 997px\" \/><\/a><\/p>\n<p>Before processing an application page, <a href=\"https:\/\/www.abuseipdb.com\/\">abuseipdb.com<\/a> checks whether the visitor&#8217;s IP address has been associated with abusive activity. This service requires an API key. Section 6 explains how to create that key. The results are cached locally to reduce the number of external requests.<\/p>\n<p><!--more--><\/p>\n<h3>1. Creating the IP reputation cache table<\/h3>\n<p>The example project uses MySQL. Create the following table in the application&#8217;s database, then add it to the project on the Datasource tables screen.<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"sql\" name=\"mshighlighter\" >\r\nCREATE TABLE ip_reputation_cache (\r\n    ip_address VARCHAR(45) NOT NULL,\r\n    abuse_score INT NOT NULL DEFAULT 0,\r\n    total_reports INT NOT NULL DEFAULT 0,\r\n    country_code VARCHAR(2) NULL,\r\n    usage_type VARCHAR(100) NULL,\r\n    checked_at DATETIME NOT NULL,\r\n    PRIMARY KEY (ip_address)\r\n);<\/textarea><\/pre>\n<\/div>\n<h3>2. Adding the reusable security-check functions<\/h3>\n<p>The first function creates a SHA-1 hash of the password and sends only its first five characters to Pwned Passwords. The remaining hash characters never leave the application.<\/p>\n<p>The second function checks the local cache before contacting AbuseIPDB.<\/p>\n<p><strong>PHPRunner<\/strong><\/p>\n<p>Open <strong>Style Editor \u2192 Custom Files<\/strong>, create <strong>security_checks.php<\/strong>, and add this code:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n<?php\r\n\r\nfunction pwnedPasswordCount($password)\r\n{\r\n    $hash = strtoupper(sha1($password));\r\n    $prefix = substr($hash, 0, 5);\r\n    $suffix = substr($hash, 5);\r\n\r\n    $ch = curl_init(\r\n        \"https:\/\/api.pwnedpasswords.com\/range\/\" . $prefix\r\n    );\r\n\r\n    curl_setopt_array($ch, array(\r\n        CURLOPT_RETURNTRANSFER => true,\r\n        CURLOPT_TIMEOUT => 5,\r\n        CURLOPT_HTTPHEADER => array(\r\n            \"User-Agent: PHPRunner-PwnedPasswordCheck\",\r\n            \"Add-Padding: true\"\r\n        )\r\n    ));\r\n\r\n    $response = curl_exec($ch);\r\n    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\r\n    curl_close($ch);\r\n\r\n    if ($response === false || $httpCode != 200) {\r\n        return -1;\r\n    }\r\n\r\n    foreach (preg_split('\/\\r\\n|\\r|\\n\/', $response) as $line) {\r\n        $parts = explode(':', trim($line), 2);\r\n\r\n        if (\r\n            count($parts) == 2\r\n            && strtoupper($parts[0]) === $suffix\r\n        ) {\r\n            return (int)$parts[1];\r\n        }\r\n    }\r\n\r\n    return 0;\r\n}\r\n\r\nfunction ipCheckFailure($message)\r\n{\r\n    return array(\r\n        \"success\" => false,\r\n        \"error\" => $message\r\n    );\r\n}\r\n\r\nfunction checkIPReputation($ipAddress, $apiKey)\r\n{\r\n    if (!filter_var($ipAddress, FILTER_VALIDATE_IP)) {\r\n        return ipCheckFailure(\"Invalid IP address.\");\r\n    }\r\n\r\n    $sql = DB::PrepareSQL(\r\n        \"SELECT *\r\n         FROM ip_reputation_cache\r\n         WHERE ip_address=':1'\r\n         AND checked_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)\",\r\n        $ipAddress\r\n    );\r\n\r\n    $rs = DB::Query($sql);\r\n    $cached = $rs ? $rs->fetchAssoc() : false;\r\n\r\n    if ($cached) {\r\n        return array(\r\n            \"success\" => true,\r\n            \"cached\" => true,\r\n            \"abuseScore\" => (int)$cached[\"abuse_score\"],\r\n            \"totalReports\" => (int)$cached[\"total_reports\"],\r\n            \"countryCode\" => $cached[\"country_code\"],\r\n            \"usageType\" => $cached[\"usage_type\"]\r\n        );\r\n    }\r\n\r\n    $url = \"https:\/\/api.abuseipdb.com\/api\/v2\/check?\"\r\n        . http_build_query(array(\r\n            \"ipAddress\" => $ipAddress,\r\n            \"maxAgeInDays\" => 90\r\n        ));\r\n\r\n    $ch = curl_init($url);\r\n\r\n    curl_setopt_array($ch, array(\r\n        CURLOPT_RETURNTRANSFER => true,\r\n        CURLOPT_TIMEOUT => 5,\r\n        CURLOPT_HTTPHEADER => array(\r\n            \"Accept: application\/json\",\r\n            \"Key: \" . $apiKey\r\n        )\r\n    ));\r\n\r\n    $response = curl_exec($ch);\r\n    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);\r\n    curl_close($ch);\r\n\r\n    if ($response === false || $httpCode != 200) {\r\n        return ipCheckFailure(\"Unable to check IP reputation.\");\r\n    }\r\n\r\n    $json = json_decode($response, true);\r\n\r\n    if (!isset($json[\"data\"])) {\r\n        return ipCheckFailure(\"Unexpected AbuseIPDB response.\");\r\n    }\r\n\r\n    $data = $json[\"data\"];\r\n    $abuseScore = (int)($data[\"abuseConfidenceScore\"] ?? 0);\r\n    $totalReports = (int)($data[\"totalReports\"] ?? 0);\r\n    $countryCode = $data[\"countryCode\"] ?? \"\";\r\n    $usageType = $data[\"usageType\"] ?? \"\";\r\n\r\n    DB::Exec(DB::PrepareSQL(\r\n        \"DELETE FROM ip_reputation_cache\r\n         WHERE ip_address=':1'\",\r\n        $ipAddress\r\n    ));\r\n\r\n    DB::Exec(DB::PrepareSQL(\r\n        \"INSERT INTO ip_reputation_cache\r\n         (ip_address, abuse_score, total_reports,\r\n          country_code, usage_type, checked_at)\r\n         VALUES (':1', :2, :3, ':4', ':5', NOW())\",\r\n        $ipAddress,\r\n        $abuseScore,\r\n        $totalReports,\r\n        $countryCode,\r\n        $usageType\r\n    ));\r\n\r\n    return array(\r\n        \"success\" => true,\r\n        \"cached\" => false,\r\n        \"abuseScore\" => $abuseScore,\r\n        \"totalReports\" => $totalReports,\r\n        \"countryCode\" => $countryCode,\r\n        \"usageType\" => $usageType\r\n    );\r\n}<\/textarea><\/pre>\n<\/div>\n<p>Open <strong>Events \u2192 Global events \u2192 After App Init<\/strong> and load the file:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\nrequire_once(\"security_checks.php\");<\/textarea><\/pre>\n<\/div>\n<p><strong>ASPRunner.NET<\/strong><\/p>\n<p>Open <strong>Style Editor \u2192 Custom Files<\/strong>, create <strong>security_checks.cs<\/strong>, and add this code. ASPRunner.NET compiles C# custom files with the generated application, so no separate include statement is required.<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\nusing System;\r\nusing System.IO;\r\nusing System.Net;\r\nusing System.Security.Cryptography;\r\nusing System.Text;\r\nusing System.Web;\r\nusing runnerDotNet;\r\n\r\nnamespace runnerDotNet\r\n{\r\n    public partial class CommonFunctions\r\n    {\r\n        public static int PwnedPasswordCount(string password)\r\n        {\r\n            byte[] bytes = Encoding.UTF8.GetBytes(password);\r\n            byte[] digest;\r\n\r\n            using (SHA1 sha1 = SHA1.Create())\r\n            {\r\n                digest = sha1.ComputeHash(bytes);\r\n            }\r\n\r\n            string hash = BitConverter.ToString(digest)\r\n                .Replace(\"-\", \"\")\r\n                .ToUpperInvariant();\r\n\r\n            string prefix = hash.Substring(0, 5);\r\n            string suffix = hash.Substring(5);\r\n\r\n            try\r\n            {\r\n                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(\r\n                    \"https:\/\/api.pwnedpasswords.com\/range\/\" + prefix\r\n                );\r\n                request.Method = \"GET\";\r\n                request.Timeout = 5000;\r\n                request.ReadWriteTimeout = 5000;\r\n                request.UserAgent =\r\n                    \"ASPRunner.NET-PwnedPasswordCheck\";\r\n                request.Headers[\"Add-Padding\"] = \"true\";\r\n\r\n                using (HttpWebResponse response =\r\n                    (HttpWebResponse)request.GetResponse())\r\n                using (StreamReader reader = new StreamReader(\r\n                    response.GetResponseStream()\r\n                ))\r\n                {\r\n                    if (response.StatusCode != HttpStatusCode.OK)\r\n                        return -1;\r\n\r\n                    string body = reader.ReadToEnd();\r\n                    string[] lines = body.Split(\r\n                        new[] { \"\\r\\n\", \"\\n\", \"\\r\" },\r\n                        StringSplitOptions.RemoveEmptyEntries\r\n                    );\r\n\r\n                    foreach (string line in lines)\r\n                    {\r\n                        string[] parts = line.Trim().Split(':');\r\n\r\n                        if (\r\n                            parts.Length == 2\r\n                            && parts[0].ToUpperInvariant() == suffix\r\n                        )\r\n                        {\r\n                            int count;\r\n                            return Int32.TryParse(parts[1], out count)\r\n                                ? count\r\n                                : -1;\r\n                        }\r\n                    }\r\n                }\r\n\r\n                return 0;\r\n            }\r\n            catch\r\n            {\r\n                return -1;\r\n            }\r\n        }\r\n\r\n        private static XVar IpCheckFailure(string message)\r\n        {\r\n            return new XVar(\r\n                \"success\", false,\r\n                \"error\", message\r\n            );\r\n        }\r\n\r\n        public static XVar CheckIPReputation(\r\n            string ipAddress,\r\n            string apiKey\r\n        )\r\n        {\r\n            if (String.IsNullOrWhiteSpace(ipAddress))\r\n                return IpCheckFailure(\"Invalid IP address.\");\r\n\r\n            dynamic sql = DB.PrepareSQL(\r\n                @\"SELECT *\r\n                  FROM ip_reputation_cache\r\n                  WHERE ip_address=':1'\r\n                  AND checked_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)\",\r\n                ipAddress\r\n            );\r\n\r\n            dynamic rs = DB.Query(sql);\r\n            dynamic cached = XVar.Pack(rs) ? rs.fetchAssoc() : null;\r\n\r\n            if (XVar.Pack(cached))\r\n            {\r\n                return new XVar(\r\n                    \"success\", true,\r\n                    \"cached\", true,\r\n                    \"abuseScore\", cached[\"abuse_score\"],\r\n                    \"totalReports\", cached[\"total_reports\"],\r\n                    \"countryCode\", cached[\"country_code\"],\r\n                    \"usageType\", cached[\"usage_type\"]\r\n                );\r\n            }\r\n\r\n            string url =\r\n                \"https:\/\/api.abuseipdb.com\/api\/v2\/check\"\r\n                + \"?ipAddress=\" + HttpUtility.UrlEncode(ipAddress)\r\n                + \"&maxAgeInDays=90\";\r\n\r\n            try\r\n            {\r\n                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);\r\n                request.Method = \"GET\";\r\n                request.Timeout = 5000;\r\n                request.ReadWriteTimeout = 5000;\r\n                request.Accept = \"application\/json\";\r\n                request.Headers[\"Key\"] = apiKey;\r\n\r\n                string responseText;\r\n\r\n                using (HttpWebResponse response =\r\n                    (HttpWebResponse)request.GetResponse())\r\n                using (StreamReader reader = new StreamReader(\r\n                    response.GetResponseStream()\r\n                ))\r\n                {\r\n                    if (response.StatusCode != HttpStatusCode.OK)\r\n                        return IpCheckFailure(\r\n                            \"Unable to check IP reputation.\"\r\n                        );\r\n\r\n                    responseText = reader.ReadToEnd();\r\n                }\r\n\r\n                dynamic json = MVCFunctions.runner_json_decode(responseText);\r\n\r\n                if (!XVar.Pack(json) || !XVar.Pack(json[\"data\"]))\r\n                    return IpCheckFailure(\r\n                        \"Unexpected AbuseIPDB response.\"\r\n                    );\r\n\r\n                dynamic data = json[\"data\"];\r\n                dynamic abuseScore = data[\"abuseConfidenceScore\"];\r\n                dynamic totalReports = data[\"totalReports\"];\r\n                dynamic countryCode = data[\"countryCode\"];\r\n                dynamic usageType = data[\"usageType\"];\r\n\r\n                DB.Exec(DB.PrepareSQL(\r\n                    @\"DELETE FROM ip_reputation_cache\r\n                      WHERE ip_address=':1'\",\r\n                    ipAddress\r\n                ));\r\n\r\n                DB.Exec(DB.PrepareSQL(\r\n                    @\"INSERT INTO ip_reputation_cache\r\n                      (ip_address, abuse_score, total_reports,\r\n                       country_code, usage_type, checked_at)\r\n                      VALUES (':1', :2, :3, ':4', ':5', NOW())\",\r\n                    ipAddress,\r\n                    abuseScore,\r\n                    totalReports,\r\n                    countryCode,\r\n                    usageType\r\n                ));\r\n\r\n                return new XVar(\r\n                    \"success\", true,\r\n                    \"cached\", false,\r\n                    \"abuseScore\", abuseScore,\r\n                    \"totalReports\", totalReports,\r\n                    \"countryCode\", countryCode,\r\n                    \"usageType\", usageType\r\n                );\r\n            }\r\n            catch\r\n            {\r\n                return IpCheckFailure(\r\n                    \"Unable to check IP reputation.\"\r\n                );\r\n            }\r\n        }\r\n    }\r\n}<\/textarea><\/pre>\n<\/div>\n<h3>3. Checking the password from a field event<\/h3>\n<p>Open the Register page in Page Designer. Select the password field, open its field events, and add an event that runs when the user leaves the field.<\/p>\n<p>Use the following JavaScript in <strong>Client Before<\/strong>:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\npageObj.getItemButton(\"register_save\").addClass(\"disabled\");\r\n\r\nparams[\"password\"] = this.getValue();\r\n\r\nif (!params[\"password\"]) {\r\n    return false;\r\n}<\/textarea><\/pre>\n<\/div>\n<p>In the <strong>Server<\/strong> part, add the code for your product.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n$result[\"count\"] = pwnedPasswordCount($params[\"password\"]);<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\nresult[\"count\"] = CommonFunctions.PwnedPasswordCount(\r\n    parameters[\"password\"].ToString()\r\n);<\/textarea><\/pre>\n<\/div>\n<p>Use this JavaScript in <strong>Client After<\/strong>:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\nvar count = Number(result.count);\r\n\r\nif (count < 0) {\r\n    swal(\"The password could not be checked. Please try again.\");\r\n    return;\r\n}\r\n\r\nif (count > 0) {\r\n    swal(\r\n        \"This password has appeared in known data breaches \"\r\n        + count\r\n        + \" times. Please choose another password.\"\r\n    );\r\n    return;\r\n}\r\n\r\npageObj.getItemButton(\"register_save\").removeClass(\"disabled\");<\/textarea><\/pre>\n<\/div>\n<h3>4. Disabling the Register button when the page loads<\/h3>\n<p>Open <strong>Events \u2192 Global events \u2192 Register page \u2192 JavaScript OnLoad<\/strong> and disable the &#8216;Register&#8217; button until the password check succeeds:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\npageObj.getItemButton(\"register_save\").addClass(\"disabled\");<\/textarea><\/pre>\n<\/div>\n<p><strong>register_save<\/strong> is the Item ID of the standard Register button.<\/p>\n<h3>5. Enforcing the password check before registration<\/h3>\n<p>The field event improves the user experience, but client-side controls can be bypassed. Repeat the check in <strong>Events \u2192 Global events \u2192 Register page \u2192 Before registration<\/strong>.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n$count = pwnedPasswordCount($userdata[\"password\"]);\r\n\r\nif ($count < 0) {\r\n    $message = \"The password could not be checked. Please try again.\";\r\n    return false;\r\n}\r\n\r\nif ($count > 0) {\r\n    $message = \"This password has appeared in known data breaches. \"\r\n        . \"Please choose another password.\";\r\n    return false;\r\n}\r\n\r\nreturn true;<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\nint count = CommonFunctions.PwnedPasswordCount(\r\n    userdata[\"password\"].ToString()\r\n);\r\n\r\nif (count < 0)\r\n{\r\n    message = \"The password could not be checked. Please try again.\";\r\n    return false;\r\n}\r\n\r\nif (count > 0)\r\n{\r\n    message = \"This password has appeared in known data breaches. \"\r\n        + \"Please choose another password.\";\r\n    return false;\r\n}\r\n\r\nreturn true;<\/textarea><\/pre>\n<\/div>\n<p>Replace <strong>password<\/strong> if your login table uses a different password field name. Field names are case-sensitive.<\/p>\n<h3>6. Checking IP reputation in After App Init<\/h3>\n<p>Create a free account at <a href=\"https:\/\/www.abuseipdb.com\/register\">abuseipdb.com\/register<\/a>. After signing in, open the account dashboard, select &#8216;API Settings&#8217;, and generate an API v2 key.<\/p>\n<p>Add the following code after the existing code in <strong>Events \u2192 Global events \u2192 After App Init<\/strong>. Replace the placeholder with your API key and keep the real key out of public project exports and source repositories.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n$ipAddress = $_SERVER[\"REMOTE_ADDR\"] ?? \"\";\r\n$apiKey = \"YOUR_ABUSEIPDB_API_KEY\";\r\n\r\n$ipResult = checkIPReputation($ipAddress, $apiKey);\r\n\r\nif (\r\n    $ipResult[\"success\"]\r\n    && $ipResult[\"abuseScore\"] >= 90\r\n) {\r\n    http_response_code(403);\r\n    exit(\"Access denied.\");\r\n}<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\nstring ipAddress = HttpContext.Current.Request.UserHostAddress;\r\nstring apiKey = \"YOUR_ABUSEIPDB_API_KEY\";\r\n\r\ndynamic ipResult = CommonFunctions.CheckIPReputation(\r\n    ipAddress,\r\n    apiKey\r\n);\r\n\r\nif (\r\n    XVar.Pack(ipResult[\"success\"])\r\n    && Convert.ToInt32(ipResult[\"abuseScore\"].ToString()) >= 90\r\n)\r\n{\r\n    HttpContext.Current.Response.StatusCode = 403;\r\n    MVCFunctions.Echo(\"Access denied.\");\r\n    MVCFunctions.Exit();\r\n}<\/textarea><\/pre>\n<\/div>\n<p>The <strong>checkIPReputation()<\/strong> function reuses a cached result for 24 hours. The sample uses the MySQL <strong>DATE_SUB()<\/strong> and <strong>NOW()<\/strong> functions. Adjust that date expression if your project uses another database.<\/p>\n<p>An AbuseIPDB failure does not block the application. A score of 90 or higher does. Adjust the threshold to match your own security requirements.<\/p>\n<p>If the application runs behind Cloudflare, a load balancer, or another reverse proxy, configure the web server to restore the visitor\u2019s original IP address before this code runs. If the application is accessed directly, no additional configuration is required. Do not read X-Forwarded-For or another forwarding header unless requests to the application can come only through a trusted proxy, because these headers can otherwise be forged.<\/p>\n<p>After rebuilding the project, test registration with a known compromised test password and then with a unique password. Confirm that the server-side registration event still rejects a compromised password if JavaScript is disabled. For the IP check, confirm that the first lookup creates a cache row and subsequent page requests reuse it for 24 hours.<\/p>\n<p>The Pwned Passwords implementation follows the service&#8217;s k-anonymity range-search model and uses response padding. See the <a href=\"https:\/\/haveibeenpwned.com\/API\/v3#PwnedPasswords\">Pwned Passwords API documentation<\/a>. See the <a href=\"https:\/\/docs.abuseipdb.com\/#check-endpoint\">AbuseIPDB Check endpoint documentation<\/a> for API-key and response details.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Add compromised-password screening and IP reputation checks to PHPRunner and ASPRunner.NET applications using haveibeenpwned.com, abuseipdb.com, field events, and a local cache.<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,1,94,8],"tags":[],"class_list":["post-3474","post","type-post","status-publish","format-standard","hentry","category-asp-net","category-php-category","category-security","category-tutorials"],"_links":{"self":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/3474","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/comments?post=3474"}],"version-history":[{"count":4,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/3474\/revisions"}],"predecessor-version":[{"id":3480,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/3474\/revisions\/3480"}],"wp:attachment":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/media?parent=3474"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/categories?post=3474"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/tags?post=3474"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}