{"id":22120545,"url":"https://github.com/islamic-network/waf","last_synced_at":"2025-07-25T12:33:49.033Z","repository":{"id":37546515,"uuid":"146272475","full_name":"islamic-network/waf","owner":"islamic-network","description":"The Islamic Network WAF","archived":true,"fork":false,"pushed_at":"2024-11-02T05:25:34.000Z","size":195,"stargazers_count":8,"open_issues_count":11,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-02T07:51:14.364Z","etag":null,"topics":["docker","firewall","php7","waf"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/islamic-network.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-08-27T08:56:30.000Z","updated_at":"2025-03-29T10:09:44.000Z","dependencies_parsed_at":"2022-08-18T02:55:52.279Z","dependency_job_id":null,"html_url":"https://github.com/islamic-network/waf","commit_stats":null,"previous_names":[],"tags_count":62,"template":false,"template_full_name":null,"purl":"pkg:github/islamic-network/waf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/islamic-network%2Fwaf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/islamic-network%2Fwaf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/islamic-network%2Fwaf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/islamic-network%2Fwaf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/islamic-network","download_url":"https://codeload.github.com/islamic-network/waf/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/islamic-network%2Fwaf/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267007602,"owners_count":24020261,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-25T02:00:09.625Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","firewall","php7","waf"],"created_at":"2024-12-01T14:27:25.365Z","updated_at":"2025-07-25T12:33:48.705Z","avatar_url":"https://github.com/islamic-network.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"## بِسْمِ اللهِ الرَّحْمٰنِ الرَّحِيْمِ\n\n[![CircleCI](https://circleci.com/gh/islamic-network/waf.svg?style=shield)](https://circleci.com/gh/islamic-network/waf) \n[![](https://img.shields.io/github/release/islamic-network/waf.svg)](https://github.com/islamic-network/waf/releases)\n![Docker Pulls](https://img.shields.io/docker/pulls/islamicnetwork/waf)\n[![](https://img.shields.io/github/license/islamic-network/waf.svg)](https://github.com/islamic-network/waf/blob/master/LICENSE)\n\n### Deprecation Notice\nAs of May 15, 2020, this project is marked as deprecated. Whilst it works just fine, running it at scale is extremely expensive. A request takes 30 MB to process and 2% of the CPU. This means that on a $10 server on DO or Linode you can only serve 35 requests - so to service about 300 requests a second you would need $100+ in servers. This problem can be fixed by taking the flexibility from the YAML file parser to allow exact matches as opposed to strings contained in the headers - but that flexibity is what made this very useful. If we have to implement exact matches, then it is probably best done as a module for Apache, Nginx, or Kong API Gateway.\n\n# A WAF for Developers\n\n**This README file is still a work in progress.**\n\nThis is a WAF written in PHP.  To configure and use it, you need to know YAML and understand the various parts of an HTTP request.\n\nIt is completely dockerised and to deploy it you will need to run a docker command and specify some environment variables.\n\n\n## Current Status\nThis project is stable but has a basic feature set. It also gets updates, often, but a breaking change will go in a new major version.\n\nSome of the things to do are listed under [issues](https://github.com/islamic-network/waf/issues).\n\n## Contributions and Support\n\nPull requests are always welcome. For feature requests, please feel free to raise an issue.\n\n## Why was it written?\n\nWe needed a WAF for the AlAdhan API.\n\nWe tried to use Incapsula and it wasn't something we could afford for the free services offered by [Islamic Network](https://islamic.network).\n\nCloudFlare was good, but it seemed to have been blocked by ISPs in Russia and China (and it doesn't really allow us to write any custom rules for the WAF).\n\nIf you've ever tried to use something like ModSecurity, you'll know it's tedious. \n\nThis WAF allows you to write rules in a yaml file - that's much easier to read and write for most developers.\n\n## Who is this for?\n\nFor developers looking to deploy a WAF within their apps or outside their API Gateway.\n\nEventually, we will provide OWASP ruleset files that you can simply include in your installation.\n\nWe will also, God willing, offer a hosted service and make the production deployment mechanism open source in due course. \n\n**The hosted service is currently being trailled.** It basically allows you to manage your ruleset file in a git repo and automatically deploys to your hosted WAF. \n\nIf you'd like to trial this, please email support@islamic.network.\n\n## Why YAML and PHP?\n\nBecause they're easy to use, easy to maintain and easy to manage.\n\n# Installation and Usage\n\nThis WAF is production ready and can be deployed as a proxy using the provided Dockerfile or docker-compose file.\n\nYou can even use the already published docker image at quay.io/islamic-network/waf or islamicnetwork/waf.\n\n\n## The underlying library and how it works\n\nYou'll need to understand some PHP for this section.\n\nTo see how the waf processes your YAML file, see the bootstrap/wafMiddleware.php file.\n\nIn a nutshell, this is what it does:\n\n\n```php\n\u003c?php\n\nuse IslamicNetwork\\Waf\\Model\\RuleSet;\nuse IslamicNetwork\\Waf\\Model\\RuleSetMatcher;\nuse Slim\\Http\\Request; // Or any other PSR7 Compliant http request object\n\n\n$ruleset = new RuleSet($filePath);\n$matcher = new RuleSetMatcher($ruleset, $request-\u003egetHeaders(), $_SERVER);\n\nif ($matcher-\u003eisWhitelisted()) {\n    // Do nothing. Maybe append headers.\n}\nif ($matcher-\u003eisBlacklisted()) {\n    // Throw http 403\n}\n\nif ($matcher-\u003eisRatelimited()) {\n    $rl = new \\IslamicNetwork\\Waf\\Model\\RateLimit($memcached, $matcher-\u003egetMatched()['rate'], $matcher-\u003egetMatched()['time']);\n    if ($rl-\u003eisLimited()) {\n        // Throw http 429\n    }\n}\n```\n\n## Defining Rulesets, Rules and Matchers\nThe WAF reads a Ruleset YAML file and decides if any of the above code will return true or not.\n\nLet's have a look at the structure of this file.\n\n```yaml\n# Note that values separated with a comma are always OR and each of the global keys are always AND\nblacklist:\n  - name: my blacklist # required\n    headers: # required\n      request: # required.HTTP_ appended\n        X-FORWARDED_FOR: [123.456.78.9, 78.99.90.3]\n        FORWARDED: [123.456.78.9, 78.99.90.3]\n        USER-aGENT: [Mozilla/5.0, python-requests/2.8]\n      server: # required\n        rEQUEST_URI: [path/one, path/two]\n        QUERY_STRING: [one=yes\u0026two=no\u0026three=maybe, another=0\u0026someother=1]\n\nwhitelist:\n  - name: my whitelist # required\n    headers: # required\n      request: # required HTTP_ appended\n          X_FORWARDED_FOR: [123.456.78.9, 78.99.90.3]\n          FORWARDED: [123.456.78.9, 78.99.90.3]\n          X_FORWARDED: [123.456.78.9, 78.99.90.3]\n          X_CLUSTER_CLIENT_IP: [123.456.78.9, 78.99.90.3]\n          CLIENT_IP: [123.456.78.9, 78.99.90.3]\n          USER_AGENT: [Mozilla/5.0, python-requests/2.8]\n          REFERER: [http://something.com, 'something else']\n          COOKIES: [cookie_one, another_cookie]\n      server: # required\n          REQUEST_URI: [path/one, path/two]\n          QUERY_STRING: [one=yes\u0026two=no\u0026three=maybe]\nratelimit:\n  - name: limiter # required\n    headers: # required\n      request: # required HTTP_ appended\n          X_FORWARDED_FOR: [123.456.78.9, 78.99.90.3]\n          FORWARDED: [123.456.78.9, 78.99.90.3]\n          X_FORWARDED: [123.456.78.9, 78.99.90.3]\n          X_CLUSTER_CLIENT_IP: [123.456.78.9, 78.99.90.3]\n          CLIENT_IP: [123.456.78.9, 78.99.90.3]\n          USER_AGENT: [Mozilla/5.0, python-requests/2.8]\n          REFERER: [http://something.com, 'something else']\n          COOKIES: [cookie_one, another_cookie]\n      server: # required\n          REQUEST_URI: [path/one, path/two]\n          QUERY_STRING: [one=yes\u0026two=no\u0026three=maybe]\n    limit:\n      rate: 1000\n      time: 3600 #60 = 1 minute, 3600 = 1 hour, 86400 = 1 day\n  - name: another limiter # required\n    headers: # required\n        request: # required HTTP_ appended\n            X_FORWARDED_FOR: [123.456.78.9, 78.99.90.3]\n            FORWARDED: [123.456.78.9, 78.99.90.3]\n            X_FORWARDED: [123.456.78.9, 78.99.90.3]\n            X_CLUSTER_CLIENT_IP: [123.456.78.9, 78.99.90.3]\n            CLIENT_IP: [123.456.78.9, 78.99.90.3]\n            USER_AGENT: [Mozilla/5.0, python-requests/2.8]\n            REFERER: [http://something.com, 'something else']\n            COOKIES: [cookie_one, another_cookie]\n        server: # required\n            REQUEST_URI: [path/one, path/two]\n            QUERY_STRING: [one=yes\u0026two=no\u0026three=maybe]\n    limit:\n      rate: 1000\n      time: 3600 #60 = 1 minute, 3600 = 1 hour, 86400 = 1 day\n````\n\n## Ruleset\n\nCurrently, 3 Rulesets are supported. In the above file, these are:\n1. Whitelist\n2. Blacklist\n3. Ratelimit\n\n### Rule\n\nAn instance of a ruleset, is a rule. So in the above Yaml, there is 1 whitelist rule, 1 blacklist rule, and there are 2 ratelimit rules.\n\nA rule comprises a name, matchers (and submatchers) and a message (the message is coming soon). See https://github.com/islamic-network/waf/issues/8.\n\n#### Matcher\n\nCurrently, only the 'headers' matcher is supported, and in that you can specify request and server headers to match. Header names can have - or _ and are case agnostic.\n\nA 'body' matcher is in progress. See https://github.com/islamic-network/waf/issues/6.\n\n#### How Matchers Work\n\nEach matcher or submatcher can be an array.\n\nSo the blacklist rule 'my blacklist' has a headers matcher which basically reads like this:\n\n\n```\n// The below is pseudo code\n\nif the request header\n    x-forwarded-for contains 123.456.78.9 OR 78.99.90.3\n    AND\n    forwarded contains 123.456.78.9 OR 78.99.90\n    AND\n    user-agent contains Mozilla/5.0 OR python-requests/2.8\nAND the server header contains\n    request-uri contains path/one OR path/two\n    AND\n    query-string contains one=yes\u0026two=no\u0026three=maybe OR another=0\u0026someother=1\nTHEN\n    this rule is matched (isBlacklisted returns true)\nELSE\n    this rule is unmatched (isBlacklisted returns false)\n\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fislamic-network%2Fwaf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fislamic-network%2Fwaf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fislamic-network%2Fwaf/lists"}