# 事件回報

本頁說明如何以 `LoginFailure` 與 `NotFound404` 把應用層事件回報給評分引擎，以及這兩個呼叫的副作用。

## 兩個回報方法

| 方法 | 遞增的 key | 參與評分 |
|---|---|---|
| `LoginFailure(w, r)` | `login:failure:{sid}` | 超過 `LoginFailure`（4）次開始加 `ScoreLoginFailure` |
| `NotFound404(w, r)` | `notfound:404:{sid}` | 超過 `NotFound404`（8）次開始加 `ScoreNotFound404` |

兩個計數以 Session 為單位，第一次遞增時設定 1 小時過期，之後不再延長（固定視窗）。計分規則見 [關聯訊號](/zh/correlation-signals)。

## 登入失敗

```go
func loginHandler(sentry *golangIPSentry.IPGuardian) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !validCredential(r) {
			if err := sentry.LoginFailure(w, r); err != nil {
				log.Printf("report login failure: %v", err)
			}
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}
		w.Write([]byte("welcome"))
	}
}
```

## 404 掃描

```go
r := gin.New()
r.Use(gin.Recovery(), sentry.GinMiddleware())
r.NoRoute(func(c *gin.Context) {
	if err := sentry.NotFound404(c.Writer, c.Request); err != nil {
		log.Printf("report 404: %v", err)
	}
	c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
})
```

## 副作用

兩個方法內部都會重新執行裝置識別：

| 副作用 | 影響 |
|---|---|
| `frequency:{ip}:{minute}` 再加 1 | 同一個請求被計入兩次每分鐘請求數 |
| 重新寫入兩個 `Set-Cookie` | 必須在 handler 寫出 body 之前呼叫 |
| 沒有 Cookie 的請求 | 計數記在本次回應新簽發的 Session；用戶端不保存該 Cookie 就不會影響下一個請求 |

## 效果範圍

預設值下，7 次登入失敗只得 30 分，不會單獨造成拒絕；要讓暴力破解直接觸發限制，降低 `LoginFailure` 或提高 `ScoreLoginFailure`，見 [參數](/zh/parameters)。攻擊者丟棄 Cookie 即換到新 Session，此時需靠 `ip:device` 等 IP 層訊號。
