Skip to content

CSP 与 HTTPS ​

CSP (Content Security Policy) ​

通过 HTTP 头告诉浏览器哪些资源是合法的,阻止注入攻击。

指令 ​

http
Content-Security-Policy:
  default-src 'self';                    # 所有资源默认只允许本站
  script-src 'self' https://cdn.com;     # 脚本来源
  style-src 'self' 'unsafe-inline';      # 样式来源(允许内联)
  img-src 'self' data: https:;           # 图片来源
  connect-src 'self' https://api.com;    # API 请求
  font-src 'self' https://fonts.com;     # 字体
  frame-src 'none';                      # 禁止嵌入 iframe
  object-src 'none';                     # 禁止插件
  base-uri 'self';                       # 限制 <base>
  form-action 'self';                    # 限制表单提交目标
Content-Security-Policy:
  default-src 'self';                    # 所有资源默认只允许本站
  script-src 'self' https://cdn.com;     # 脚本来源
  style-src 'self' 'unsafe-inline';      # 样式来源(允许内联)
  img-src 'self' data: https:;           # 图片来源
  connect-src 'self' https://api.com;    # API 请求
  font-src 'self' https://fonts.com;     # 字体
  frame-src 'none';                      # 禁止嵌入 iframe
  object-src 'none';                     # 禁止插件
  base-uri 'self';                       # 限制 <base>
  form-action 'self';                    # 限制表单提交目标

常用值 ​

值含义
'self'同源
'none'禁止
'unsafe-inline'允许内联(尽量避克)
'unsafe-eval'允许 eval(尽量避克)
https:所有 HTTPS 来源
data:data: URI
'strict-dynamic'信任已加载脚本的动态加载
nonce-xxx只允许特定 nonce 的脚本

nonce 示例 ​

js
// 服务端生成随机 nonce
const nonce = crypto.randomUUID()

// 设置 CSP
res.setHeader(
  'Content-Security-Policy',
  `script-src 'nonce-${nonce}'`
)

// HTML 中只有带正确 nonce 的脚本才能执行
<script nonce="<%= nonce %>">
  console.log('This script runs')
</script>

<script>
  console.log('This script blocked by CSP')
</script>
// 服务端生成随机 nonce
const nonce = crypto.randomUUID()

// 设置 CSP
res.setHeader(
  'Content-Security-Policy',
  `script-src 'nonce-${nonce}'`
)

// HTML 中只有带正确 nonce 的脚本才能执行
<script nonce="<%= nonce %>">
  console.log('This script runs')
</script>

<script>
  console.log('This script blocked by CSP')
</script>

报告模式 ​

http
# 仅报告不阻止(上线前先观察)
Content-Security-Policy-Report-Only:
  script-src 'self';
  report-uri /csp-report

# 浏览器会 POST 违规报告
{
  "csp-report": {
    "document-uri": "https://example.com/",
    "violated-directive": "script-src",
    "blocked-uri": "https://evil.com/hack.js"
  }
}
# 仅报告不阻止(上线前先观察)
Content-Security-Policy-Report-Only:
  script-src 'self';
  report-uri /csp-report

# 浏览器会 POST 违规报告
{
  "csp-report": {
    "document-uri": "https://example.com/",
    "violated-directive": "script-src",
    "blocked-uri": "https://evil.com/hack.js"
  }
}

HTTPS (TLS/SSL) ​

TLS 握手过程 ​

客户端                     服务端
  │                          │
  ├─ ClientHello ──────────► │  支持的加密套件、TLS 版本
  │                          │
  │ ◄── ServerHello ────────┤  选择的加密套件、证书
  │                          │
  │ ◄── Certificate ────────┤  服务端证书(含公钥)
  │                          │
  │ ◄── ServerHelloDone ────┤
  │                          │
  ├─ ClientKeyExchange ────► │  用公钥加密的预主密钥
  │                          │
  ├─ ChangeCipherSpec ─────► │  开始加密通信
  │                          │
  │ ◄── ChangeCipherSpec ───┤
  │                          │
  └────── 加密通信 ──────────┘
客户端                     服务端
  │                          │
  ├─ ClientHello ──────────► │  支持的加密套件、TLS 版本
  │                          │
  │ ◄── ServerHello ────────┤  选择的加密套件、证书
  │                          │
  │ ◄── Certificate ────────┤  服务端证书(含公钥)
  │                          │
  │ ◄── ServerHelloDone ────┤
  │                          │
  ├─ ClientKeyExchange ────► │  用公钥加密的预主密钥
  │                          │
  ├─ ChangeCipherSpec ─────► │  开始加密通信
  │                          │
  │ ◄── ChangeCipherSpec ───┤
  │                          │
  └────── 加密通信 ──────────┘

证书类型 ​

类型验证级别价格适用
DV (Domain Validation)仅域名所有权免费个人博客
OV (Organization)验证企业身份中企业网站
EV (Extended)严格验证高银行/金融

免费证书方案 ​

bash
# Let's Encrypt + Certbot
certbot --nginx -d example.com -d www.example.com

# acme.sh(更自动化)
curl https://get.acme.sh | sh
acme.sh --issue -d example.com --nginx
acme.sh --install-cert -d example.com --key-file /etc/nginx/ssl/key.pem --fullchain-file /etc/nginx/ssl/cert.pem
# Let's Encrypt + Certbot
certbot --nginx -d example.com -d www.example.com

# acme.sh(更自动化)
curl https://get.acme.sh | sh
acme.sh --issue -d example.com --nginx
acme.sh --install-cert -d example.com --key-file /etc/nginx/ssl/key.pem --fullchain-file /etc/nginx/ssl/cert.pem

HSTS(HTTP Strict Transport Security) ​

http
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  • 告诉浏览器只通过 HTTPS 访问
  • includeSubDomains — 子域名也生效
  • preload — 提交到浏览器预加载列表
  • 在 Nginx 中配置:
nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Nginx HTTPS 配置模板 ​

nginx
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-XSS-Protection "0";
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-XSS-Protection "0";
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

安全相关 HTTP 头 ​

nginx
# 在 Nginx 中添加安全头
add_header X-Content-Type-Options nosniff;           # 禁止 MIME 嗅探
add_header X-Frame-Options SAMEORIGIN;               # 防止点击劫持
add_header X-XSS-Protection "0";                     # 已废弃但兼容
add_header Referrer-Policy strict-origin-when-cross-origin;  # 引用策略
add_header Permissions-Policy "camera=(), microphone=()";    # 权限限制
# 在 Nginx 中添加安全头
add_header X-Content-Type-Options nosniff;           # 禁止 MIME 嗅探
add_header X-Frame-Options SAMEORIGIN;               # 防止点击劫持
add_header X-XSS-Protection "0";                     # 已废弃但兼容
add_header Referrer-Policy strict-origin-when-cross-origin;  # 引用策略
add_header Permissions-Policy "camera=(), microphone=()";    # 权限限制