我40岁了, 还在写javascript
攻击清单(10 种)
- SQL 注入 -- 插入恶意 SQL 语句
- XSS -- 注入恶意脚本
- CSRF -- 伪造跨站请求
- 弱密码撞库 -- 破解或撞库
- 文件上传漏洞 -- 上传恶意文件
- 路径遍历 --
../访问敏感文件 - 中间人攻击 -- 拦截通信数据
- 点击劫持 -- 透明 iframe 诱导点击
- DDoS / 暴力破解 -- 海量请求耗尽资源
- WebSocket 劫持 -- 劫持实时通信
1. SQL 注入 -- 参数化查询
核心代码:
ts
const userInput = ['张三', '男', '19']
db.query('SELECT * FROM users WHERE name = ? AND sex = ? AND age = ?', userInput)
// 实际执行的 SQL:SELECT * FROM users WHERE name = '张三' AND sex = '男' AND age = '19'
? 是占位符,数组按顺序替换 ?。数据库驱动自动转义特殊字符(如 '),输入只作为数据,不改变 SQL 结构。
注入攻击示例:
攻击者在用户名输入框中输入:
text
' OR '1'='1
错误写法(字符串拼接):
ts
const sql = `SELECT * FROM users WHERE name = '${userName}'`
// 生成的 SQL:SELECT * FROM users WHERE name = '' OR '1'='1'
由于 '1'='1' 永远为真,返回所有用户,攻击成功。
正确写法(参数化查询):
ts
db.query('SELECT * FROM users WHERE name = ?', [userName])
// 实际执行的 SQL:SELECT * FROM users WHERE name = '\' OR \'1\'=\'1'
整个输入被当作普通字符串,无法篡改查询逻辑,攻击无效。
结论 :永远使用 ? 占位符 + 数组传参,杜绝 SQL 注入。
2. XSS(跨站脚本)-- 输出转义 + textContent
后端(输出转义)
ts
import { Elysia } from 'elysia'
// 转义 HTML 特殊字符,防止浏览器将用户输入当作代码执行
const escapeHtml = (s: string) =>
s.replace(/[&<>]/g, (m) => ({ '&': '&', '<': '<', '>': '>' })[m] || m)
new Elysia()
.get('/hello', ({ query }) => {
const safe = escapeHtml(query.name || 'guest')
return `<h1>Hello ${safe}</h1>`
})
.listen(3000)
前端(使用 textContent 而非 innerHTML)
html
<div id="out"></div>
<script>
fetch('/hello?name=<script>alert(1)</script>')
.then(r => r.text())
.then(html => {
document.getElementById('out').textContent = html // 不会执行脚本
})
</script>
3. CSRF(跨站请求伪造)-- Token + SameSite Cookie
后端(Bun.CSRF + sameSite: 'lax')
ts
import { Elysia } from 'elysia'
const csrfStore = new Map()
new Elysia()
.get('/form', ({ cookie, setCookie }) => {
const sid = cookie.sessionId?.value || crypto.randomUUID()
setCookie('sessionId', sid, { httpOnly: true, sameSite: 'lax' })
const { token } = Bun.CSRF.generateToken()
csrfStore.set(sid, token)
return `
<form action="/transfer" method="POST">
<input name="csrf" value="${token}">
<button>转账</button>
</form>
`
})
.post('/transfer', ({ body, cookie }) => {
const valid = Bun.CSRF.verifyToken(body.csrf, csrfStore.get(cookie.sessionId?.value))
if (!valid) return new Response('CSRF无效', { status: 403 })
return '转账成功'
})
.listen(3000)
前端 :表单自动携带隐藏 csrf 字段;AJAX 时手动加到 X-CSRF-Token 头。
4. 弱密码与撞库 -- 强哈希(Argon2id)
后端(Bun.password.hash 默认 Argon2id)
ts
import { Elysia } from 'elysia'
new Elysia()
.post('/register', async ({ body }) => {
const hash = await Bun.password.hash(body.password) // Argon2id 慢哈希
// 存储 hash...
return { ok: true }
})
.listen(3000)
前端:可加 zxcvbn 强度提示(非必需)。
5. 文件上传漏洞 -- 白名单 + 随机名 + 大小限制
后端
ts
import { Elysia } from 'elysia'
new Elysia()
.post('/upload', async ({ body }) => {
const file = body.file as File
if (!['image/jpeg', 'image/png'].includes(file.type))
return new Response('类型错误', { status: 400 })
if (file.size > 5 * 1024 * 1024)
return new Response('文件过大', { status: 400 })
const ext = file.name.split('.').pop()
const newName = `${crypto.randomUUID()}.${ext}`
await Bun.write(`./uploads/${newName}`, file)
return { name: newName }
})
.listen(3000)
前端
html
<input type="file" id="upload" accept="image/jpeg,image/png">
<button onclick="uploadFile()">上传</button>
<script>
async function uploadFile() {
const file = document.getElementById('upload').files[0]
if (file.size > 5 * 1024 * 1024) return alert('文件不能超过5MB')
const fd = new FormData()
fd.append('file', file)
await fetch('/upload', { method: 'POST', body: fd })
}
</script>
6. 路径遍历(目录穿越)-- 路径规范化 + 根目录检查
后端
ts
import { Elysia } from 'elysia'
import { resolve, normalize } from 'path'
new Elysia()
.get('/files/*', ({ params, set }) => {
const reqPath = params['*']
const full = resolve('./public', normalize(reqPath))
if (!full.startsWith(resolve('./public'))) {
set.status = 403
return 'Forbidden'
}
return Bun.file(full)
})
.listen(3000)
前端:无特殊配合。
7. 中间人攻击(MITM)-- HSTS + 强制 HTTPS
后端
ts
import { Elysia } from 'elysia'
new Elysia()
.onBeforeHandle(({ set, request }) => {
if (request.headers.get('x-forwarded-proto') !== 'https') {
return new Response('请使用 HTTPS', { status: 403 })
}
set.headers['Strict-Transport-Security'] = 'max-age=31536000'
})
.listen(3000)
前端 :所有 API 请求使用 https://。
8. 点击劫持 -- X-Frame-Options
后端
ts
import { Elysia } from 'elysia'
new Elysia()
.onBeforeHandle(({ set }) => {
set.headers['X-Frame-Options'] = 'DENY'
})
.listen(3000)
前端:无需配合。
9. DDoS / 暴力破解 -- 限速库(@elysiajs/rate-limit)
安装
bash
bun add @elysiajs/rate-limit
后端
ts
import { Elysia } from 'elysia'
import { rateLimit } from '@elysiajs/rate-limit'
new Elysia()
.use(rateLimit({ max: 10, duration: 60000 }))
.post('/login', () => '登录成功')
.listen(3000)
前端(指数退避)
js
async function retryFetch(url, opts, attempt = 3) {
const res = await fetch(url, opts)
if (res.status === 429 && attempt > 0) {
const delay = 1000 * (4 - attempt)
await new Promise(r => setTimeout(r, delay))
return retryFetch(url, opts, attempt - 1)
}
return res
}
10. WebSocket 劫持 -- 校验 Origin
后端
ts
import { Elysia } from 'elysia'
new Elysia()
.ws('/ws', {
open: (ws) => {
const origin = ws.data.request.headers.get('origin')
if (origin !== 'https://yourdomain.com') ws.close()
}
})
.listen(3000)
前端 :正常 new WebSocket('wss://...')。
整理一下:10 种攻击与核心防御
| 攻击 | 后端核心防御 | 前端辅助 |
|---|---|---|
| SQL 注入 | 参数化查询(? 占位符 + 数组) |
无 |
| XSS | 输出转义(escapeHtml) |
textContent |
| CSRF | Bun.CSRF + sameSite:'lax' |
自动携带 token |
| 弱密码 | Bun.password.hash(Argon2id) |
可选强度提示 |
| 文件上传 | 白名单 + 随机名 + 大小限制 | accept + 大小预检 |
| 路径遍历 | resolve + startsWith |
无 |
| 中间人攻击 | HSTS + 强制 HTTPS | 使用 https:// |
| 点击劫持 | X-Frame-Options: DENY |
无 |
| DDoS / 暴力破解 | @elysiajs/rate-limit |
指数退避 |
| WebSocket 劫持 | 校验 Origin | 无 |
以上 10 个关键词,再开发阶段你可以要求作为提示词给到 AI,帮你写代码实现这些防御措施。