seguinleo/WebSecurityCheatSheet
GitHub: seguinleo/WebSecurityCheatSheet
面向全栈开发者的 Web 安全加固速查手册,提供从前端到后端、从服务器到容器的全链路安全配置与编码实践。
Stars: 2 | Forks: 0
# Web 安全备忘录
## 📖 目录
- [**HTTPS (例如 Apache2)**](#https-ie-apache2)
- [**Let's Encrypt**](#lets-encrypt)
- [**将所有 HTTP 流量重定向到 HTTPS**](#redirect-all-http-traffic-to-https)
- [**传输层安全性 (TLS)**](#transport-layer-security-tls)
- [**HTTP 严格传输安全 (HSTS)**](#http-strict-transport-security-hsts)
- [**Apache2**](#apache2)
- [**启用 HTTP2**](#enable-http2)
- [**启用 mod\_security**](#enable-mod_security)
- [**启用 Gzip**](#enable-gzip-or-brotli)
- [**隐藏服务器签名**](#hide-server-signature)
- [**限制文件访问**](#restrict-access-to-files)
- [**Nginx**](#nginx-recommended)
- [**HTTP Headers**](#http-headers)
- [**授权**](#authorization)
- [**数据库**](#database)
- [**PHP**](#php)
- [**PHP-FPM**](#php-fpm)
- [**PHP PDO**](#php-pdo)
- [**php.ini**](#phpini)
- [**Node.js/npm**](#nodejsnpm)
- [**Express**](#express)
- [**服务器**](#server)
- [**Express session**](#express-session)
- [**用户注册**](#user-registration)
- [**用户登录**](#user-login)
- [**CSRF Token**](#csrf-token)
- [**Cookies**](#cookies)
- [**文件上传**](#file-upload)
- [**Docker**](#docker)
- [**Ubuntu VPS**](#ubuntu-vps)
- [**HTML DOM 消毒**](#html-dom-sanitization)
- [**Zod**](#zod)
- [**链接**](#links)
- [**POST vs GET**](#post-vs-get)
- [**e.innerHTML**](#einnerhtml)
- [**eval() 和 new Function()**](#eval-and-new-function)
- [**DOMPurify**](#dompurify)
- [**分析工具**](#analysis-tools)
- [**来源和资源**](#sources-and-resources)
**_本文档是一份简明指南,旨在列出主要的 Web 漏洞(特别是 JavaScript 相关的漏洞)以及一些解决方案。本指南应结合高质量、最新的文档作为补充。_**
**_本指南面向使用 JavaScript 技术(React、Vue 等)以及带有 CRUD/REST API 的 Node.js/PHP 后端的全栈开发人员。_**
**_因此,本指南不包含 .NET、JAVA、Django 或 Ruby。_**
## **HTTPS (例如 Apache2)**
### **Let's Encrypt**
使用 Let's Encrypt 安装证书:
```
sudo apt install certbot python3-certbot-apache
```
```
sudo certbot certonly --standalone -d example.com -d www.example.com
```
向您的 DNS 区域添加 CAA 记录:
```
CAA 0 issue "letsencrypt.org"
```
### **将所有 HTTP 流量重定向到 HTTPS**
在 _/etc/apache2/apache2.conf_ 中写入:
```
Redirect permanent /
```
### **传输层安全性 (TLS)**
通用 Web 应用程序应默认使用 TLS 1.3(如有必要则支持 TLS 1.2),并禁用所有其他协议。仅启用 TLS 1.2 和 1.3。转到 _/etc/apache2/conf-available/ssl.conf_ 并写入:
```
SSLProtocol TLSv1.2 TLSv1.3
```
### **HTTP 严格传输安全 (HSTS)**
HTTP 严格传输安全 (HSTS) 是一种网站机制,用于指示 Web 浏览器该站点只能通过 HTTPS 访问。该机制通过站点发送包含其策略的 Strict-Transport-Security HTTP 响应头来实现。在 _/etc/apache2/apache2.conf_ 中写入:
```
Header set strict-transport-security "max-age=31536000; includesubdomains; preload"
```
重新加载 Apache 并将您的网站提交至
## **Apache2**
### **启用 HTTP2**
HTTP/2 为 HTTP/1.1 创建者未曾预料的几个问题提供了解决方案。具体而言,HTTP/2 比 HTTP/1.1 快得多且效率更高:
``sudo a2enmod http2``
### **启用 mod_security**
Mod security 是一款免费的 Web 应用程序防火墙 (WAF),可与 Apache2 或 nginx 配合使用:
``sudo apt install libapache2-modsecurity``
```
SecRuleEngine On <- /etc/modsecurity/modsecurity.conf
```
### **启用 Gzip (或 Brotli)**
``a2enmod deflate``
### **隐藏服务器签名**
暴露带有服务器/PHP 版本信息的 Web 服务器签名可能是一个安全风险,因为这实质上是在告诉攻击者您系统已知的漏洞。在 _/etc/apache2/apache2.conf_ 中写入:
```
ServerTokens Prod
ServerSignature Off
```
### **限制文件访问**
在 _/etc/apache2/apache2.conf_ 中写入:
```
Options FollowSymLinks
AllowOverride None
Require all denied
Options -Indexes
AllowOverride None
Require all granted
```
## **Nginx (推荐)**
遵循上述 Apache2 配置的 nginx 模板:
```
server {
listen 80;
server_name your-website.com www.your-website.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name your-website.com www.your-website.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /usr/share/nginx/html;
index index.html;
server_tokens off;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
location / {
try_files $uri $uri/ /index.html;
limit_except GET POST {
deny all;
}
}
}
```
## **HTTP Headers**
Apache2 和 nginx 的推荐 headers:
```
x-content-type-options: "nosniff"
access-control-allow-origin "https://domain.com"
referrer-policy "no-referrer"
content-security-policy "upgrade-insecure-requests; default-src 'self'; base-uri 'none'; connect-src 'self'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self'; media-src 'self'; object-src 'none' ; script-src 'self'; script-src-attr 'none'; style-src 'self'"
permissions-policy "geolocation=(), …"
cross-origin-embedder-policy: "require-corp"
cross-origin-opener-policy "same-origin"
cross-origin-resource-policy "cross-origin"
```
此外,请确保移除 _Server_ 和 _X-Powered-By_ headers。
## **授权**
- 默认拒绝
- 实施最小权限原则
- 验证所有权限
- 验证文件访问
- 对上传的文件进行过滤消毒
- 敏感操作需要用户密码
## **数据库**
- 使用强数据库密码并限制用户权限
- 在将所有用户登录密码存入数据库之前进行哈希处理
- 对于 MySQL/MariaDB 数据库,使用预处理查询来防止注入
```
# php
$query = $PDO->prepare("SELECT a FROM b WHERE c=:c LIMIT 1");
$query->execute([':c' => $c]);
$row = $query->fetch();
```
```
// js
try {
const [rows] = await pool.execute(
"SELECT a FROM b WHERE c = ? LIMIT 1",
[userId]
)
if (rows.length !== 1) return 0
return rows[0].a
} catch {
return 0
}
```
- 对于 MySQL/MariaDB 数据库,使用 _mysql_secure_installation_
- 对于 NoSQL 数据库,如 MongoDB,使用类型化模型来防止某些注入
- 在 MongoDB 中避免使用 _$accumulator_、_$function_、_$where_
- 将密钥存储在安全的保管库中,例如 AWS Secrets Manager、Azure KeyVault 或 Hashicorp
## **PHP**
### **PHP-FPM**
PHP-FPM (FastCGI Process Manager) 由于其卓越的性能、进程隔离和灵活的配置,通常比 Apache mod_php 更受青睐:
```
sudo apt install php-fpm
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event proxy_fcgi proxy
```
### **PHP PDO**
PDO (PHP Data Objects) 是一个数据库访问抽象层,为访问各种数据库提供了统一的接口。使用 PDO 的安全 MySQL 数据库连接:
```
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$dsn = "mysql:host=$host;dbname=$db";
try {
$PDO = new PDO($dsn, $user, $pass, $options);
} catch (Exception $e) {
throw new Exception('Connection failed');
return;
}
```
### **php.ini**
适用于 PHP-FPM 的加固模板,写入 _/etc/php/<version>/fpm/php.ini_:
```
expose_php = off
error_reporting = e_all & ~e_deprecated & ~e_strict
display_errors = off
display_startup_errors = off
ignore_repeated_errors = off
allow_url_fopen = off
allow_url_include = off
session.use_strict_mode = 1
session.use_only_cookies = 1
session.cookie_secure = 1
session.cookie_httponly = 1
session.cookie_samesite = strict
session.sid_length = > 128
```
## **Node.js/npm**
- 始终保持所有 npm 依赖项处于最新状态,使用 _npm audit_
- 限制依赖项的使用
- 使用 _npm doctor_ 确保您的 npm 安装具备管理 JavaScript 包所需的一切
- 使用 eslint 编写高质量代码
## **Express**
### **服务器**
[推荐] 保护 Express 服务器(位于反向代理之后):
```
import express from 'express'
import helmet from 'helmet'
const app = express()
// If behind a reverse proxy like Nginx
app.set('trust proxy', 1)
// If requests are application/json
app.use(express.json({ limit: '50kb' }))
// Security headers
app.use(helmet())
app.disable('x-powered-by')
const PORT = process.env.PORT || 3000
app.listen(
PORT,
'127.0.0.1', // bind only to localhost if possible
() => {
console.log(`Server is running on port ${PORT}`)
})
```
或者,使用 HTTPS 保护 Express 服务器(无反向代理):
```
import express from 'express'
import helmet from 'helmet'
import https from 'https'
import fs from 'fs'
const app = express()
app.use(express.json({ limit: '50kb' }))
app.use(helmet())
app.disable('x-powered-by')
const PORT = process.env.PORT || 3000
https.createServer({
key: fs.readFileSync(process.env.SSL_KEY),
cert: fs.readFileSync(process.env.SSL_CERT),
}, app).listen(PORT, () => {
console.log(`HTTPS server running on port ${PORT}`)
})
```
如果您需要 CORS 请求:
```
import cors from 'cors'
const corsOptions = {
origin: process.env.YOUR_URL,
credentials: true,
}
app.use(cors(corsOptions))
```
使用 _express-rate-limit_ 保护所有路由:
```
const router = express.Router()
const limiter = rateLimit({
windowMs: 5 * 60 * 1000,
max: 100,
message: 'Too many requests, please try again later.',
})
router.use(limiter)
```
### **Express session**
如果您正在使用 _express-session_,请对其进行安全配置并存储在 _Redis_ 中:
```
import express from 'express'
import session from 'express-session'
import { RedisStore } from 'connect-redis'
import { createClient } from 'redis'
const app = express()
const PORT = process.env.PORT || 3000
const redisClient = createClient({
url: process.env.REDIS_URL,
})
try {
await redisClient.connect()
console.log('Redis client connected')
} catch (err) {
console.error(err)
process.exit(1)
}
const redisStore = new RedisStore({
client: redisClient,
prefix: 'your_app:',
ttl: 3600,
})
app.use(
session({
store: redisStore,
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: true,
sameSite: 'Strict',
},
})
)
```
## **用户注册**
使用 _bcrypt_ 和 _zod_ 的安全 Express 用户注册:
```
const createAccountSchema = z.object({
nameCreate: z
.string()
.min(3)
.max(30)
.regex(/^[\p{L} -]+$/u),
psswdCreate: z.string().min(10).max(64)
})
router.post('/create-account', async (req, res) => {
const parsed = createAccountSchema.safeParse(req.body)
if (!parsed.success) {
return res.status(400).send('Account creation failed')
}
const { nameCreate, psswdCreate } = parsed.data
const id = crypto.randomBytes(12).toString('hex')
const psswdCreateHash = await bcrypt.hash(psswdCreate, 12)
try {
await pool.execute(
"INSERT INTO users (id, name, psswd) VALUES (?, ?, ?)",
[id, nameCreate, psswdCreateHash]
)
return res.status(200).send('Account created successfully')
} catch {
return res.status(400).send('Account creation failed')
}
})
```
## **用户登录**
使用速率限制和 _passport.js_ 的安全 Express 登录系统:
```
import rateLimit from 'express-rate-limit'
const loginLimiter = rateLimit({
windowMs: 3 * 60 * 1000,
max: 5,
message: 'Too many login attempts from this IP, please try again later'
})
app.post('/login', loginLimiter, async (req, res, next) => {
try {
const user = await new Promise((resolve, reject) => {
passport.authenticate('local', { session: false }, (err, user) => {
if (err) return reject(err)
resolve(user)
})(req, res, next)
})
if (!user) return res.status(401).send('Wrong username or password.')
req.session.regenerate((err) => {
if (err) return res.status(401).send('Wrong username or password.')
// create session here
})
return res.status(200).send('Logged in!')
} catch {
return res.status(401).send('Wrong username or password.')
}
})
```
**对于敏感应用,请务必使用 MFA (2FA) !**
## **CSRF Token**
SameSite cookie 作为纵深防御手段是很好的,但并不能阻止所有可能的 CSRF 攻击。请使用 CSRF Token 或双重提交 Cookie。[阅读 OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html)
我为 Express 推荐 [csrf-csrf](https://github.com/Psifi-Solutions/csrf-csrf)。
## **Cookies**
``Domain=domain.com; Path=/; Secure; HttpOnly; SameSite=Lax or Strict``
**Secure:** 所有 cookie 都必须使用 _Secure_ 指令设置,表示它们只能通过 HTTPS 发送
**HttpOnly:** 不需要通过 JavaScript 访问的 Cookie 应设置 _HttpOnly_ 指令以阻止访问
**Domain:** 只有当 cookie 需要在其他域上访问时才应设置 _Domain_;应将其设置为限制性最强的域
**Path:** Cookie 应设置为限制性最强的 _Path_
**SameSite:**
- **Strict (首选):** 仅在同站上下文中发送 cookie。在跨站请求和跨站导航中省略 cookie
- **Lax:** 在同站请求和导航到您的网站时发送 cookie。如果 _Strict_ 限制性太强,请使用此值
## **文件上传**
在保存前务必检查扩展名、实际的 MIME 类型以及大小:
```
import multer from 'multer'
import path from 'path'
import fs from 'fs'
import { fileTypeFromBuffer } from 'file-type'
const uploadDir = '/var/uploads'
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
const ALLOWED_TYPES = [
{
mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
ext: 'xlsx'
}
]
const storage = multer.memoryStorage()
export const upload = multer({
storage,
limits: { fileSize: 512000 }
})
export const handleUpload = async (req, res, next) => {
try {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded' })
}
const buffer = req.file.buffer
const fileType = await fileTypeFromBuffer(buffer)
if (!fileType) {
return res.status(400).json({ error: 'Unknown file type' })
}
const allowed = ALLOWED_TYPES.find(
t => t.mime === fileType.mime && t.ext === fileType.ext
)
if (!allowed) {
return res.status(400).json({ error: 'Invalid file type' })
}
const originalName = req.file.originalname
const baseName = path.parse(originalName).name
const safeName = baseName.replace(/[^a-z0-9_\-]/gi, '_')
const finalName = `${safeName}.${fileType.ext}`
const finalPath = path.join(uploadDir, finalName)
await fs.promises.writeFile(finalPath, buffer)
next()
} catch (err) {
next(err)
}
}
```
## **Docker**
- 使用官方且最小化的镜像
- 使用 _.dockerignore_ 隐藏服务器机密
- 使用 _--read-only_ 标志以只读文件系统运行容器
- 避免使用 _ADD_,优先使用 _COPY_
- 在 _DockerFile_ 中设置一个权限受限的用户
```
RUN groupadd -r myuser && useradd -r -g myuser myuser
# 在此以 ROOT 用户执行你需要执行的操作,例如安装软件包等。
USER myuser
```
## **Ubuntu VPS**
- 为所有用户使用强密码
- 禁用 root 登录
- 创建一个权限受限并启用 2FA 或物理密钥的用户
- 始终更新所有包并限制其数量
- 禁用未使用的网络端口
- 使用 Fail2Ban 防止 DoS 和 Bruteforce 攻击,并在 _sshd_config_ 中禁用 SSH root 登录
```
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no
```
- 始终进行安全且定期的备份
- 记录一切
- 使用 SFTP 代替 FTP
- 使用防火墙,例如 iptables 或 ufw
- 使用 _robots.txt_ 默认禁止所有内容,并且不要泄露敏感 URL
## **HTML DOM 消毒**
### **Zod**
Zod 是一款用于过滤输入的神器:
```
import * as z from 'zod'
const User = z.object({
name: z.string().min(3).max(64),
})
```
### **链接**
始终使用 _rel="noreferrer noopener"_ 以防止将 referrer header 发送到新页面。
### **POST vs GET**
永远不要信任用户输入,验证并过滤所有数据。对幂等请求使用 GET,对状态更改使用 POST。使用强大的正则表达式对数据进行过滤和验证,并使用 _application/json_ 代替 _application/x-www-form-urlencoded_:
```
try {
if (!yourData ||
// regex, sanitize, etc.
) return
const data = JSON.stringify({ yourData })
const res = await fetch('api/fetch/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: data,
})
if (!res.ok) {
//
return
}
//
} catch {
//
}
```
### **e.innerHTML**
切勿在没有 DOMPurify 的情况下使用 _innerHTML_;请改用 _innerText_ 或 _textContent_。您也可以使用 _document.createElement()_ 创建您的元素。
### **eval() 和 new Function()**
切勿使用这些 JavaScript 函数。从字符串执行 JavaScript 存在巨大的安全风险。当您使用 _eval()_ 时,恶意行为者很容易运行任意代码。
### **DOMPurify**
DOMPurify 对 HTML 进行过滤并防止 XSS 攻击。您可以将包含恶意 HTML 的字符串提供给 DOMPurify,它将返回一个包含干净 HTML 的字符串(除非另行配置)。DOMPurify 将剔除所有包含危险 HTML 的内容,从而防止 XSS 攻击和其他恶意行为。
```
import DOMPurify from 'dompurify'
const PURIFY_CONFIG = {
SANITIZE_NAMED_PROPS: true,
ALLOW_DATA_ATTR: false,
ALLOWED_URI_REGEXP: /^(https?|mailto|tel):/i
}
const clean = DOMPurify.sanitize(dirty, PURIFY_CONFIG)
```
## **分析工具**
[**Mozilla Observatory**](https://developer.mozilla.org/en-US/observatory)
[**SSLLabs**](https://www.ssllabs.com/ssltest/)
[**Cryptcheck**](https://cryptcheck.fr/)
[**W3C Validator**](https://validator.w3.org/)
## **来源和资源**
标签:API密钥检测, AppImage, CheatSheet, CISA项目, CMS安全, CRUD安全, CSRF防护, Docker安全, DOM净化, Express, GNU通用公共许可证, HTTPS, JavaScript, MITM代理, Node.js, OpenVAS, PHP, React, REST API安全, SQL注入防御, Syscall, Syscalls, TLS, Vue, Web安全, Web应用防火墙, Web开发, XSS防御, 前端安全, 后端安全, 安全响应头, 安全指南, 安全最佳实践, 安全配置, 搜索引擎查询, 文件系统扫描, 蓝队分析, 请求拦截, 输入验证, 防御工具