OpenResty
Installation
1
sudo luarocks install botbye-openresty
Configuration
Configure the botbye_phishing module in your nginx http block. On construction the client reports the server-side integration via a best-effort initRequest() handshake — it needs a botbye_state shared dict (the once-per-instance guard) and a call from init_worker_by_lua_block.
Getting clientKey
clientKey is the public, browser-safe identifier of your phishing project. It travels in the asset URL path, so it is safe to expose — no secret token and no Base64 encoding are required.
Find it on the Get Started screen of your phishing project in the BotBye dashboard.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
http {
# ...
lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
lua_ssl_verify_depth 3;
lua_shared_dict botbye_state 1m;
init_by_lua_block {
require("botbye_phishing").setConf({
endpoint = "https://verify.botbye.com",
client_key = "<public-client-key>",
})
}
init_worker_by_lua_block {
require("botbye_phishing").initRequest()
}
}
Usage
Anti-phishing needs two routes on your own origin: an SVG route — the URL your client code passes to getCatcher({ url }) — and a PNG route that the SVG references. The paths are arbitrary, so name them like ordinary static assets and let the route decide the format. A path that spells out the vendor or the feature (/api/phishing/…) is what a copied page is searched for and stripped of, and a format query param on the pixel URL reads the same way.
On the SVG route, pass inner_png_url — the absolute URL of your PNG route: the returned SVG embeds it as its tracking pixel. It is required for format = "svg": Lua has no type to demand it the way the other SDKs do, so the module checks at call time and returns nil, "missing inner_png_url" without it. Build that URL from your own host — image_id is owned by the SDK and is not read from the forwarded query. skip_execution defaults to true, the script-less SVG; pass false only for browsers predating crossorigin on svg <image> (Chrome 118, Firefox 114, Safari 17.2), where the script-driven variant is the one that still reports.
These examples forward no query: format, image_id and executable are set by the call itself, and only module_name / module_version pass through from the browser's pixel query — which a catcher mounted on your own routes never receives.
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
location /your-image-route.svg {
content_by_lua_block {
local phishing = require("botbye_phishing")
-- Origin and Referer are read off the request by the module itself
local res, err = phishing.fetchCatcher({
format = "svg",
inner_png_url = "https://" .. ngx.var.host .. "/your-image-route.png",
})
if not res then
ngx.status = 502
ngx.say(err or "upstream error")
return ngx.exit(502)
end
ngx.status = res.status or 200
ngx.header["Content-Type"] = (res.headers and res.headers["Content-Type"]) or "image/svg+xml"
if res.body ~= nil then
ngx.print(res.body)
end
}
}
location /your-image-route.png {
content_by_lua_block {
local phishing = require("botbye_phishing")
local res, err = phishing.fetchCatcher({ format = "png" })
if not res then
ngx.status = 502
ngx.say(err or "upstream error")
return ngx.exit(502)
end
ngx.status = res.status or 200
ngx.header["Content-Type"] = (res.headers and res.headers["Content-Type"]) or "image/png"
if res.body ~= nil then
ngx.print(res.body)
end
}
}
Settings
Configuration parameters for phishing integration (botbye_phishing.setConf):
| Setting | Description | Required | Default Value |
|---|---|---|---|
| endpoint | Host of the phishing API | no | https://verify.botbye.com |
| client_key | Public client-key of your phishing project | yes | - |
| connection_timeout | Connect / send / read timeout of one upstream call, ms | no | 1000 |
setConf takes these keys verbatim: an unknown key (clientKey, for one) is logged as [BotBye] ignoring unknown config key and dropped, leaving the client-key empty.