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防御, 前端安全, 后端安全, 安全响应头, 安全指南, 安全最佳实践, 安全配置, 搜索引擎查询, 文件系统扫描, 蓝队分析, 请求拦截, 输入验证, 防御工具