-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCrytifyGateFilter.php
More file actions
140 lines (123 loc) · 4.9 KB
/
Copy pathCrytifyGateFilter.php
File metadata and controls
140 lines (123 loc) · 4.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<?php
namespace App\Filters;
/**
* Crytify Gate — CodeIgniter 4 Filter
*
* Drop this file into your customer's app/Filters/ directory.
* Register it in app/Config/Filters.php as a 'before' filter.
*
* --- Registration in app/Config/Filters.php ---------------------------------
*
* public array $aliases = [
* // ... existing filters
* 'crytify' => \App\Filters\CrytifyGateFilter::class,
* ];
*
* --- Apply to specific routes in app/Config/Routes.php ----------------------
*
* // Protect a single page
* $routes->get('checkout', 'OrderController::checkout', ['filter' => 'crytify']);
*
* // Protect all pages in a group
* $routes->group('members', ['filter' => 'crytify'], function ($routes) {
* $routes->get('dashboard', 'MemberController::dashboard');
* $routes->get('profile', 'MemberController::profile');
* });
*
* --- Or apply globally in app/Config/Filters.php ----------------------------
*
* public array $globals = [
* 'before' => [
* 'crytify',
* ],
* ];
*
* --- Environment variables (.env) --------------------------------------------
*
* CRYTIFY_API_KEY = ck_live_xxxxxxxxxxxx
* CRYTIFY_MODE = medium
* CRYTIFY_BLOCK_VIEW = crytify/blocked (optional, default: 403 plain text)
*/
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
// Inline the Crytify client so this is a self-contained file.
// In production you'd composer require crytify/php-sdk instead.
require_once __DIR__ . '/../../../crytify-sdk/CrytifyClient.php'; // adjust path
class CrytifyGateFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
$apiKey = env('CRYTIFY_API_KEY', '');
// Skip if not configured — don't break sites with incomplete setup
if ($apiKey === '') {
return null;
}
$mode = env('CRYTIFY_MODE', 'medium');
$ip = $this->resolveIp($request);
// Collect headers Crytify can use
$headers = [];
foreach (['user-agent', 'accept', 'accept-language', 'accept-encoding',
'sec-fetch-site', 'sec-fetch-mode', 'sec-fetch-dest',
'sec-ch-ua', 'sec-ch-ua-mobile', 'sec-ch-ua-platform'] as $h) {
$val = $request->getHeaderLine($h);
if ($val !== '') {
$headers[$h] = $val;
}
}
$crytify = new \CrytifyClient($apiKey, 'https://api.crytify.com', $mode);
$result = $crytify->trust($ip, $headers, [
'path' => '/' . ltrim($request->getUri()->getPath(), '/'),
'method' => $request->getMethod(),
]);
if ($result->isBlock()) {
$blockView = env('CRYTIFY_BLOCK_VIEW', '');
if ($blockView !== '' && view($blockView, [], ['saveData' => false])) {
return response()->setStatusCode(403)->setBody(view($blockView));
}
return response()
->setStatusCode(403)
->setContentType('text/html')
->setBody($this->defaultBlockPage());
}
if ($result->isChallenge() && $result->challengeUrl()) {
return redirect()->to($result->challengeUrl());
}
return null; // Allow
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
return $response;
}
private function resolveIp(RequestInterface $request): string
{
// Cloudflare
$cf = $request->getHeaderLine('CF-Connecting-IP');
if ($cf !== '') return $cf;
// Standard proxy header — only trust if behind a trusted load balancer
$xff = $request->getHeaderLine('X-Forwarded-For');
if ($xff !== '') {
$first = trim(explode(',', $xff)[0]);
if (filter_var($first, FILTER_VALIDATE_IP)) return $first;
}
return $request->getIPAddress();
}
private function defaultBlockPage(): string
{
return '<!DOCTYPE html><html><head><meta charset="utf-8">
<title>Access Denied</title>
<style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#f8fafc}
.box{text-align:center;max-width:420px;padding:40px 32px}
h1{color:#0f172a;font-size:22px;margin:0 0 12px}
p{color:#64748b;margin:0;line-height:1.6}</style>
</head><body>
<div class="box">
<svg width="52" height="52" fill="none" viewBox="0 0 24 24" style="margin-bottom:20px">
<circle cx="12" cy="12" r="10" stroke="#e11d48" stroke-width="1.5"/>
<path d="M15 9l-6 6M9 9l6 6" stroke="#e11d48" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<h1>Access Denied</h1>
<p>Our systems detected unusual activity from your connection and blocked access to this page. If you believe this is a mistake, please contact the site owner.</p>
</div></body></html>';
}
}