diff --git a/README.md b/README.md index 87c0da9..e2797d7 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,12 @@ - **瀑布流布局** — 首页文章以瀑布流展示,支持无限滚动加载 - **阅读统计** — 文章阅读量统计,带机器人流量检测 - **附件上传** — 文章支持上传附件,基于内容寻址自动去重 -- **RSS 订阅** — 自动生成 RSS Feed(`/rss`、`/feed`) +- **RSS 订阅** — 自动生成 RSS Feed(`/rss`、`/feed`),链接使用站点设置的规范地址 - **搜索功能** — 文章全文搜索 - **多语言** — 支持中文 / English,自动检测浏览器语言或手动切换 - **自适应界面** — Tailwind CSS,桌面端和移动端均可正常使用 -- **开箱即用** — 首次运行自动创建配置文件、数据库和管理员账号 +- **安全加固** — 会话密钥加密随机、Cookie HttpOnly/SameSite=Lax/HTTPS Secure、全站 CSRF、SQL 注入防护(参数化+路由 ID 数值化)、`/uploads` 白名单挂载(SQLite 数据库不可下载)、登录限速(IP+用户名,5 次失败锁 15 分钟)、附件越权校验、上传安全(危险扩展黑名单+magic-bytes 检测+头像 JPEG 重编码)、Gravatar 默认关闭、禁用用户会话实时失效,完整清单见 [SECURITY_TODO.md](./SECURITY_TODO.md) +- **开箱即用** — 首次运行自动创建配置文件、数据库;管理员账号密码为随机生成并仅一次性打印(不再使用 admin/admin) ## 技术栈 @@ -31,6 +32,7 @@ | Session | [gin-contrib/sessions](https://github.com/gin-contrib/sessions) | | 配置 | [gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) | | 密码 | [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto/bcrypt) | +| 文件校验 | [gabriel-vasile/mimetype](https://github.com/gabriel-vasile/mimetype) | | CSS | [Tailwind CSS](https://tailwindcss.com)(构建期静态生成,见 `scripts/build_tailwind.sh`) | ## 快速开始 @@ -43,6 +45,8 @@ go run . > 前端资源全部本地化(`static/css/app.css` 为 Tailwind 静态构建产物,已提交)。修改 HTML 模板/Go 代码中的 Tailwind 类后,运行 `./scripts/build_tailwind.sh`(需 Node ≥ 18,npx 可用)重新生成并提交新产物;vendor 库更新同理重新下载到 `static/vendor/` 并提交。 +> 安全状态一览见下方 [安全加固](#安全加固) 章节,完整修复清单与验证记录见 [SECURITY_TODO.md](./SECURITY_TODO.md)。 + ## 配置 首次运行时,配置文件会自动生成: @@ -57,9 +61,14 @@ go run . database: type: sqlite # sqlite(默认)或 mysql dsn: "" # MySQL 连接串,sqlite 模式下忽略 -port: "8080" # Web 服务端口 +web: + port: "8080" # Web 服务端口,"" 或 "0" 可只启用 socket + socket: "" # unix socket 路径(Linux 部署推荐,见 install_linux.sh) + trusted_proxies: # 可信反向代理 IP/CIDR;直接影响 X-Forwarded-For + - 127.0.0.1 # 仅列表内的代理可设置客户端 IP(防 XFF 伪造) + - ::1 path: ./win/srv/blog_go # 数据存储路径(数据库、上传文件) -secret: <自动生成> # Session 加密密钥 +secret: <自动生成> # Session 加密密钥;缺失时拒绝启动 ``` ### 使用 MySQL @@ -70,7 +79,8 @@ secret: <自动生成> # Session 加密密钥 database: type: mysql dsn: user:password@tcp(127.0.0.1:3306)/blog_go?charset=utf8mb4&parseTime=True&loc=Local -port: "8080" +web: + port: "8080" ``` 先创建数据库: @@ -103,12 +113,16 @@ go_blog/ │ ├── tag.go # 标签模型 │ └── upload_config.go # 上传配置 ├── middleware/ -│ └── auth.go # 登录验证、角色鉴权、语言检测 +│ ├── auth.go # 登录验证、角色鉴权、语言检测 +│ ├── csrf.go # CSRF 同步器令牌防护 +│ ├── https.go # 请求是否 HTTPS 检测(cookie Secure) +│ └── security_headers.go # 安全响应头(CSP、HSTS、nosniff 等) ├── handlers/ │ ├── helpers.go # 公共工具函数 │ ├── home.go # 首页 │ ├── article.go # 文章详情、文章列表 API │ ├── auth.go # 登录 +│ ├── login_ratelimit.go # 登录限速(IP+用户名,5 次失败锁定 15 分钟) │ ├── admin.go # 管理后台首页 │ ├── admin_comment.go # 评论管理 │ ├── admin_user.go # 用户管理 @@ -119,7 +133,7 @@ go_blog/ │ ├── profile.go # 个人中心 │ ├── rss.go # RSS Feed │ ├── settings.go # 站点/导航/上传/评论设置 -│ └── upload_validator.go # 上传文件校验 +│ └── upload_validator.go # 上传文件校验(扩展名白名单 + magic-bytes) ├── i18n/ │ └── i18n.go # 中英文翻译映射 + Accept-Language 检测 ├── static/ @@ -161,9 +175,34 @@ go_blog/ │ └── config.yaml └── srv/blog_go/ ├── blog.db - └── avatars/ + ├── attachments/ + ├── avatars/ + └── logos/ ``` +## 安全加固 + +基于 2026-08-19 安全审计与网络上线验证(共 25 项发现,P0–P3 均已修复,详见 [SECURITY_TODO.md](./SECURITY_TODO.md)): + +- **认证与会话** + - 会话密钥由 `crypto/rand` 生成 32 字节随机数;配置文件缺失 secret 时拒绝启动(不再静默回退) + - Cookie 加固:`HttpOnly` + `SameSite=Lax`,HTTPS 下自动加 `Secure` + - 全站 CSRF 防护(同步器令牌,30+ 表单与 AJAX 全覆盖);登录/注册成功强制会话轮换(防会话固定) + - 登录限速:按 IP+用户名 5 次失败锁定 15 分钟;用户不存在时也执行 bcrypt 比较,抹平计时侧信道 + - 禁用/锁定/软删用户的会话实时失效;管理员口令 bcrypt cost 12 +- **数据与注入** + - GORM 全参数化查询;管理路由的 `:id` 先解析为数值再入查询(防字符串条件注入) + - `/uploads` 仅白名单挂载子目录,SQLite 数据库文件不可从公网下载;目录列表与路径穿越一律 404 +- **上传安全** + - 扩展名白名单 + 危险扩展名黑名单(.html/.svg/.js 等,防同源 Active Content) + - 内容 magic-bytes 与声明类型一致性校验(gabriel-vasile/mimetype) + - 头像强制解码→256px 缩放→JPEG 重编码后落盘,原始字节一律不落地 +- **输出与传输** + - CSP(`default-src 'self'`,第三方前端资源已本地化)、`X-Frame-Options: DENY`、`X-Content-Type-Options: nosniff`、`Referrer-Policy`、`Permissions-Policy`;HTTPS 下发 HSTS + - 客户端 IP 解析仅信任 `web.trusted_proxies` 名单内代理(防 X-Forwarded-For 伪造);RSS 链接使用站点设置的规范地址(防 Host 头污染) + +安全回归用例 50+(`handlers/*_test.go`、`main_test.go`、`middleware/*_test.go`),`go test -race ./...` 全绿。 + ## 路由 ### 公开路由