概念定义
HSTS 响应头只有在浏览器收到过一次 HTTPS 响应后才生效,第一个请求依然可能被降级攻击剥离。预加载列表把策略直接内置进浏览器,从第一次连接起就强制 HTTPS,彻底关闭这一引导漏洞。注册条件包括子域覆盖、顶级域要求与重定向行为。
工作机制
站点先返回带 max-age、includeSubDomains 与 preload 指令的 Strict-Transport-Security 头,再到 hstspreload.org 提交。通过自动校验后进入 Chromium 维护的列表,其他浏览器同步消费。一旦随浏览器发布,移除需要以月计的漫长流程。
使用场景
适合永远只走 HTTPS 的域名:公开网站、API 主机、登录域。启用前必须审计所有子域,包括被遗忘的内部工具与打印机管理面板。同时配合 Secure Cookie 标志,确保 Cookie 也绝不走明文。
常见误区
预加载近乎不可逆,任何一个仅支持 HTTP 的子域都会在收录后变得不可达。仍然使用 http:// 链接的旧客户端将直接失败而非重定向。正确的上线顺序是:全站 HTTPS、逐步加大 max-age 的 HSTS 头、最后才提交 preload。
代码示例
export function parseContentDisposition(header) {
if (typeof header !== "string" || header.trim() === "") {
throw new TypeError("header must be a non-empty string");
}
const segments = splitParameters(header);
const type = segments.shift().trim().toLowerCase();
if (type !== "inline" && type !== "attachment") {
throw new Error(`unsupported disposition type "${type}"`);
}
const parameters = new Map();
for (const segment of segments) {
const equals = segment.indexOf("=");
if (equals === -1) continue;
const name = segment.slice(0, equals).trim().toLowerCase();
if (name === "") continue;
parameters.set(name, unquote(segment.slice(equals + 1).trim()));
}
return { type, filename: resolveFilename(parameters), parameters };
}
function splitParameters(header) {
const segments = [];
let current = "";
let quoted = false;
for (const character of header) {
if (character === '"') quoted = !quoted;
if (character === ";" && !quoted) {
segments.push(current);
current = "";
continue;
}
current += character;
}
segments.push(current);
return segments;
}
function unquote(value) {
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
return value.slice(1, -1).replace(/\\(.)/g, "$1");
}
return value;
}
function resolveFilename(parameters) {
const extended = parameters.get("filename*");
if (extended !== undefined) {
const match = /^([\w-]+)'([\w-]*)'(.+)$/.exec(extended);
if (match !== null) {
try {
return sanitize(decodeURIComponent(match[3]));
} catch {
return sanitize(extended);
}
}
}
const plain = parameters.get("filename");
return plain === undefined ? null : sanitize(plain);
}
function sanitize(filename) {
return filename.split(/[\\/]/).pop() || null;
}
console.log(parseContentDisposition('attachment; filename="report.pdf"'));
console.log(parseContentDisposition("attachment; filename*=UTF-8''%E4%B8%AD%E6%96%87.pdf"));
console.log(parseContentDisposition('inline; filename="../../etc/passwd"'));
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
)
type Shipment struct {
ID string `json:"id"`
Status string `json:"status"`
}
func queryShipment(ctx context.Context, baseURL, id string) (*Shipment, error) {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel() // 必须释放计时器资源
req, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL+"/shipments/"+id, nil)
if err != nil {
return nil, fmt.Errorf("build request: %w", err)
}
req.Header.Set("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return nil, fmt.Errorf("shipment %s timed out after 3s: %w", id, err)
}
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
var shipment Shipment
if err := json.NewDecoder(resp.Body).Decode(&shipment); err != nil {
return nil, fmt.Errorf("decode body: %w", err)
}
return &shipment, nil
}
func main() {
s, err := queryShipment(context.Background(), "https://api.example.com", "sh-1")
fmt.Println(s, err)
}