robyfirnandoyusuf/SeqSQLi
GitHub: robyfirnandoyusuf/SeqSQLi
基于 Q-learning 强化学习的自适应 SQL 注入 WAF 绕过工具,将 payload 变异顺序建模为序列决策问题并自动学习最优绕过策略。
Stars: 0 | Forks: 0
# SeqSQLi
[](https://www.python.org/)
[]()
[]()
🌐 **语言:** **英语** | [印尼语](README.id.md)
## 📚 目录
- [为什么开发这个工具](#-why-this-tool-exists)
- [方法](#-approach)
- [系统架构](#-system-architecture)
- [训练流程 (RL Episode)](#-training-flow-rl-episode)
- [序列图:单步执行](#-sequence-diagram--single-step)
- [项目结构](#-project-structure)
- [安装](#-installation)
- [用法](#-usage)
- [研究问题](#-research-questions)
- [输出文件](#-output-files)
- [初学者 RL 概念](#-rl-concepts-for-beginners)
- [免责声明](#-disclaimer)
## 为什么开发这个工具
现代 **Web 应用程序防火墙 (WAF)** 使用
*基于签名的黑名单*来阻止 SQL 注入,即类似 `UNION`、`SELECT`、空格
和注释的关键字列表。为了绕过它们,渗透测试人员必须对 payload 进行 **变异**
(用 `%0a` 替换空格、大小写混合、关键字拆分等)。但是
传统方法存在三个局限性:
| 传统方法 | 问题 |
|---|---|
| **手动试错** | 速度慢、耗费精力且无法扩展 |
| **静态绕过列表** (sqlmap tamper) | WAF 厂商会很快修补这些列表 |
| **暴力变异器** | 会产生数千个请求,极易被速率限制检测到 |
**核心洞察**:对于分层过滤器,单次变异是不够的,所需的
是正确的变异**序列**。例如,在 sqli-labs
Less-27 上,序列 `case_split → vtab`(2 步)生成的 payload
可以顺利通过,而相反的顺序 `vtab → case_split` 则会失败。
SeqSQLi 将其建模为一个**序列决策问题**,并使用
**Q-learning** 进行求解,agent 从
经验中学习有效的变异序列,并尽可能减少请求次数。
## 方法
该问题被表述为一个 **马尔可夫决策过程 (MDP)**:
| MDP 组件 | 实现 |
|---|---|
| **状态** `s` | 上次响应结果 + 上次动作 + 步数 + 14 位 payload 特征向量 |
| **动作** `a` | 30 多种变异类型(大小写、注释、编码、拆分等) |
| **奖励** `r` | +10 成功,+0.5 SQL 错误,−2 WAF 拦截,−0.08 步数惩罚 |
| **策略** `π(a\|s)` | 带有过滤器感知探索偏置的 Epsilon-贪心 |
agent 会进行 N 个 episode 的训练(默认为 300)。每个 episode 以
一个基础 payload(例如 `0' UNION SELECT 1,2,'3`)开始,agent 选择变异
直到 payload 绕过 WAF,或达到 15 步的上限。
**相比 SSQLi 的创新点(参考论文):**
1. **状态感知变异**,payload 特征向量帮助 agent
记住已经应用了哪些变异(隐式历史)。
2. **过滤器感知探索**,随机探索会偏向于
与检测到的过滤器类型相关的变异。
3. **步数惩罚**,奖励函数推动策略趋向于
高效的解决方案(步数更少,请求更少)。
## 系统架构
```
flowchart TB
subgraph CLI["🖥️ Entry Points"]
agent[agent.py]
baseline[baseline.py]
end
subgraph PKG["📦 seqsqli/ Package"]
config[config.py
hyperparameters] subgraph CORE["core/ - engines"] profile[profile.py
TargetProfile + Presets] http[http.py
HTTP + URL encoding] response[response.py
WAF/SQL classifier] fingerprint[fingerprint.py
auto-detect injection] mutations[mutations.py
30+ payload mutators] end subgraph RL["rl/ - learning"] state[state.py
encode_state ϕ] qlearning[qlearning.py
Q-table + update] train[train.py
training loop] evaluate[evaluate.py
RQ3 ordering analysis] end builder[builder.py
target factories] extractor[extractor.py
DB data dumper] end subgraph EXT["🌐 External"] target[(Target WAF
+ Web App)] qfile[(q_table.json)] end agent --> config agent --> fingerprint agent --> train agent --> evaluate agent --> extractor agent --> builder baseline --> evaluate baseline --> qlearning train --> qlearning train --> mutations train --> response train --> state train --> http evaluate --> qlearning fingerprint --> http fingerprint --> response extractor --> qlearning extractor --> mutations extractor --> http qlearning <--> qfile http <--> target classDef pkg fill:#e1f5ff,stroke:#0277bd classDef ext fill:#fff3e0,stroke:#e65100 classDef cli fill:#f3e5f5,stroke:#6a1b9a class config,profile,http,response,fingerprint,mutations,state,qlearning,train,evaluate,builder,extractor pkg class target,qfile ext class agent,baseline cli ``` ## 训练流程 (RL Episode) 下图说明了一个完整的 episode: ``` flowchart TD Start([Episode start]) --> Init["payload = base_payload
state = encode_state(INIT)"] Init --> Loop{"step < 15?"} Loop -->|Yes| Choose["choose_action(state, ε)
epsilon-greedy"] Choose --> Mutate["mutated = MUTATIONS[action](payload)"] Mutate --> Send["send_request(target, mutated)"] Send --> Classify{"classify_response"} Classify -->|SUCCESS| Win["reward = +10
− step_penalty"] Classify -->|WAF_BLOCKED| Block["reward = −2
− step_penalty"] Classify -->|SQL_ERROR| Err["reward = +0.5
− step_penalty"] Classify -->|Other| Other["reward = −0.5 to −1.5
− step_penalty"] Win --> Update["update_Q(state, action, r, next_state)"] Block --> Update Err --> Update Other --> Update Update --> Success{"result == SUCCESS?"} Success -->|Yes| Done([Episode end
success=True]) Success -->|No| Advance["payload = mutated
state = next_state
step += 1"] Advance --> Loop Loop -->|No| Failed([Episode end
success=False]) Done --> Decay["ε = max(ε × 0.993, 0.05)"] Failed --> Decay Decay --> Log["append episode log"] classDef good fill:#c8e6c9,stroke:#2e7d32 classDef bad fill:#ffcdd2,stroke:#c62828 classDef neutral fill:#fff9c4,stroke:#f57f17 class Win,Done good class Block,Failed bad class Err,Other neutral ``` ## 序列图:单步执行 当 agent 做出一个决策时,幕后发生了什么: ``` sequenceDiagram autonumber participant T as train.py participant Q as Q-table participant M as mutations.py participant H as http.py participant W as Target+WAF participant R as response.py T->>Q: choose_action(state, ε) alt random < ε (explore) Q-->>T: random action from filter-aware pool else (exploit) Q-->>T: argmax_a Q(state, a) end T->>M: MUTATIONS[action](payload) Note over M: e.g. case_split:
UNION → uNiOn M-->>T: mutated payload T->>H: send_request(target, mutated) H->>H: _smart_url_encode(payload) Note over H: preserve %0a, %09 etc. H->>W: GET /Less-27/?id=... W-->>H: HTTP response H-->>T: (resp_text, status_code) T->>R: classify_response(resp_text, status) R-->>T: 'SUCCESS' | 'WAF_BLOCKED' | ... T->>T: get_reward(result, step) T->>Q: update_Q(state, action, r, next_state) Note over Q: Q(s,a) += α(r + γ·max Q(s',·) − Q(s,a)) ``` ## 📁 项目结构 ``` seqsqli-v2/ ├── agent.py # Entry point - CLI orchestration only (~190 lines) ├── baseline.py # Comparison: random, static, heuristic vs RL ├── README.md # ← you are here (English) ├── README.id.md # Bahasa Indonesia version │ └── seqsqli/ # Main package ├── __init__.py ├── config.py # All hyperparameters in one place ├── builder.py # TargetProfile factories + legacy compat ├── extractor.py # DataExtractor - dump DB after bypass │ ├── core/ # Domain logic (no RL) │ ├── profile.py # TargetProfile dataclass + sqli-labs presets │ ├── http.py # Smart URL encoding + session │ ├── response.py # classify_response (WAF/SUCCESS/ERROR) │ ├── fingerprint.py # Auto-detect quote, columns, filter type │ └── mutations.py # 30+ payload mutators + filter-aware hints │ └── rl/ # Reinforcement learning ├── state.py # encode_state - ϕ(p_t, h_t) ├── qlearning.py # Q-table, choose_action, update_Q, save/load ├── train.py # Training episode loop └── evaluate.py # evaluate + analyze_q_table + analyze_ordering ``` **关注点分离理念:** - **`core/`**,纯粹的领域逻辑,对 RL 一无所知。 - **`rl/`**,RL 组件,不了解 HTTP 或变异细节。 - **`agent.py`**,仅作为编排器,无业务逻辑。 想要修改超参数?编辑 `config.py`。添加新预设?编辑 `core/profile.py`。添加新变异?编辑 `core/mutations.py`。每项更改 都局限于一个文件中。 ## 安装 **前置条件**:Python 3.9+,sqli-labs 实验环境(本地或远程)。 ``` # Clone repo git clone git@github.com:robyfirnandoyusuf/SeqSQLi.git seqsqli cd seqsqli # 安装依赖(仅一个) pip install requests # (可选)验证 package python -c "from seqsqli.config import EPSILON; print(f'OK, ε={EPSILON}')" ``` ## 用法 ### 1. 训练 agent ``` # 自动 fingerprint + 训练(推荐用于新目标) python3 agent.py --less 27 --episodes 300 # 跳过 fingerprinting(使用预设;更快、确定性) python3 agent.py --less 27 --episodes 300 --no-fingerprint # 自定义 URL python3 agent.py --url "http://target/vuln.php" --param id --episodes 300 ``` 输出: - `q_table.json`,学习到的 Q 值(可在多次运行中复用) - `results_less27.json`,每个 episode 的日志 - `ordering_less27.json`,RQ3 顺序分析 ### 2. 基线对比(训练后运行) ``` python3 baseline.py --less 27 --episodes 50 --no-fingerprint ``` 对比 **5 种方法**:Random、Static Round-Robin、Single Heuristic、 Filter-Aware (~SSQLi) 和 RL Agent。输出: `comparison_less27.json` + `ordering_baseline_less27.json`。 ### 3. 数据提取(绕过后) ``` # 训练 + 提取 DB 内容 python3 agent.py --less 27 --episodes 300 --extract # 使用现有 Q-table 进行提取而无需重新训练 python3 agent.py --less 27 --extract --load --eval-only ``` ### 4. 实用标志 | 标志 | 用途 | |---|---| | `--episodes N` | 训练的 episode 数量(默认:300) | | `--load` | 加载之前的 Q-table(持续学习) | | `--eval-only` | 跳过训练,运行贪心策略 | | `--fingerprint` | 仅进行指纹识别,然后退出 | | `--no-fingerprint` | 跳过自动检测,直接使用预设 | | `--extract` | 绕过成功后转储数据库内容 | | `--all` | 跨所有预设进行训练(Less-1 到 Less-36) | ## 研究问题 | RQ | 问题 | 回答方式 | |---|---|---| | **RQ1** | RL 能否学习到有效的 WAF 绕过策略? | `agent.py` → 成功率曲线从约 0%(第 1 个 episode)上升到约 100%(第 200 个 episode) | | **RQ2** | RL 是否比静态方法更高效? | `baseline.py` → RL 对比 Random/Static/Heuristic | | **RQ3** | 变异**顺序**是否会影响绕过成功率? | `analyze_ordering()` → 4 项分析(首步、二元组、逆序对、位置敏感度) | 具体针对 RQ3,请参阅 `ordering_less27.json`,其中 **逆序对 对比**(`A→B` 对比 `B→A`)是主要证据。 ## 输出文件 | 文件 | 内容 | 用途 | |---|---|---| | `q_table.json` | (状态, 动作) → Q 值对 | 复用策略,持续学习 | | `results_less{N}.json` | 每 episode:序列、奖励、成功 | 绘制学习曲线 | | `ordering_less{N}.json` | 4 项排序分析 (RQ3) | 论文中的表格 | | `comparison_less{N}.json` | 5 种基线方法的性能 | RQ2,对比图表 | | `ordering_baseline_less{N}.json` | 贪心 RL agent 的排序分析 | 交叉验证 RQ3 | | `extract_less{N}.json` | 数据库转储(如果使用 `--extract`) | 端到端漏洞利用证据 | ## 初学者 RL 概念 如果你刚接触 RL,以下是代码/输出中使用的术语: - **Episode**:一次完整的绕过尝试,从开始到成功/失败。 - **步数**:一个 episode 中的一次变异(最多 15 次)。 - **Epsilon (ε)**:选择随机动作而非贪心动作的概率。 - 开始:ε=0.4(重度探索),结束:ε=0.05(主要是利用)。 - **Q 值 `Q(s,a)`**:在状态 `s` 下采取动作 `a` 的估计回报。 - **奖励**:每步的数值反馈(+10 成功,-2 被拦截等)。 - **SR (成功率)**:成功绕过的 episode 的百分比。 - **收敛**:SR 趋于稳定高值的阶段(通常在第 150-200 个 episode)。 **阅读训练输出:** ``` Ep 200 | eps=0.098 | SR=100% | Steps=2.0 | R=7.76 ↑ ↑ ↑ ↑ ↑ episode exploration success avg steps avg reward rate ``` ## ⚠️ 免责声明 本工具专为**学术研究**和**授权渗透 测试**而构建。在未经明确许可的情况下将其用于攻击系统是非法的, 且违反研究伦理。作者对任何滥用行为不承担责任。 推荐的测试环境: - [sqli-labs](https://github.com/Audi-1/sqli-labs)(本地 Docker) - [DVWA](https://github.com/digininja/DVWA) - 隔离的实验环境(用于学习) **灵感来源于:** - SSQLi:Sequential SQL Injection(主要参考文献) - Q-learning:Watkins & Dayan (1992) - sqli-labs:Audi-1 (https://github.com/Audi-1/sqli-labs)
hyperparameters] subgraph CORE["core/ - engines"] profile[profile.py
TargetProfile + Presets] http[http.py
HTTP + URL encoding] response[response.py
WAF/SQL classifier] fingerprint[fingerprint.py
auto-detect injection] mutations[mutations.py
30+ payload mutators] end subgraph RL["rl/ - learning"] state[state.py
encode_state ϕ] qlearning[qlearning.py
Q-table + update] train[train.py
training loop] evaluate[evaluate.py
RQ3 ordering analysis] end builder[builder.py
target factories] extractor[extractor.py
DB data dumper] end subgraph EXT["🌐 External"] target[(Target WAF
+ Web App)] qfile[(q_table.json)] end agent --> config agent --> fingerprint agent --> train agent --> evaluate agent --> extractor agent --> builder baseline --> evaluate baseline --> qlearning train --> qlearning train --> mutations train --> response train --> state train --> http evaluate --> qlearning fingerprint --> http fingerprint --> response extractor --> qlearning extractor --> mutations extractor --> http qlearning <--> qfile http <--> target classDef pkg fill:#e1f5ff,stroke:#0277bd classDef ext fill:#fff3e0,stroke:#e65100 classDef cli fill:#f3e5f5,stroke:#6a1b9a class config,profile,http,response,fingerprint,mutations,state,qlearning,train,evaluate,builder,extractor pkg class target,qfile ext class agent,baseline cli ``` ## 训练流程 (RL Episode) 下图说明了一个完整的 episode: ``` flowchart TD Start([Episode start]) --> Init["payload = base_payload
state = encode_state(INIT)"] Init --> Loop{"step < 15?"} Loop -->|Yes| Choose["choose_action(state, ε)
epsilon-greedy"] Choose --> Mutate["mutated = MUTATIONS[action](payload)"] Mutate --> Send["send_request(target, mutated)"] Send --> Classify{"classify_response"} Classify -->|SUCCESS| Win["reward = +10
− step_penalty"] Classify -->|WAF_BLOCKED| Block["reward = −2
− step_penalty"] Classify -->|SQL_ERROR| Err["reward = +0.5
− step_penalty"] Classify -->|Other| Other["reward = −0.5 to −1.5
− step_penalty"] Win --> Update["update_Q(state, action, r, next_state)"] Block --> Update Err --> Update Other --> Update Update --> Success{"result == SUCCESS?"} Success -->|Yes| Done([Episode end
success=True]) Success -->|No| Advance["payload = mutated
state = next_state
step += 1"] Advance --> Loop Loop -->|No| Failed([Episode end
success=False]) Done --> Decay["ε = max(ε × 0.993, 0.05)"] Failed --> Decay Decay --> Log["append episode log"] classDef good fill:#c8e6c9,stroke:#2e7d32 classDef bad fill:#ffcdd2,stroke:#c62828 classDef neutral fill:#fff9c4,stroke:#f57f17 class Win,Done good class Block,Failed bad class Err,Other neutral ``` ## 序列图:单步执行 当 agent 做出一个决策时,幕后发生了什么: ``` sequenceDiagram autonumber participant T as train.py participant Q as Q-table participant M as mutations.py participant H as http.py participant W as Target+WAF participant R as response.py T->>Q: choose_action(state, ε) alt random < ε (explore) Q-->>T: random action from filter-aware pool else (exploit) Q-->>T: argmax_a Q(state, a) end T->>M: MUTATIONS[action](payload) Note over M: e.g. case_split:
UNION → uNiOn M-->>T: mutated payload T->>H: send_request(target, mutated) H->>H: _smart_url_encode(payload) Note over H: preserve %0a, %09 etc. H->>W: GET /Less-27/?id=... W-->>H: HTTP response H-->>T: (resp_text, status_code) T->>R: classify_response(resp_text, status) R-->>T: 'SUCCESS' | 'WAF_BLOCKED' | ... T->>T: get_reward(result, step) T->>Q: update_Q(state, action, r, next_state) Note over Q: Q(s,a) += α(r + γ·max Q(s',·) − Q(s,a)) ``` ## 📁 项目结构 ``` seqsqli-v2/ ├── agent.py # Entry point - CLI orchestration only (~190 lines) ├── baseline.py # Comparison: random, static, heuristic vs RL ├── README.md # ← you are here (English) ├── README.id.md # Bahasa Indonesia version │ └── seqsqli/ # Main package ├── __init__.py ├── config.py # All hyperparameters in one place ├── builder.py # TargetProfile factories + legacy compat ├── extractor.py # DataExtractor - dump DB after bypass │ ├── core/ # Domain logic (no RL) │ ├── profile.py # TargetProfile dataclass + sqli-labs presets │ ├── http.py # Smart URL encoding + session │ ├── response.py # classify_response (WAF/SUCCESS/ERROR) │ ├── fingerprint.py # Auto-detect quote, columns, filter type │ └── mutations.py # 30+ payload mutators + filter-aware hints │ └── rl/ # Reinforcement learning ├── state.py # encode_state - ϕ(p_t, h_t) ├── qlearning.py # Q-table, choose_action, update_Q, save/load ├── train.py # Training episode loop └── evaluate.py # evaluate + analyze_q_table + analyze_ordering ``` **关注点分离理念:** - **`core/`**,纯粹的领域逻辑,对 RL 一无所知。 - **`rl/`**,RL 组件,不了解 HTTP 或变异细节。 - **`agent.py`**,仅作为编排器,无业务逻辑。 想要修改超参数?编辑 `config.py`。添加新预设?编辑 `core/profile.py`。添加新变异?编辑 `core/mutations.py`。每项更改 都局限于一个文件中。 ## 安装 **前置条件**:Python 3.9+,sqli-labs 实验环境(本地或远程)。 ``` # Clone repo git clone git@github.com:robyfirnandoyusuf/SeqSQLi.git seqsqli cd seqsqli # 安装依赖(仅一个) pip install requests # (可选)验证 package python -c "from seqsqli.config import EPSILON; print(f'OK, ε={EPSILON}')" ``` ## 用法 ### 1. 训练 agent ``` # 自动 fingerprint + 训练(推荐用于新目标) python3 agent.py --less 27 --episodes 300 # 跳过 fingerprinting(使用预设;更快、确定性) python3 agent.py --less 27 --episodes 300 --no-fingerprint # 自定义 URL python3 agent.py --url "http://target/vuln.php" --param id --episodes 300 ``` 输出: - `q_table.json`,学习到的 Q 值(可在多次运行中复用) - `results_less27.json`,每个 episode 的日志 - `ordering_less27.json`,RQ3 顺序分析 ### 2. 基线对比(训练后运行) ``` python3 baseline.py --less 27 --episodes 50 --no-fingerprint ``` 对比 **5 种方法**:Random、Static Round-Robin、Single Heuristic、 Filter-Aware (~SSQLi) 和 RL Agent。输出: `comparison_less27.json` + `ordering_baseline_less27.json`。 ### 3. 数据提取(绕过后) ``` # 训练 + 提取 DB 内容 python3 agent.py --less 27 --episodes 300 --extract # 使用现有 Q-table 进行提取而无需重新训练 python3 agent.py --less 27 --extract --load --eval-only ``` ### 4. 实用标志 | 标志 | 用途 | |---|---| | `--episodes N` | 训练的 episode 数量(默认:300) | | `--load` | 加载之前的 Q-table(持续学习) | | `--eval-only` | 跳过训练,运行贪心策略 | | `--fingerprint` | 仅进行指纹识别,然后退出 | | `--no-fingerprint` | 跳过自动检测,直接使用预设 | | `--extract` | 绕过成功后转储数据库内容 | | `--all` | 跨所有预设进行训练(Less-1 到 Less-36) | ## 研究问题 | RQ | 问题 | 回答方式 | |---|---|---| | **RQ1** | RL 能否学习到有效的 WAF 绕过策略? | `agent.py` → 成功率曲线从约 0%(第 1 个 episode)上升到约 100%(第 200 个 episode) | | **RQ2** | RL 是否比静态方法更高效? | `baseline.py` → RL 对比 Random/Static/Heuristic | | **RQ3** | 变异**顺序**是否会影响绕过成功率? | `analyze_ordering()` → 4 项分析(首步、二元组、逆序对、位置敏感度) | 具体针对 RQ3,请参阅 `ordering_less27.json`,其中 **逆序对 对比**(`A→B` 对比 `B→A`)是主要证据。 ## 输出文件 | 文件 | 内容 | 用途 | |---|---|---| | `q_table.json` | (状态, 动作) → Q 值对 | 复用策略,持续学习 | | `results_less{N}.json` | 每 episode:序列、奖励、成功 | 绘制学习曲线 | | `ordering_less{N}.json` | 4 项排序分析 (RQ3) | 论文中的表格 | | `comparison_less{N}.json` | 5 种基线方法的性能 | RQ2,对比图表 | | `ordering_baseline_less{N}.json` | 贪心 RL agent 的排序分析 | 交叉验证 RQ3 | | `extract_less{N}.json` | 数据库转储(如果使用 `--extract`) | 端到端漏洞利用证据 | ## 初学者 RL 概念 如果你刚接触 RL,以下是代码/输出中使用的术语: - **Episode**:一次完整的绕过尝试,从开始到成功/失败。 - **步数**:一个 episode 中的一次变异(最多 15 次)。 - **Epsilon (ε)**:选择随机动作而非贪心动作的概率。 - 开始:ε=0.4(重度探索),结束:ε=0.05(主要是利用)。 - **Q 值 `Q(s,a)`**:在状态 `s` 下采取动作 `a` 的估计回报。 - **奖励**:每步的数值反馈(+10 成功,-2 被拦截等)。 - **SR (成功率)**:成功绕过的 episode 的百分比。 - **收敛**:SR 趋于稳定高值的阶段(通常在第 150-200 个 episode)。 **阅读训练输出:** ``` Ep 200 | eps=0.098 | SR=100% | Steps=2.0 | R=7.76 ↑ ↑ ↑ ↑ ↑ episode exploration success avg steps avg reward rate ``` ## ⚠️ 免责声明 本工具专为**学术研究**和**授权渗透 测试**而构建。在未经明确许可的情况下将其用于攻击系统是非法的, 且违反研究伦理。作者对任何滥用行为不承担责任。 推荐的测试环境: - [sqli-labs](https://github.com/Audi-1/sqli-labs)(本地 Docker) - [DVWA](https://github.com/digininja/DVWA) - 隔离的实验环境(用于学习) **灵感来源于:** - SSQLi:Sequential SQL Injection(主要参考文献) - Q-learning:Watkins & Dayan (1992) - sqli-labs:Audi-1 (https://github.com/Audi-1/sqli-labs)
标签:AppImage, CISA项目, Payload变异, Python, Q-learning, WAF绕过, Web安全, Web应用防火墙, 学术研究, 强化学习, 数据展示, 无后门, 演示模式, 红队, 绕过技术, 网络安全, 自动化攻击, 蓝队分析, 逆向工具, 隐私保护, 顺序决策