mirror of
https://github.com/Rayzggz/server_torii.git
synced 2025-06-17 04:31:22 +08:00
feat: CAPTCHA session ID
This commit is contained in:
@ -65,6 +65,10 @@
|
|||||||
alert("Bad CAPTCHA, please refresh the page and try again.\n"
|
alert("Bad CAPTCHA, please refresh the page and try again.\n"
|
||||||
+ "您未能通过人机验证,请刷新页面后重试。");
|
+ "您未能通过人机验证,请刷新页面后重试。");
|
||||||
break;
|
break;
|
||||||
|
case "timeout":
|
||||||
|
alert("Verification timeout, please refresh the page and try again.\n"
|
||||||
|
+ "验证超时,请刷新页面后重试。");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
alert("Unexpected error occurred, please refresh the page and try again.\n"
|
alert("Unexpected error occurred, please refresh the page and try again.\n"
|
||||||
+ "发生了意料之外的错误,请刷新页面后重试。");
|
+ "发生了意料之外的错误,请刷新页面后重试。");
|
||||||
@ -72,12 +76,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkCaptchaRender() {
|
||||||
|
const captchaDiv = document.querySelector(".h-captcha");
|
||||||
|
|
||||||
|
if (captchaDiv && captchaDiv.children.length > 0) {
|
||||||
|
} else {
|
||||||
|
document.getElementById("verifyBox").innerHTML = "Loading CAPTCHA failed, please check your internet connection and try again.<br>"
|
||||||
|
+ "加载人机验证失败,请检查尝试更换网络环境后重试。";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(checkCaptchaRender, 5000); // 5秒后检查
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="message">Checking that you are not a robot</div>
|
<div class="message">Checking that you are not a robot</div>
|
||||||
<div class="verifyBox"><div class="h-captcha" data-sitekey="" data-callback="onSubmit"></div></div>
|
<div class="message">请完成人机验证</div>
|
||||||
|
<div class="verifyBox" id="verifyBox"><div class="h-captcha" data-sitekey="" data-callback="onSubmit"></div></div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
DDoS Protection Powered by <a href="https://github.com/Rayzggz/server_torii">⛩️Server Torii</a>
|
DDoS Protection Powered by <a href="https://github.com/Rayzggz/server_torii">⛩️Server Torii</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
secret_key: "0378b0f84c4310279918d71a5647ba5d"
|
secret_key: "0378b0f84c4310279918d71a5647ba5d"
|
||||||
captcha_validate_time: 60
|
captcha_validate_time: 600
|
||||||
|
captcha_challenge_timeout: 120
|
||||||
hcaptcha_secret: ""
|
hcaptcha_secret: ""
|
@ -1,8 +1,8 @@
|
|||||||
port: "25555"
|
port: "25555"
|
||||||
web_path: "/torii"
|
web_path: "/torii"
|
||||||
rule_path: "/www/dev/server_torii/config/rules"
|
rule_path: "/www/server_torii/config/rules"
|
||||||
error_page: "/www/dev/server_torii/config/error_page"
|
error_page: "/www/server_torii/config/error_page"
|
||||||
log_path: "/www/dev/server_torii/log/access.log"
|
log_path: "/www/server_torii/log/access.log"
|
||||||
node_name: "Server Torii"
|
node_name: "Server Torii"
|
||||||
connecting_host_headers:
|
connecting_host_headers:
|
||||||
- "Torii-Real-Host"
|
- "Torii-Real-Host"
|
@ -31,7 +31,7 @@ func Captcha(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *ac
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !verifyClearanceCookie(reqData, *ruleSet) {
|
if !verifyClearanceCookie(reqData, *ruleSet) {
|
||||||
decision.SetCode(action.Done, []byte("CAPTCHA"))
|
decision.SetResponse(action.Done, []byte("CAPTCHA"), genSessionID(reqData, *ruleSet))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +51,11 @@ func CheckCaptcha(r *http.Request, reqData dataType.UserRequest, ruleSet *config
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !verifySessionCookie(reqData, *ruleSet) {
|
||||||
|
decision.SetResponse(action.Done, []byte("200"), []byte("timeout"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
data := url.Values{}
|
||||||
data.Set("secret", ruleSet.CAPTCHARule.HCaptchaSecret)
|
data.Set("secret", ruleSet.CAPTCHARule.HCaptchaSecret)
|
||||||
data.Set("response", hCaptchaResponse)
|
data.Set("response", hCaptchaResponse)
|
||||||
@ -84,7 +89,7 @@ func CheckCaptcha(r *http.Request, reqData dataType.UserRequest, ruleSet *config
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !hCaptchaResp.Success {
|
if !hCaptchaResp.Success {
|
||||||
decision.SetResponse(action.Done, []byte("200"), []byte("bad4"))
|
decision.SetResponse(action.Done, []byte("200"), []byte("bad"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,10 +98,46 @@ func CheckCaptcha(r *http.Request, reqData dataType.UserRequest, ruleSet *config
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func genSessionID(reqData dataType.UserRequest, ruleSet config.RuleSet) []byte {
|
||||||
|
timeNow := time.Now().Unix()
|
||||||
|
mac := hmac.New(sha512.New, []byte(ruleSet.CAPTCHARule.SecretKey))
|
||||||
|
mac.Write([]byte(fmt.Sprintf("%d%s%sCAPTCHA-SESSION-ID", timeNow, reqData.Host, reqData.UserAgent)))
|
||||||
|
return []byte(fmt.Sprintf("%s:%s", fmt.Sprintf("%d", time.Now().Unix()), fmt.Sprintf("%x", mac.Sum(nil))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifySessionCookie(reqData dataType.UserRequest, ruleSet config.RuleSet) bool {
|
||||||
|
if reqData.ToriiSessionID == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
parts := strings.Split(reqData.ToriiSessionID, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
timestamp := parts[0]
|
||||||
|
expectedHash := parts[1]
|
||||||
|
|
||||||
|
timeNow := time.Now().Unix()
|
||||||
|
parsedTimestamp, err := strconv.ParseInt(timestamp, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error parsing timestamp: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeNow-parsedTimestamp > ruleSet.CAPTCHARule.CaptchaChallengeTimeout {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
mac := hmac.New(sha512.New, []byte(ruleSet.CAPTCHARule.SecretKey))
|
||||||
|
mac.Write([]byte(fmt.Sprintf("%d%s%sCAPTCHA-SESSION-ID", parsedTimestamp, reqData.Host, reqData.UserAgent)))
|
||||||
|
computedHash := fmt.Sprintf("%x", mac.Sum(nil))
|
||||||
|
|
||||||
|
return hmac.Equal([]byte(computedHash), []byte(expectedHash))
|
||||||
|
}
|
||||||
|
|
||||||
func GenClearance(reqData dataType.UserRequest, ruleSet config.RuleSet) []byte {
|
func GenClearance(reqData dataType.UserRequest, ruleSet config.RuleSet) []byte {
|
||||||
timeNow := time.Now().Unix()
|
timeNow := time.Now().Unix()
|
||||||
mac := hmac.New(sha512.New, []byte(ruleSet.CAPTCHARule.SecretKey))
|
mac := hmac.New(sha512.New, []byte(ruleSet.CAPTCHARule.SecretKey))
|
||||||
mac.Write([]byte(fmt.Sprintf("%d%s%s", timeNow, reqData.Host, reqData.UserAgent)))
|
mac.Write([]byte(fmt.Sprintf("%d%s%sCAPTCHA-CLEARANCE", timeNow, reqData.Host, reqData.UserAgent)))
|
||||||
return []byte(fmt.Sprintf("%s:%s", fmt.Sprintf("%d", time.Now().Unix()), fmt.Sprintf("%x", mac.Sum(nil))))
|
return []byte(fmt.Sprintf("%s:%s", fmt.Sprintf("%d", time.Now().Unix()), fmt.Sprintf("%x", mac.Sum(nil))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +164,7 @@ func verifyClearanceCookie(reqData dataType.UserRequest, ruleSet config.RuleSet)
|
|||||||
}
|
}
|
||||||
|
|
||||||
mac := hmac.New(sha512.New, []byte(ruleSet.CAPTCHARule.SecretKey))
|
mac := hmac.New(sha512.New, []byte(ruleSet.CAPTCHARule.SecretKey))
|
||||||
mac.Write([]byte(fmt.Sprintf("%d%s%s", parsedTimestamp, reqData.Host, reqData.UserAgent)))
|
mac.Write([]byte(fmt.Sprintf("%d%s%sCAPTCHA-CLEARANCE", parsedTimestamp, reqData.Host, reqData.UserAgent)))
|
||||||
computedHash := fmt.Sprintf("%x", mac.Sum(nil))
|
computedHash := fmt.Sprintf("%x", mac.Sum(nil))
|
||||||
|
|
||||||
return hmac.Equal([]byte(computedHash), []byte(expectedHash))
|
return hmac.Equal([]byte(computedHash), []byte(expectedHash))
|
||||||
|
@ -11,7 +11,8 @@ type UserRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CaptchaRule struct {
|
type CaptchaRule struct {
|
||||||
SecretKey string `yaml:"secret_key"`
|
SecretKey string `yaml:"secret_key"`
|
||||||
CaptchaValidateTime int64 `yaml:"captcha_validate_time"`
|
CaptchaValidateTime int64 `yaml:"captcha_validate_time"`
|
||||||
HCaptchaSecret string `yaml:"hcaptcha_secret"`
|
CaptchaChallengeTimeout int64 `yaml:"captcha_challenge_timeout"`
|
||||||
|
HCaptchaSecret string `yaml:"hcaptcha_secret"`
|
||||||
}
|
}
|
||||||
|
@ -70,8 +70,9 @@ func CheckMain(w http.ResponseWriter, userRequestData dataType.UserRequest, rule
|
|||||||
http.Error(w, "500 - Internal Server Error", http.StatusInternalServerError)
|
http.Error(w, "500 - Internal Server Error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusServiceUnavailable)
|
w.Header().Set("Set-Cookie", "__torii_session_id="+string(decision.ResponseData)+"; Path=/; Max-Age=86400; Priority=High; HttpOnly;")
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
if err = tpl.Execute(w, nil); err != nil {
|
if err = tpl.Execute(w, nil); err != nil {
|
||||||
log.Printf("Error template: %v", err)
|
log.Printf("Error template: %v", err)
|
||||||
http.Error(w, "500 - Internal Server Error", http.StatusInternalServerError)
|
http.Error(w, "500 - Internal Server Error", http.StatusInternalServerError)
|
||||||
|
@ -29,13 +29,21 @@ func CheckTorii(w http.ResponseWriter, r *http.Request, reqData dataType.UserReq
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else if bytes.Compare(decision.ResponseData, []byte("good")) == 0 {
|
} else if bytes.Compare(decision.ResponseData, []byte("good")) == 0 {
|
||||||
w.Header().Set("Set-Cookie", "__torii_clearance="+string(check.GenClearance(reqData, *ruleSet))+"; Path=/; HttpOnly")
|
w.Header().Set("Set-Cookie", "__torii_clearance="+string(check.GenClearance(reqData, *ruleSet))+"; Path=/; Max-Age=86400; Priority=High; HttpOnly;")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_, err := w.Write(decision.ResponseData)
|
_, err := w.Write(decision.ResponseData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error writing response: %v", err)
|
log.Printf("Error writing response: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else if bytes.Compare(decision.ResponseData, []byte("timeout")) == 0 {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err := w.Write([]byte("timeout"))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error writing response: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
//should not be here
|
//should not be here
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
Reference in New Issue
Block a user