mirror of
https://github.com/midoks/mdserver-web.git
synced 2026-10-10 01:39:25 +08:00
update
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
-- Copyright (C) midoks
|
||||
|
||||
local _M = { _VERSION = '1.0' }
|
||||
local mt = { __index = _M }
|
||||
local setmetatable = setmetatable
|
||||
|
||||
local obf_log = require "resty.obf.log"
|
||||
local tpl = require "resty.obf.tpl"
|
||||
local log_fmt = obf_log.fmt
|
||||
local aes = require "resty.aes"
|
||||
local ffi = require "ffi"
|
||||
local ffi_str = ffi.string
|
||||
local random = require "resty.random"
|
||||
local util = require "resty.obf.util"
|
||||
local obf_cache = ngx.shared.obf_cache
|
||||
local obf_cache_bytes = 0
|
||||
|
||||
local find = string.find
|
||||
local log = ngx.log
|
||||
|
||||
|
||||
function _M.new(self)
|
||||
local self = {
|
||||
}
|
||||
return setmetatable(self, mt)
|
||||
end
|
||||
|
||||
-- 编码
|
||||
function _M.obf_encode(content)
|
||||
local enc, tag, iv_bin
|
||||
local content_type = ngx.header.content_type or ""
|
||||
local upstream_ct = ngx.var.upstream_http_content_type or ""
|
||||
local cl = ngx.header["Content-Length"] or ""
|
||||
local upstream_cl = ngx.var.upstream_http_content_length or ""
|
||||
|
||||
local key = random.bytes(8, true)
|
||||
-- log(ngx.ERR, log_fmt("enc=aes-256-gcm, pass_b64=%s", ngx.encode_base64(password)))
|
||||
local cipher = aes.cipher(256, "gcm")
|
||||
local a, aerr = aes:new(key, nil, cipher, aes.hash.md5, 1, 12, true)
|
||||
|
||||
if not a then
|
||||
log(ngx.ERR, log_fmt("aes init error: %s", tostring(aerr)))
|
||||
else
|
||||
iv_bin = ffi_str(a._iv, 12)
|
||||
local res, eerr = a:encrypt(content)
|
||||
if not res then
|
||||
log(ngx.ERR, log_fmt("aes encrypt error: %s", tostring(eerr)))
|
||||
else
|
||||
if type(res) == "table" then
|
||||
enc = res[1]
|
||||
tag = res[2]
|
||||
else
|
||||
enc = res
|
||||
end
|
||||
content = enc .. (tag or "")
|
||||
end
|
||||
end
|
||||
|
||||
return enc or content, key, iv_bin, tag
|
||||
end
|
||||
|
||||
function _M.get_cache_key()
|
||||
local args = ngx.req.get_uri_args()
|
||||
local parts = {}
|
||||
for k, v in pairs(args or {}) do
|
||||
if k ~= "obf" then
|
||||
local val = v
|
||||
if type(val) == "table" then val = val[1] end
|
||||
parts[#parts + 1] = tostring(k) .. "=" .. tostring(val)
|
||||
end
|
||||
end
|
||||
table.sort(parts)
|
||||
local q = table.concat(parts, "&")
|
||||
local cache_key = (ngx.var.scheme or "") .. "://" .. (ngx.var.host or "") .. (ngx.var.uri or "") .. (q ~= "" and ("?" .. q) or "")
|
||||
return cache_key
|
||||
end
|
||||
|
||||
-- HTML标签混淆
|
||||
function _M.obf_html()
|
||||
local content_type = ngx.header.content_type or ""
|
||||
local ctx = ngx.ctx
|
||||
|
||||
local html_debug = "false"
|
||||
local cache_timeout = 600
|
||||
|
||||
local args = ngx.req.get_uri_args()
|
||||
obf = args and args.obf or nil
|
||||
if obf == "debug" then
|
||||
html_debug = "true"
|
||||
end
|
||||
|
||||
local close_debug = ngx.var.close_debug
|
||||
if close_debug == "true" then
|
||||
html_debug = "false"
|
||||
end
|
||||
|
||||
local var_obf_timeout = ngx.var.obf_timeout
|
||||
if var_obf_timeout then
|
||||
cache_timeout = var_obf_timeout
|
||||
end
|
||||
-- log(ngx.ERR, log_fmt("var_obf_timeout: %s", tostring(var_obf_timeout)))
|
||||
|
||||
if not ctx.obf_buffer then
|
||||
ctx.obf_buffer = {}
|
||||
ctx.obf_passthrough = false
|
||||
end
|
||||
|
||||
local chunk, eof = ngx.arg[1], ngx.arg[2]
|
||||
if chunk and chunk ~= "" then
|
||||
if not ctx.obf_first_t then
|
||||
ctx.obf_first_t = util.tmark()
|
||||
end
|
||||
if ctx.obf_passthrough then
|
||||
ngx.arg[1] = chunk
|
||||
else
|
||||
ctx.obf_buffer[#ctx.obf_buffer + 1] = chunk
|
||||
ngx.arg[1] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if eof then
|
||||
if ctx.obf_passthrough then
|
||||
return
|
||||
end
|
||||
local prof = ngx.var.obf_prof
|
||||
local t_all0 = util.tmark()
|
||||
local content = table.concat(ctx.obf_buffer)
|
||||
local obf_cache = ngx.shared.obf_cache
|
||||
|
||||
if find(content_type, "text/html", 1, true) then
|
||||
|
||||
local var_rand_var = ngx.var.obf_rand_var
|
||||
local var_rand_extra = ngx.var.obf_rand_extra
|
||||
local var_b64 = ngx.var.obf_uint8_b64
|
||||
local js_mode = ngx.var.obf_js_mode
|
||||
local js_url = ngx.var.obf_js_url
|
||||
local cache_key = _M.get_cache_key()..tostring(html_debug)..tostring(var_rand_var)..tostring(var_rand_extra)..tostring(var_b64)..tostring(js_mode)..tostring(js_url)
|
||||
local cached = obf_cache and obf_cache:get(cache_key)
|
||||
if cached then
|
||||
if prof == "true" then
|
||||
log(ngx.ERR, log_fmt("obf_prof cache_hit=1 size=%d total_ms=%.2f wait_ms=%.2f", #cached, util.dt_ms(t_all0), ctx.obf_first_t and util.dt_ms(ctx.obf_first_t) or 0))
|
||||
end
|
||||
ngx.arg[1] = cached
|
||||
ctx.obf_buffer = nil
|
||||
return
|
||||
else
|
||||
if prof == "true" then
|
||||
log(ngx.ERR, log_fmt("obf_prof cache_miss=1"))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local t_enc0 = util.tmark()
|
||||
local content,key,iv,tag = _M.obf_encode(content)
|
||||
local enc_ms = util.dt_ms(t_enc0)
|
||||
|
||||
local t_ser0 = util.tmark()
|
||||
local content_data = util.to_uint8array(content or "")
|
||||
local iv_data = util.to_uint8array(iv or "")
|
||||
local tag_data = util.to_uint8array(tag or "")
|
||||
local key_data = util.to_uint8array(key or "")
|
||||
local ser_ms = util.dt_ms(t_ser0)
|
||||
|
||||
local t_tpl0 = util.tmark()
|
||||
local html_data = tpl.content(content_data, iv_data, tag_data, key_data,tostring(html_debug))
|
||||
local tpl_ms = util.dt_ms(t_tpl0)
|
||||
|
||||
local max_item = tonumber(ngx.var.obf_cache_item_max) or 0
|
||||
local max_bytes = tonumber(ngx.var.obf_cache_max_bytes) or 0
|
||||
local exptime = tonumber(cache_timeout) or 600
|
||||
if obf_cache then
|
||||
if max_item <= 0 or #html_data <= max_item then
|
||||
if max_bytes > 0 then
|
||||
local free = obf_cache:free_space()
|
||||
if not free or free >= #html_data then
|
||||
obf_cache:set(cache_key, html_data, exptime)
|
||||
end
|
||||
else
|
||||
obf_cache:set(cache_key, html_data, exptime)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if prof == "true" then
|
||||
log(ngx.ERR, log_fmt("obf_prof size=%d enc_ms=%.2f ser_ms=%.2f tpl_ms=%.2f total_ms=%.2f wait_ms=%.2f", #content, enc_ms, ser_ms, tpl_ms, util.dt_ms(t_all0), ctx.obf_first_t and util.dt_ms(ctx.obf_first_t) or 0))
|
||||
end
|
||||
ngx.arg[1] = html_data
|
||||
end
|
||||
|
||||
ctx.obf_buffer = nil
|
||||
ctx.obf_passthrough = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _M.done()
|
||||
local content_type = ngx.header.content_type or ""
|
||||
if find(content_type, "text/html") then
|
||||
local prof = ngx.var.obf_prof
|
||||
local t_start = util.tmark()
|
||||
_M.obf_html()
|
||||
if prof == "true" then
|
||||
log(ngx.ERR, log_fmt("total_ms=%.2f", util.dt_ms(t_start)))
|
||||
end
|
||||
else
|
||||
if ngx.var.obf_prof == "true" then
|
||||
log(ngx.ERR, log_fmt("obf_prof skip_ct=1 ct=%s", tostring(content_type)))
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 响应处理函数
|
||||
function _M.process_response()
|
||||
local content_type = ngx.header.content_type or ""
|
||||
local var_close = ngx.var.close_close
|
||||
if var_close == "true" then
|
||||
_M.done()
|
||||
return
|
||||
else
|
||||
local args = ngx.req.get_uri_args()
|
||||
local obf = args and args.obf or nil
|
||||
if obf == "close" then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
_M.done()
|
||||
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,113 @@
|
||||
-- Copyright (C) midoks
|
||||
|
||||
local _M = { _VERSION = '1.0' }
|
||||
local mt = { __index = _M }
|
||||
local setmetatable = setmetatable
|
||||
|
||||
local util = require "resty.obf.util"
|
||||
|
||||
local forgejs = require "resty.obf.forgejs"
|
||||
|
||||
local obf_log = require "resty.obf.log"
|
||||
local log_fmt = obf_log.fmt
|
||||
local log = ngx.log
|
||||
|
||||
|
||||
function _M.new(self)
|
||||
local self = {
|
||||
}
|
||||
return setmetatable(self, mt)
|
||||
end
|
||||
|
||||
function _M.content(data, iv, tag, key, debug_data)
|
||||
local head_html = "<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><link rel=\"icon\" href=\"data:,\"></head><body></body></html>"
|
||||
|
||||
local fj_open = "<script type=\"text/javascript\">"
|
||||
local fj_close = "</script>\n"
|
||||
|
||||
local data_script = "<script>\nvar encrypted={{__HOLD_1__}}; var iv_data="..iv.."; var tag_data="..tag.."; var key="..key..";var d="..debug_data..";function u8ToBytes(u8){var s=\"\";for(var i=0;i<u8.length;i++){s+=String.fromCharCode(u8[i]);}return s;}\n"..
|
||||
"function evpBytesToKey(pass, keyLen, ivLen){\n"..
|
||||
" var m=[]; var i=0; var md=forge.md.md5.create();\n"..
|
||||
" function concatLen(arr){var n=0; for(var j=0;j<arr.length;j++){n+=arr[j].length;} return n;}\n"..
|
||||
" while(concatLen(m) < (keyLen+ivLen)){\n"..
|
||||
" md.start(); if(i>0){ md.update(m[i-1]); }\n"..
|
||||
" md.update(pass);\n"..
|
||||
" var d = md.digest().getBytes(); m.push(d); i++;\n"..
|
||||
" }\n"..
|
||||
" var ms = m.join(\"\"); var key = ms.substring(0,keyLen); var iv = ms.substring(keyLen,keyLen+ivLen);\n"..
|
||||
" return {key:key, iv:iv};\n"..
|
||||
"}\n\n"..
|
||||
"window.onload = function(){\n"..
|
||||
" var startTime = Date.now();\n\n"..
|
||||
" var dk = evpBytesToKey(u8ToBytes(key), 32, 32);\n"..
|
||||
" var decipher = forge.cipher.createDecipher(\"AES-GCM\", dk.key);\n"..
|
||||
" decipher.start({iv: u8ToBytes(iv_data), tag: forge.util.createBuffer(u8ToBytes(tag_data))});\n"..
|
||||
" decipher.update(forge.util.createBuffer(u8ToBytes(encrypted)));\n"..
|
||||
" var ok = decipher.finish();\n\n"..
|
||||
" if (ok) {\n"..
|
||||
" var newDoc = new DOMParser().parseFromString(decipher.output, \"text/html\");\n\n"..
|
||||
" if (d){\n"..
|
||||
" console.log(newDoc);\n"..
|
||||
" console.log(decipher.output);\n"..
|
||||
" }\n"..
|
||||
" document.head.innerHTML = newDoc.head.innerHTML;\n"..
|
||||
" document.open();\n"..
|
||||
" document.write(decipher.output);\n"..
|
||||
" document.close();\n"..
|
||||
" }\n"..
|
||||
" var endTime = Date.now();\n"..
|
||||
" if (d){\n"..
|
||||
" console.log(\"dec cos(ms):\",endTime - startTime);\n"..
|
||||
" }\n"..
|
||||
"}\n</script>\n"
|
||||
|
||||
|
||||
if not (ngx.var.obf_rand_var == "false") then
|
||||
data_script = util.obf_rand(data_script)
|
||||
end
|
||||
|
||||
local prof = ngx.var.obf_prof
|
||||
if not (ngx.var.obf_rand_extra == "false") then
|
||||
local t_add0 = util.tmark()
|
||||
data_script = util.obf_rand_data(data_script)
|
||||
if prof == "true" then
|
||||
log(ngx.ERR, log_fmt("obf_prof obf_rand_data add_ms=%.2f", util.dt_ms(t_add0)))
|
||||
end
|
||||
end
|
||||
|
||||
local t_df0 = util.tmark()
|
||||
data_script = util.data_filter(data_script)
|
||||
if prof == "true" then
|
||||
log(ngx.ERR, log_fmt("obf_prof data_filter ms=%.2f", util.dt_ms(t_df0)))
|
||||
end
|
||||
|
||||
data_script = data_script:gsub("{{__HOLD_1__}}", data)
|
||||
|
||||
local forge_part = ""
|
||||
local mode = ngx.var.obf_js_mode or "link"
|
||||
local url = ngx.var.obf_js_url or ""
|
||||
|
||||
if mode == "link" then
|
||||
if url ~= '' then
|
||||
forge_part = "<script src=\""..url.."\"></script>\n"
|
||||
else
|
||||
forge_part = "<script src=\"https://cdn.jsdelivr.net/npm/node-forge@1.3.1/dist/forge.min.js\"></script>\n"
|
||||
end
|
||||
elseif mode == "inline" then
|
||||
if url ~= '' then
|
||||
forge_part = "<script src=\""..url.."\"></script>\n"
|
||||
else
|
||||
forge_part = fj_open .. forgejs.content() .. fj_close
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat({
|
||||
head_html,
|
||||
forge_part,
|
||||
data_script,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
|
||||
return _M
|
||||
@@ -33,15 +33,25 @@ function _M.obf_encode(content)
|
||||
local cl = ngx.header["Content-Length"] or ""
|
||||
local upstream_cl = ngx.var.upstream_http_content_length or ""
|
||||
|
||||
local key = random.bytes(8, true)
|
||||
-- log(ngx.ERR, log_fmt("enc=aes-256-gcm, pass_b64=%s", ngx.encode_base64(password)))
|
||||
local cipher = aes.cipher(256, "gcm")
|
||||
local a, aerr = aes:new(key, nil, cipher, aes.hash.md5, 1, 12, true)
|
||||
local kdf_raw = ngx.var.obf_kdf_raw == "true"
|
||||
local key
|
||||
local a, aerr
|
||||
if kdf_raw then
|
||||
key = random.bytes(32, true)
|
||||
iv_bin = random.bytes(12, true)
|
||||
a, aerr = aes:new(key, nil, cipher, { iv = iv_bin }, nil, 12, true)
|
||||
else
|
||||
key = random.bytes(8, true)
|
||||
a, aerr = aes:new(key, nil, cipher, aes.hash.md5, 1, 12, true)
|
||||
end
|
||||
|
||||
if not a then
|
||||
log(ngx.ERR, log_fmt("aes init error: %s", tostring(aerr)))
|
||||
else
|
||||
iv_bin = ffi_str(a._iv, 12)
|
||||
if not kdf_raw then
|
||||
iv_bin = ffi_str(a._iv, 12)
|
||||
end
|
||||
local res, eerr = a:encrypt(content)
|
||||
if not res then
|
||||
log(ngx.ERR, log_fmt("aes encrypt error: %s", tostring(eerr)))
|
||||
|
||||
@@ -25,7 +25,34 @@ function _M.content(data, iv, tag, key, debug_data)
|
||||
local fj_open = "<script type=\"text/javascript\">"
|
||||
local fj_close = "</script>\n"
|
||||
|
||||
local data_script = "<script>\nvar encrypted={{__HOLD_1__}}; var iv_data="..iv.."; var tag_data="..tag.."; var key="..key..";var d="..debug_data..";function u8ToBytes(u8){var s=\"\";for(var i=0;i<u8.length;i++){s+=String.fromCharCode(u8[i]);}return s;}\n"..
|
||||
local use_raw = ngx.var.obf_kdf_raw == "true"
|
||||
local data_script
|
||||
if use_raw then
|
||||
data_script = "<script>\nvar encrypted={{__HOLD_1__}}; var iv_data="..iv.."; var tag_data="..tag.."; var key="..key..";var d="..debug_data..";function u8ToBytes(u8){var s=\"\";for(var i=0;i<u8.length;i++){s+=String.fromCharCode(u8[i]);}return s;}\n"..
|
||||
"window.onload = function(){\n"..
|
||||
" var startTime = Date.now();\n\n"..
|
||||
" var decipher = forge.cipher.createDecipher(\"AES-GCM\", u8ToBytes(key));\n"..
|
||||
" decipher.start({iv: u8ToBytes(iv_data), tag: forge.util.createBuffer(u8ToBytes(tag_data))});\n"..
|
||||
" decipher.update(forge.util.createBuffer(u8ToBytes(encrypted)));\n"..
|
||||
" var ok = decipher.finish();\n\n"..
|
||||
" if (ok) {\n"..
|
||||
" var newDoc = new DOMParser().parseFromString(decipher.output, \"text/html\");\n\n"..
|
||||
" if (d){\n"..
|
||||
" console.log(newDoc);\n"..
|
||||
" console.log(decipher.output);\n"..
|
||||
" }\n"..
|
||||
" document.head.innerHTML = newDoc.head.innerHTML;\n"..
|
||||
" document.open();\n"..
|
||||
" document.write(decipher.output);\n"..
|
||||
" document.close();\n"..
|
||||
" }\n"..
|
||||
" var endTime = Date.now();\n"..
|
||||
" if (d){\n"..
|
||||
" console.log(\"dec cos(ms):\",endTime - startTime);\n"..
|
||||
" }\n"..
|
||||
"}\n</script>\n"
|
||||
else
|
||||
data_script = "<script>\nvar encrypted={{__HOLD_1__}}; var iv_data="..iv.."; var tag_data="..tag.."; var key="..key..";var d="..debug_data..";function u8ToBytes(u8){var s=\"\";for(var i=0;i<u8.length;i++){s+=String.fromCharCode(u8[i]);}return s;}\n"..
|
||||
"function evpBytesToKey(pass, keyLen, ivLen){\n"..
|
||||
" var m=[]; var i=0; var md=forge.md.md5.create();\n"..
|
||||
" function concatLen(arr){var n=0; for(var j=0;j<arr.length;j++){n+=arr[j].length;} return n;}\n"..
|
||||
@@ -60,6 +87,7 @@ function _M.content(data, iv, tag, key, debug_data)
|
||||
" console.log(\"dec cos(ms):\",endTime - startTime);\n"..
|
||||
" }\n"..
|
||||
"}\n</script>\n"
|
||||
end
|
||||
|
||||
|
||||
if not (ngx.var.obf_rand_var == "false") then
|
||||
|
||||
Reference in New Issue
Block a user