Security Basic Notes
Security Programming
Security Principles
安全三要素:
- 机密性 (Confidentiality).
- 完整性 (Integrity).
- 可用性 (Availability).
安全原则:
- 白名单原则:
- 防火墙白名单: 默认只开放
80
与443
端口. - XSS 防御白名单: 富文本编辑器只允许
<a>
与<img>
标签.
- 防火墙白名单: 默认只开放
- 最小权限原则.
- 纵深防御原则:
- Web 应用安全.
- 网络环境安全.
- 数据库安全.
- OS 系统安全.
- 数据与代码分离原则:
- 缓冲区溢出漏洞: 混淆代码与数据边界, 导致安全问题发生.
- 注入漏洞: 混合应用代码与用户数据, 导致 SQL/XSS/CSRF/XML 漏洞发生.
- 不可预测性原则:
- 有效地对抗基于篡改与伪造的攻击.
- 随机栈基址防御缓冲区溢出攻击.
- 加密哈希 Token 防御 CSRF 攻击.
STRIDE 威胁分析模型:
Threat | Definition | Security |
---|---|---|
Spoofing | 伪装 (他人身份) | 认证 |
Tampering | 篡改 (数据或代码) | 完整性 |
Repudiation | 抵赖 (做过的事情) | 不可抵赖性 |
Information Disclosure | 信息泄露 (机密数据) | 机密性 |
Denial of Service | 拒绝服务 | 可用性 |
Elevation of Privilege | 提升权限 (未经授权获得许可) | 授权 |
DREAD 风险分析模型:
- :
- 高危: 12 ~ 15 分.
- 中危: 8 ~ 11 分.
- 低危: 0 ~ 7 分.
- Damage Potential:
- High: 完全验证权限, 执行管理员操作, 非法上传文件.
- Medium: 泄露敏感信息.
- Low: 泄露其他信息.
- Reproducibility:
- High: 攻击者可以随意重复攻击.
- Medium: 攻击者可以重复攻击, 有时间限制.
- Low: 攻击者难以重复攻击.
- Exploitability:
- High: 初学者短期能掌握攻击方法.
- Medium: 熟练者才能掌握攻击方法.
- Low: 漏洞利用条件非常苛刻.
- Affected users:
- High: All user, key user, default configuration.
- Medium: Some user, non-default configuration.
- Low: Rare user, anonymous user.
- Discoverability:
- High: 漏洞显眼, 攻击条件容易获得.
- Medium: 私有区域才可发现漏洞, 需要深入挖掘.
- Low: 发现漏洞极其困难.
Software Security Assurance
- 逻辑安全.
- 数据安全:
- 数据访问权限.
- 数据访问时间.
- 数据存储格式.
- 数据存储位置.
- 数据分解与组合.
- UI 安全.
- 约束安全:
- 数据正确类型.
- 数据正确范围.
- 数 据长度的最大值和最小值.
- 数据非法值.
Defensive Programming
保护程序免遭非法数据的破坏:
- 检查所有来源于外部的数据:
当从外部接口中获取数据时, 应检查所获得的数据值, 以确保它在允许的范围内.
- 检查接囗数据字段: 是否存在/数据类型/取值范围/缺省值/正则表达式/特殊字符.
- 检查方法的属性参数做必要的转换: 是否存在/数据类型/取值范围/缺省值/正则表达式/特殊字符.
- 检查子程序所有输入参数的值.
- 决定如何处理错误的输入数据: 一旦检测到非法参数, 选择适合的错误处理方处理.
断言:
- 用错误处理代码处理预期发生的状况, 用断言去处理那些不该发生的错误!
- 利用断言来注解前条件和后条件: 对于
入参
/外部数据
/返回结果
进行检查, 是否符合业务逻辑. - 避免将需要执行的子程序放到断言中.
- 通过写断言, 不仅可以提高防御性, 还能提高可读性.
程序的健壮性:
- 健壮性具体指的是应用在不正常的输入或不正常的外部环境下仍能表现出正常的程度.
- 不断尝试采取措施来包容错误的输入以此让程序正常运转 (对自己的代码要保守, 对用户的行为要开放:
主动防御处理是有降级/容错处理, 尽量不要走到
Error Boundary
. - 考虑各种各样的极端情况:
- 考虑到各种可能的输入修士, 兼容全面.
- Mock 各种极端数据进行测试.
- 即使终止执行, 也要准确/无歧义的向用户展示全面的错误信息.
- 抛出有助于 Debug 的错误信息: 丰富捕获到的错误信息, 包含更多上下文信息.
程序的正确性:
- 返回
中立值
/默认值
: 处理错误的最佳做法就是继续执行操作并简单的返回一个没有危害的值.- 换用下一个正确的数据: 在轮询中, 如返回数据有误就丢掉, 进行下一轮查询.
- 返回上一次正确的数据: 同上, 不跳过的也可以返回上一次正确的数据.
- 选择最接近的合法值.
- 上报错误日志: 上报的错误日志对还原问题和调试友好.
- 返回一个错误状态码.
- 启动错误处理子程序或对象.
- 显示对用户友好的出错消息: 展示给用户的错误信息需要加工, 用户能看明白, 有建设性.
- 正确性要求高的话, 就直接退出程序.
异常处理:
- 用异常通知程序的其他部分, 发生了不可忽略的错误.
- 只在真正例外的情况下才抛出异常: 当前有错误就直接抛出去, 会导致线上监控的错误信息质量不高.
- 不能用异常来推卸责任: 能在局部处理掉就在局部解决掉, 不要简单抛出去.
- 避免在构造函数和析构函数中抛出异常, 除非你在同一个地方把它们捕获.
- 在恰当的抽象层次抛出异常: 不要把底层的异常抛给高层的调用方, 暴露具体实现的细节.
- 异常消息中加入关于导致异常发生的全部信息.
- 避免使用空
catch
语句. - 考虑创建一个集中的异常上报机制.
- 考虑异常的替换机制.
代码隔栏:
- 左侧外部接口数据假定是脏数据 (不可信), 通过中间 这些类 (子程序) 构成隔栏, 负责清理与验证数据, 并返回可信的数据.
- 最右侧的类 (子程序) 全部在假定数据干净 (安全) 的基础上工作, 这样可以让大部分的代码无须再担负检查错误数据的职责.
- 适配器模式和门面模式用来隔离或适配变化, 是对不可控变化的防御.
辅助调试代码 (Debugging Aids
):
- 在早期的引入辅助调试代码, 发布时移除调试辅助的代码:
console.log
算是一种辅助调试代码, 发布时清除.- 在复杂的调试场景下, 有必要专门写一些辅助调试代码.
- 利用 Chrome 插件追踪变量和状态变化辅助调试.
- 采用进攻式编程:
- 尽量让异常的情况在开发期间暴露出来, 而在产品上线时自我恢复.
- 在开发阶段考虑到最坏的情况.
- 写单测是一进攻式编程.
UI 的防御性:
- 防白屏:
- 白屏监控.
- 资源加载失败重试.
Service Worker
: 资源 fallback 机制.- 模块都包装了
Error Boundary
. - 兼容性探测和提示.
- 白屏提示信息.
- 防慢 (网络慢/响应慢/渲染慢/执行慢): 前端性能优化.
- 防卡 (卡顿/假死): 前端性能优化.
- 防布局错乱: 前端响应式开发.
- 防极端内容 (缺失/超长/连续字符/未转义): 前端响应式开发.
- UI 一致性问题:
- 发布设计规范 (
Design System
). - UI 走查工具.
- 视觉回归测试.
- 发布设计规范 (
- 防样式污染:
- 代码审核.
- 样式隔离.
Security Headers
Security headers list:
- X-Content-Type-Options.
- X-Frame-Options:
DENY
: 禁止被加载进任何 frame.SAMEORIGIN
: 仅允许被加载进同域内的 frame.
- X-XSS-Protection:
0
:表示禁用这个策略.1
: 默认, 对危险脚本做一些标志或修改, 以阻止在浏览器上渲染执行.1; mode=block
:, 强制不渲染.
- Setting
Context-Security-Policy
header. - Ensure all connections to be HTTPS.
- Avoid Clicking-jacking using
X-Frame-Options
. - Disable
X-Powered-By
header. - Not put sensitive data in URL query and
Referer
header.
Same Origin Policy
origin
= protocol
+ domain
+ port
:
http://www.foo.com | Same Origin | |
---|---|---|
https://www.foo.com | No | protocol |
http://app.foo.com | No | domain |
http://foo.com | No | domain |
http://www.foo.com:8080 | No | port |
http://www.foo.com/a/ | Yes |
{
"Access-Control-Allow-Origin": "https://api.foo.com"
}
- 由客户端 HTML 标签等发出的跨域
GET
请求默认合法, 构成开放的 Web 世界: 通过src
属性加载的资源, 浏览器限制了 JavaScript 的权限, 使其不能读写返回的内容. XMLHttpRequest
受同源策略限制: 默认只能访问同源对象的内容.- Cookie 受同源策略限制.
- Web storage 受同源策略限制:
- Local storage.
- Session storage.
Content Security Policy
CSP
help prevent from XSS
:
{
"header": {
"Content-Security-Policy":
allow 'self';
script-src 'nonce-random123' 'strict-dynamic' 'unsafe-eval' script.example.com;
media-src media.example.com;
img-src img.example.com;
object-src 'none';
base-uri 'none'
}
}
upgrade-insecure-requests
指示浏览器在发出网络请求之前升级不安全的 URL
:
<meta
http-equiv="Content-Security-Policy"
content="upgrade-insecure-requests"
/>
block-all-mixed-content
指示浏览器永不加载混合内容,
所有混合内容资源请求都会被阻止,
包括主动和被动混合内容:
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content" />
nonce
-only CSP
block 3rd scripts and dynamic scripts generate by trusted users:
<script nonce="random123">
alert('this is fine!');
</script>
<script nonce="random123" src="https://cdnjs.com/lib.js"></script>
<!-- XSS injected by attacker - blocked by CSP -->
<script>
alert('xss');
</script>
strict-dynamic
CSP
allow dynamic scripts generate by trusted users:
<!-- Content-Security-Policy: script-src 'nonce-random123' 'strict-dynamic' -->
<script nonce="random123">
const s = document.createElement('script');
s.src = '/path/to/script.js';
s.async = true;
document.head.appendChild(s); // can execute correctly
</script>
domain
-specific CSP
block untrusted domain scripts:
<!-- Given this CSP header -->
<!-- Content-Security-Policy: script-src https://example.com/ -->
<!-- The following third-party script will not be loaded or executed -->
<script src="https://not-example.com/js/library.js"></script>
Trusted Types
- TrustedURL.
- TrustedHTML.
- TrustedScript.
- TrustedScriptURL.
// fallback policy
TrustedTypes.createPolicy(
'default',
{
createHTML(s) {
console.error('Please fix! Insecure string assignment detected:', s);
return s;
},
},
true
);
// Content-Security-Policy-Report-Only: trusted-types myPolicy; report-uri /cspReport
const SanitizingPolicy = TrustedTypes.createPolicy(
'myPolicy',
{
createHTML: (s: string) => myCustomSanitizer(s),
},
false
);
const trustedHTML = SanitizingPolicy.createHTML(foo);
element.innerHTML = trustedHTML;
Sandbox
eval()
: 它能访问执行上下文中的局部变量, 也能访问所有全局变量, 是一个非常危险的函数.new Function()
: 在全局作用域中被创建, 不会创建闭包. 当运行函数时, 只能访问本地变量和全局变量, 不能访问 Function 构造器被调用生成的上下文的作用域.with () {}
: 它首先会在传入的对象中查找对应的变量, 如果找不到就会往更上层的全局作用域去查找, 导致全局环境污染.- 快照沙盒: ES5 沙盒, 备份恢复全局上下文.
- 代理沙盒: ES6 沙盒, 使用 Proxy 代理全局上下文.
<iframe>
沙盒: 使用<iframe>
隔离全局上下文.ShadowRealm
沙盒.
Snapshot Sandbox
SnapshotSandbox
:
class SnapshotSandbox {
constructor() {
this.proxy = window; // window 属性.
this.windowSnapshot = {}; // 快照.
this.modifyPropsMap = {}; // 记录在 window 上的修改.
this.sandboxRunning = false;
}
active() {
this.windowSnapshot = {}; // 快照.
for (const prop in window) {
if (window.hasOwn(prop)) {
this.windowSnapshot[prop] = window[prop];
}
Object.keys(this.modifyPropsMap).forEach(p => {
window[p] = this.modifyPropsMap[p];
});
}
this.sandboxRunning = true;
}
inactive() {
this.modifyPropsMap = {}; // 记录在 window 上的修改.
for (const prop in window) {
if (window.hasOwn(prop)) {
if (window[prop] !== this.windowSnapshot[prop]) {
this.modifyPropsMap[prop] = window[prop];
window[prop] = this.windowSnapshot[prop];
}
}
}
this.sandboxRunning = false;
}
}
Proxy Sandbox
ProxySandbox
:
function ProxySandbox(code) {
code = `with (sandbox) {${code}}`;
// eslint-disable-next-line no-new-func
const fn = new Function('sandbox', code);
return function (sandbox) {
const sandboxProxy = new Proxy(sandbox, {
has(target, key) {
return true;
},
get(target, key) {
if (key === Symbol.unscopables) return undefined;
return target[key];
},
});
return fn(sandboxProxy);
};
}
Iframe Sandbox
class SandboxWindow {
constructor(context, frameWindow) {
return new Proxy(frameWindow, {
get(target, name) {
if (name in context) {
return context[name];
} else if (typeof target[name] === 'function' && /^[a-z]/.test(name)) {
return target[name].bind && target[name].bind(target);
} else {
return target[name];
}
},
set(target, name, value) {
if (name in context) {
return (context[name] = value);
}
target[name] = value;
},
});
}
}
// 需要全局共享的变量
const context = {
document: new Proxy(window.document, {}),
location: new Proxy(window.location),
history: new Proxy(window.history),
};
// 创建 iframe
const userInputUrl = '';
const iframe = document.createElement('iframe', {
url: userInputUrl,
src: 'about:blank',
sandbox:
'allow-scripts allow-same-origin allow-popups allow-presentation allow-top-navigation',
style: 'display: none;',
});
document.body.appendChild(iframe);
const sandboxGlobal = new Proxy(iframe.contentWindow, {});
// 创建沙箱
const newSandboxWindow = new SandboxWindow(context, sandboxGlobal);
Crypto
Web crypto API:
- 公钥加密私钥解密: 只有私钥拥有者可以获取信息, e.g HTTPS 会话密钥传输.
- 公钥解密私钥加密: 只有私钥拥有者可以发布签名, e.g 数字签名.
User Privacy
Browser Privacy Detection
- Browser leaks.
User Fingerprint
- Use Canvas or WebGL to generate user fingerprint.
- Location information:
- Country.
- Region.
- City.
- Zip code.
- Latitude.
- Longitude.
- Timezone.
- Connection information:
- IP address.
- Internet service provider.
- Organization.
- ASN.
- Tor browser detection.
- Software information:
- Browser.
- Browser plugins detection.
- Operation system.
- User agent.
- Preferred language.
- Cookie enabled detection.
- Java enabled detection.
- DNT header enabled detection.
- Automated browser detection.
- Content filters detection: Adblock detection.
- Hardware information:
- Screen resolution.
- Color resolution.
- Device type.
- Device memory.
- CPU cores.
- Max touch points.
- WebGL vendor.
- WebGL renderer.
- Battery level.
- Batter status.
function getCanvasFingerprint() {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
context.font = '18pt Arial';
context.textBaseline = 'top';
context.fillText('Hello, user.', 2, 2);
return canvas.toDataURL('image/jpeg');
}
getCanvasFingerprint();
Security Vulnerability
XSS
XSS Attack
Cross-site scripting (跨站脚本):
- Reflected XSS:
url input (search pages)
http://localhost:8080/test?name=<script>alert('attack')</script>
. - Stored XSS: store script into database.
- Inject script in HTML/CSS/JavaScript/JSON/HTTP:
<a href=javascript:...>
.<base href=javascript:...>
.<bgsound src=javascript:...>
.<body background=javascript:...>
.<frameset><frame src="javascript:..."></frameset>
.<iframe src=javascript:...>
.<img src=x onerror=...>
.<input type=image src=javascript:...>
.<layer src=...>
.<link href="javascript:..." rel="stylesheet" type="text/css">
.<meta http-equiv="refresh" content="0;url=javascript:...">
.<object type=text/x-scriptlet data=...>
.<script>...</script>
.<style type=text/javascript>alert('xss')</style>
.<table background=javascript:...>
.<td background=javascript:...>
.<button onmouseover="alert(/1/)" onclick="alert(/2/)"></button>
.<button form="test" formaction="javascript:alert(1)">
.<button form=test onformchange=alert(2)>
.<input onfocus=write(1) autofocus>
.<form id=test onforminput=alert(1)><input></form>
.<audio onerror=javascript:alert(1)>
.<audio><source onerror="javascript:alert(1)"></audio>
.<video onerror=javascript:alert(1)>
.<video><source onerror="javascript:alert(1)"></video>
.@import ""
.background: url("")
.window.name
.window.location
.document.domain
.document.referrer
.document.location.assign()
.document.location.replace()
.document.write()
.element.innerHTML
.element.outerHTML
.cookie
.localStorage
.
XSS Protection
Don't trust user:
- Escape control characters (输入校验):
"
->"
,&
->&
,'
->'
,<
-><
,>
->>
.input.replace(/<script>|<script/>/g, '')
.input.trim()
.- React DOM escapes any values embedded in JSX before rendering them.
- Secure encode output result (输出编码):
保证编码方式一致, e.g 混用单字节编码与多字节编码, 导致攻击者绕过
\
系统转义符. - 任何用户提供的 URL 在进一步处理前 (e.g 保存到数据库),
应先做无害化处理 (
SanitizeUrl
). - Check HTTP
Content-Type
header. - Content security policy:
script-src 'self' https://apis.google.com
. - Use mature template engine: built-in XSS protection feature.
CSRF
CSRF Attack
Cross-site request forgery (跨站请求伪造)
挟制用户在当前已登录的 Web 应用程序上执行非本意的操作,
利用已认证用户 (长期本地 Cookie
), 访问攻击者网站, 并被强制执行脚本,
在用户不知情的情况下提交 GET
/POST
request (长期本地 Cookie
):
<link href="">
.<a href="">
.<img src="">
.<img lowsrc="">
.<img dynsrc="">
.<iframe src="">
.<frame src="">
.<meta http-equiv="refresh" content="0; url=">
.<script src="">
.<bgsound src="">
.<embed src="">
.<video src="">
.<audio src="">
.<table background="">
.@import ""
.background: url("")
.
- XSS 利用的是网站对用户 (输入) 的信任.
- CSRF 利用的是网站对用户网页浏览器的信任.
CSRF Protection
REST
(representational state transfer) 原则:GET
request: only read objects, 确保无副作用.PUT
request: only create new objects.POST
request: only modify objects.DELETE
request: only delete objects.
- 确保
request
正常发起渠道:- Anti-CSRF cookie:
- 不可预测 salt token:
Set-Cookie: _xsrf=5978e29d4ef434a1
. - Hidden token check in
<form>
.
- 不可预测 salt token:
- 开启同源策略:
Set-Cookie: _xsrf=5978e29d4ef434a1; SameSite=Strict;
. - 检查 HTTP
Referer
请求头. - 检查第三方网站
URL
, 显示第三方地址跳转警告页面:- Remove sensitive data in URL query and
Referer
header. <a href="https://3rd.com" target="_blank" rel="noopener noreferrer nofollow">
.
- Remove sensitive data in URL query and
- Anti-CSRF cookie:
- Require re-authentication for sensitive action: 支付账单, 修改邮箱, 删除账号.
- Use mature
CSRF
protection library: express.jscsurf
library.
# Reject cross-origin requests to protect from
# CSRF, XSSI & other bugs
def allow_request(req):
# Allow requests from browsers which don't send Fetch Metadata
if not req['sec-fetch-site']:
return True
# Allow same-site and browser-initiated requests
if req['sec-fetch-site'] in ('same-origin', 'same-site', 'none'):
return True
# Allow simple top-level navigation from anywhere
if req['sec-fetch-mode'] === 'navigate' and req.method === 'GET':
return True
return False
Click Jacking
Click Jacking Attack
Hover transparent malicious link upon trusted true button:
click
.drag
anddrop
.touch
.
Click Jacking Protection
X-Frame-Options
:
DENY
.SAMEORIGIN
.ALLOW-FROM origin
.
// nodejs
response.setHeader('X-Frame-Options', 'DENY');
Content security policy:
Content-Security-Policy: frame-ancestors 'none'
Content-Security-Policy: frame-ancestors 'self'
Content-Security-Policy: frame-ancestors example.com google.com
Prevent load self in frame (Frame Busting
):
<style>
/* Hide page by default */
html {
display: none;
}
</style>
<script>
if (
top != self ||
top.location != location ||
top.location != self.location ||
parent.frames.length > 0 ||
window != top ||
window.self != window.top ||
window.top !== window.self ||
(parent && parent != window) ||
(parent && parent.frames && parent.frames.length > 0) ||
(self.parent && !(self.parent === self) && self.parent.frames.length != 0)
) {
// Break out of the frame.
top.location = self.location;
top.location.href = document.location.href;
top.location.href = self.location.href;
top.location.replace(self.location);
top.location.href = window.location.href;
top.location.replace(document.location);
top.location.href = window.location.href;
top.location.href = 'URL';
document.write('');
top.location = location;
top.location.replace(document.location);
top.location.replace('URL');
top.location.href = document.location;
top.location.replace(window.location.href);
top.location.href = location.href;
self.parent.location = document.location;
parent.location.href = self.document.location;
parent.location = self.location;
} else {
// Everything checks out, show the page.
document.documentElement.style.display = 'block';
}
</script>
<iframe>
sandbox
属性与 security
,
可以限制 <iframe>
页面中的 JavaScript 脚本执行,
从而使得 Frame Busting
失效.
SQL Injection
SQL Injection Attack
User input ' OR 1=1--
:
SELECT *
FROM users
WHERE email = 'user@email.com'
AND pass = '' OR 1=1--' LIMIT 1
SQL Injection Protection
- Don't allow multiple statements.
- Validate user input: 是否存在/数据类型/取值范围/缺省值/正则表达式/特殊字符.
- Allowlist user input.
- Least privilege principle:
allow
SELECT
/INSERT
/UPDATE
/DELETE
on certain data, forbiddenCREATE
/DROP
/MODIFY
. - Use mature object-relational mapping (ORM) library:
built-in SQL injection protection feature,
users.findBy({ email: "billy@gmail.com" })
. - Parameterized statements: use placeholders instead of variable interpolation.
-- Construct the SQL statement we want to run, specifying the parameter.
Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
Statement statement = connection.createStatement();
String sql = "SELECT * FROM users WHERE email = ? and encrypted_password = ?";
statement.executeQuery(sql, email, password);
Command Injection
Command Injection Attack
{
"query": "?domain=google.com%3BEcho%20%22Hacked%22"
}
Command Injection Protection
- Escape control characters:
<
/>
/?
/=
/&&
. - Disable code execution during deserialization.
- 尽量不使用系统执行命令.
- 保证动态函数为受信任函数, 防止被攻击者替换.
HTTP Injection
Malicious Redirect Attack
{
"query": "?redirect=google.com%3BEcho%20%22Hacked%22"
}
Malicious Redirect Protection
- 检查第三方网站
URL
, 显示第三方地址跳转警告页面:- Remove sensitive data in URL query and
Referer
header. <a href="https://3rd.com" target="_blank" rel="noopener noreferrer nofollow">
.
- Remove sensitive data in URL query and
- Check
?url=
/?redirect
data:- Escape control character.
- Limit redirect range.
- Check
Referrer
header when doing redirect.
function isRelative(url) {
return url && url.match(/^\/[^\/\\]/);
}
HTTP Header Injection Attack
通过截断 HTTP 响应头 (换行符/空字符),
覆盖 Location
/Referer
响应头,
注入攻击者设置的 HTTP 响应头.
HTTP Header Injection Protection
- 不将外部输入作为 HTTP 响应头.
- 检验 HTTP 响应头特殊字符: e.g 换行符.
- 使用成熟的库生成 HTTP 响应头, 禁止简单字符拼接.
Object Injection
Object Injection Attack
__proto__.XX
.constructor
.hasOwnProperty
.
Insecure Object Comparison
Injection:
const token = req.cookie.token;
// Vulnerability:
// SESSIONS[constructor] => `true`
if (token && SESSIONS[token]) {
next();
}
Solutions:
crypto.timingSafeEqual
.object.hasOwnProperty(token)
.
File Upload Injection
File Upload Attack
当使用 JS 代码限制上传文件类型时, 攻击者 disable JS in browser, 并上传 malicious code file:
// Malicious `web shell` code file
<?php
if (isset($_REQUEST['cmd'])) {
$cmd = ($_REQUEST['cmd']);
system($cmd);
} else {
echo 'What is your bidding?';
}
?>
一旦攻击者成功将 webShell.php
当成头像上传成功,
便可以在头像处执行 web shell
远程攻击.
File Upload Protection
对于用户上传文件:
- 隔离文件: host files on secure system.
- 禁止文件执行:
-x
. - 重命名/哈希化文件: 防止攻击者找到此文件.
- 检查文件格式 (extension/
MIME
type/Content-Type
). - 检查文件内容.
- 检查
Content-Type
header. - 扫描文件: virus scanner.
Authentication Vulnerability
Password Attack
Password mis-management:
- Reused key attack: 使用同一个密钥进行多次加/解密, 将使得破解流密码变得非常简单.
Password Protection
- Require complex password.
- Require multi-factor authentication.
- Secure and robust password hashing:
bcrypt
. - Secure and robust password salting.
- Secure password resets.
- Validate email address.
- Ban disposable email account.
- 密钥管理:
- 防止密钥从非正常的渠道泄露.
- 定期更换密钥.
- 采用安全的密钥管理系统: Web 应用通过 service API 动态获取密钥, 只加载到内存中, 不写入本地文件.
User Enumeration Attack
通过暴力工具得到被攻击网站的用户名单, 并利用社工得到密码:
REST API 无法抵抗此种攻击, e.g GitHub user profile.
User Enumeration Protection
User API Protection
- 限制 API 访问频率与次数.
- 设置 IP 黑名单.
- 记录登陆日志.
- CAPTCHA: completely automated public turing test to tell computers and humans apart.
- 隐藏用户 ID, 只显示昵称.
Login Protection
使攻击者无法枚举用户名, 他无法确定是用户不存在还是密码错误:
- Login error message: Unknown User or Password.
- All login code-paths take same time on average: time consuming operations.
- All login code-paths take same context: session ID, cookie.
Sign Up and Reset Protection
Not with name, should with email:
- 使攻击者无法枚举用户名, 他无法确定是用户不存在还是用户已存在.
- Not Exist: Sending sign-up email.
- Exist: Sending pwd-reset email.
Session Vulnerability
Session Hijacking Protection
Same site cookie recipe,
Set-Cookie: session_id=278283910977381992837; HttpOnly; Secure; SameSite=Lax
:
- Prevent XSS cookie theft,
disable JavaScript access to cookie:
Set-Cookie: session_id=278283910977381992837; HttpOnly
. - Prevent CSRF cookie theft,
enable same-origin policy, only allow cookie on
GET
request (social media sharing):Set-Cookie: session_id=278283910977381992837; SameSite=Lax
. - Prevent man-in-the-middle attack,
use HTTPS connection:
Set-Cookie: session_id=278283910977381992837; Secure
.
Session Fixation Protection
在 HTTP Cookie 中传输复杂的 session ID, 并在成功连接/恶意篡改后重置 session ID:
- Not passing session ID in
queryString
/requestBody
: 跳转至第三方链接时, 会在Referer
header 处泄露 session ID, passing them in HTTP Cookie. 同样地, 不允许在 URL query 处放置任何其他敏感数据 (如 token). - Generate complex session ID.
- 认证成功前不在会话变量中存储敏感信息.
- Reset session ID after set up session successfully.
- Reset session ID after it's been changed manually on client (
Set-Cookie
):- IP.
- Device.
- User agent.
req.session.regenerate(function (err) {
process(err);
});
const generateSessionId = session => uid(24);
Directory Traversal
Directory Traversal Attack
GET /../../../passwd.key HTTP/1.1
Directory Traversal Protection
- 检查请求路径是否安全, 否则不返回响应.
- Use mature hosting service.
- Use indirect file reference.
- Sanitize file reference.
Information Leakage
Information Leakage Attack
- Server in response headers.
- Cookie: J_SESSION_ID -> Java.
- URL:
.jsp
,.php
,.asp
. - Error message.
- AJAX responses.
- JSON/XML responses.
- Code information.
[
{
"Server": "Apache/1.3.23",
"Accept-Ranges": "bytes",
"Content-length": 196,
"Connection": "close",
"Content-Type": "text/html",
"Cookie": "SESSION_ID=XXXXX"
},
{
"Server": "Microsoft-IIS/5.0",
"Content-Type": "text/html",
"Accept-Ranges": "bytes",
"ETag": "b0aac0542e25c31",
"Content-Length": 7369
}
]
Information Leakage Protection
NODE_ENV=production
.- 处理/混淆/加密原始数据 (raw data).
- 处理/混淆客户端代码.
- 去除工具库的版本信息.
- Disable
Server
HTTP header and similar headers. - Use clean URLs without extensions.
- Use generic cookie parameters.
- Disable client-side error reporting.
- Sanitize data passed to client.
- Obfuscate JavaScript.
- Sanitize template files.
- Ensure correct configuration of Web root directory.
- 配置边界路由器和防火墙: 阻止从敏感来源发起的信息请求.
- 限制对所有敏感信息源的访问.
- 永远不要硬编码密码, 且不放在易暴露位置.
- 对任何敏感信息源使用双因素认证.
- 执行审核来查找潜在的漏洞.
- 使用评估工具来确定是否存在从指定位置外的任何位置访问敏感信息源的可能.
Supply Chain Vulnerability
Supply Chain Attack
- Running file encryption attack in Node.js module.
- left-pad.
- eslint.
- antd.
- faker.js.
- colors.js.
- node-ipc.
- es5-ext.
- event-source-polyfill.
- styled-components.
Supply Chain Protection
评估 NPM package 质量:
- 代码质量.
- 测试完备性.
- 文档完备性.
- 工程完备性 (DevOps).
- 开发人员构成.
- 兼容性:
- 流行度.
- 历史遗留 Bug.
- 重复实现复杂度.
- 使用时长.
- 后续依赖版本更新策略.
Malicious Package Attack
名字与流行包相近, 通过 postinstall
脚本执行病毒脚本, 获取系统环境变量信息 e.g crossenv
.
Malicious Package Protection
- No typo in
package.json
: - 禁止执行
postinstall
脚本. - Use mature dependency management tools:
renovate
. - Stay alert to security issues:
- Apply operating system patches.
- Security tools:
npm audit
,socket.security
. - Official advisories.
- Mailing lists and blogs.
- Social media.
- Package integrity check.
cat FILENAME.js | openssl dgst -sha384 -binary | openssl base64 -A
XML Vulnerability
XML Attack
Inline document type definition in XML led to dangerous macros:
- XML bombs.
- XML external entities.
XML Protection
- Disable DTD (Document Type Definitions) parse in XML parser:
xml2js
,parse-xml
,node-xml
. - Least privilege principle.