taguianas/lab-secure-coding-authentication
GitHub: taguianas/lab-secure-coding-authentication
一份涵盖CSRF、认证绕过、JWT漏洞和IDOR等常见Web安全漏洞的实战学习笔记,结合DVWA和PortSwigger平台提供详细的攻击复现步骤和修复建议。
Stars: 0 | Forks: 0
   
# 🔐 Web 应用安全实验室 / Web 安全实验室
### 作者:Anas TAGUI
**工具:** DVWA · PortSwigger Web Security Academy · Burp Suite Community Edition
**主题:** CSRF · 认证 · 会话管理 · 访问控制 · IDOR · JWT
## 📋 目录
- [第 1 部分 – CSRF (DVWA)](#part-1--csrf-dvwa)
- [第 2 部分 – 认证与会话](#part-2--authentication--sessions)
- [第 3 部分 – 访问控制](#part-3--access-control)
- [关键要点 / 我学到的内容](#key-takeaways--ce-que-jai-retenu)
## 第 1 部分 – CSRF (DVWA)
### 这是什么问题?
**EN:** CSRF (Cross-Site Request Forgery) is one of those vulnerabilities that sounds complicated but is actually terrifying in how simple it is. The idea: trick a logged-in user's browser into making a request they never intended to make. The server sees a valid session cookie and just... does it.
**FR:** Le CSRF (Cross-Site Request Forgery) est une de ces vulnérabilités qui semble complexe mais qui est en réalité effrayante par sa simplicité. Le principe : tromper le navigateur d'un utilisateur connecté pour qu'il envoie une requête à son insu. Le serveur voit un cookie de session valide et... l'exécute.
### 🟢 级别:低
**EN:** At this level, there's literally zero protection. The password change happens over a GET request with the new password sitting right there in the URL. All I had to do was craft a link and get the victim to click it (or even just visit a page that auto-loads it).
**FR:** À ce niveau, il n'y a absolument aucune protection. Le changement de mot de passe se fait via une requête GET avec le nouveau mot de passe directement dans l'URL. Il suffit de créer un lien et de faire en sorte que la victime le visite.
```
http://localhost/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change
```
**结果:** 密码立即更改。没有点击,没有确认,什么都没有。
*Mot de passe changé instantanément. Aucun clic, aucune confirmation.*
### 🟡 级别:中
**EN:** This level tries to protect itself by checking the `Referer` header , basically asking "where did this request come from?" The catch? It uses `stripos()` to check if the word `localhost` appears *anywhere* in the URL. So I just named my attack file `localhost.html` and served it from my own machine. The check passed.
**FR:** Ce niveau tente de se protéger en vérifiant l'en-tête `Referer` , en demandant "d'où vient cette requête ?". Le problème ? Il utilise `stripos()` pour vérifier si le mot `localhost` apparaît *quelque part* dans l'URL. J'ai donc simplement nommé mon fichier d'attaque `localhost.html` et l'ai servi depuis ma machine. Le filtre est passé.
**攻击文件 (`localhost.html`):**
```
```
服务命令: `python3 -m http.server 8000`
### 🔴 级别:高
**EN:** Now we're talking. The app generates a random `user_token` on every page load. You can't guess it, you can't reuse an old one. A classic CSRF attack fails here... unless you combine it with XSS. The trick is to use JavaScript to silently fetch the CSRF page, steal the token from the HTML, then fire the forged request with the valid token included.
**FR:** Là ça se corse. L'application génère un `user_token` aléatoire à chaque chargement de page. Impossible à deviner, impossible à réutiliser. Une attaque CSRF classique échoue ici... sauf si on la combine avec du XSS. L'astuce : utiliser JavaScript pour récupérer silencieusement la page CSRF, extraire le token du HTML, puis envoyer la requête forgée avec ce token valide.
```
// Fetch the page to get the token / Récupérer la page pour obtenir le token
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost/vulnerabilities/csrf/", false);
xhr.send(null);
// Extract the token / Extraire le token
var parser = new DOMParser();
var doc = parser.parseFromString(xhr.responseText, "text/html");
var token = doc.getElementsByName('user_token')[0].value;
// Fire the forged request / Envoyer la requête forgée
var req = new XMLHttpRequest();
req.open("GET",
"http://localhost/vulnerabilities/csrf/?password_new=highhacked&password_conf=highhacked&Change=Change&user_token=" + token,
false
);
req.send(null);
```
## 第 2 部分 – 认证与会话
**平台:** PortSwigger Web Security Academy
**工具:** Burp Suite Community Edition
### 1. 通过不同响应进行用户名枚举
**实验:** [Username enumeration via different responses](https://portswigger.net/web-security/authentication/password-based/lab-username-enumeration-via-different-responses)
**EN:** The login page always returned HTTP 200 , no matter what username you tried. Looked safe. But the response *length* was slightly different when a username actually existed. That tiny difference was enough to enumerate valid accounts using Burp Intruder.
**FR:** La page de connexion retournait toujours un HTTP 200 , peu importe le nom d'utilisateur. Ça semblait sûr. Mais la *longueur* de la réponse était légèrement différente quand un nom d'utilisateur existait vraiment. Cette petite différence suffisait à énumérer les comptes valides avec Burp Intruder.
**步骤:**
1. 拦截 `POST /login` → 发送到 Intruder / *Intercepter `POST /login` → envoyer dans Intruder*
2. 使用词表对 `username` 字段进行 Fuzz / *Fuzzer le champ `username` avec la wordlist*
3. 按响应长度排序,`arkansas` 返回 `2986`,而其他返回 `2984` / *Trier par longueur , `arkansas` retourne `2986` contre `2984` pour les autres*
4. 对 `password` 运行相同的攻击 → `matthew` 返回 `302` 重定向 / *Même attaque sur `password` → `matthew` retourne une redirection `302`*
### 2. 2FA 简单绕过
**实验:** [2FA simple bypass](https://portswigger.net/web-security/authentication/multi-factor/lab-2fa-simple-bypass)
**EN:** This one was almost too easy and that's what makes it scary. The app has a two-step login: password first, then a 2FA code. But after entering the password, nothing stops you from just... skipping the 2FA page and navigating directly to `/my-account`. The server never checked whether step 2 was actually completed.
**FR:** Celle-là était presque trop facile , et c'est justement ce qui la rend inquiétante. L'app a une connexion en deux étapes : d'abord le mot de passe, puis un code 2FA. Mais après avoir entré le mot de passe, rien n'empêche de... sauter la page 2FA et naviguer directement vers `/my-account`. Le serveur ne vérifiait jamais si l'étape 2 avait été complétée.
**步骤:**
1. 以 `carlos / montoya` 登录 / *Se connecter en tant que `carlos / montoya`*
2. 被重定向到 `/login2` 输入 2FA 代码 / *Être redirigé vers `/login2` pour le code 2FA*
3. 在地址栏中手动输入 `/my-account` / *Taper manuellement `/my-account` dans la barre d'adresse*
4. 获得完全访问权限 / *Accès complet accordé*
### 3. 通过未验证签名绕过 JWT 认证
**实验:** [JWT authentication bypass via unverified signature](https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-unverified-signature)
**EN:** JWTs have three parts: header, payload, and a cryptographic signature that proves the token wasn't tampered with. This server decoded the payload and trusted it , but never actually verified the signature. So I just edited my username in the payload from `wiener` to `administrator`, left the signature untouched, and sent it. Admin access, no questions asked.
**FR:** Les JWT ont trois parties : en-tête, payload, et une signature cryptographique qui prouve que le token n'a pas été modifié. Ce serveur décodait le payload et lui faisait confiance , sans jamais vérifier la signature. J'ai donc modifié mon nom d'utilisateur de `wiener` à `administrator`, laissé la signature intacte, et envoyé le tout. Accès admin, sans aucune question.
**修改前:** `"sub": "wiener"`
**修改后:** `"sub": "administrator"`
## 第 3 部分 – 访问控制
**平台:** PortSwigger Web Security Academy
**EN:** This section was a reality check. Most of these vulnerabilities don't require any special tools or clever tricks , just the willingness to try things the developer assumed nobody would try.
**FR:** Cette section a été un vrai réveil. La plupart de ces vulnérabilités ne nécessitent aucun outil spécial ni astuce complexe , juste la volonté d'essayer des choses que le développeur supposait que personne ne tenterait.
### 1. 可在用户配置文件中修改用户角色
**实验:** [User role can be modified in user profile](https://portswigger.net/web-security/access-control/lab-user-role-can-be-modified-in-user-profile)
**漏洞:** Mass Assignment / 垂直权限提升
**EN:** The profile update endpoint accepted a JSON body. The UI only showed an email field. But I added `"roleid": 2` to the request body and the server happily processed it, upgrading my account to admin.
**FR:** L'endpoint de mise à jour du profil acceptait un corps JSON. L'interface n'affichait qu'un champ email. Mais j'ai ajouté `"roleid": 2` dans le corps de la requête et le serveur l'a traité sans broncher, me donnant les droits admin.
### 2. 用户 ID 由请求参数控制 (IDOR/GUID)
**实验:** [User ID controlled by request parameters](https://portswigger.net/web-security/access-control/lab-user-id-controlled-by-request-parameter-with-unpredictable-user-ids)
**EN:** The app used GUIDs instead of sequential IDs , a smart move. But those GUIDs were visible in the blog's author profile URLs. Once I had Carlos's GUID, I swapped it into my own account URL and accessed his profile, including his API key.
**FR:** L'app utilisait des GUIDs au lieu d'IDs séquentiels , une bonne idée. Mais ces GUIDs étaient visibles dans les URLs des profils d'auteurs du blog. Une fois le GUID de Carlos obtenu, je l'ai remplacé dans l'URL de mon propre compte et j'ai accédé à son profil, clé API incluse.
### 3. 未受保护的管理员功能
**实验:** [Unprotected admin functionality](https://portswigger.net/web-security/access-control/lab-unprotected-admin-functionality)
**EN:** The admin panel wasn't linked anywhere in the UI. The developers thought that was enough. It wasn't , the path `/administrator-panel` was sitting right there in `robots.txt`. Visiting the URL gave full admin access with zero authentication checks.
**FR:** Le panneau d'administration n'était lié nulle part dans l'interface. Les développeurs pensaient que c'était suffisant. Ce ne l'était pas , le chemin `/administrator-panel` était là, dans `robots.txt`. Visiter l'URL donnait un accès admin complet sans aucune vérification.
### 4. 不安全的直接对象引用 (静态文件上的 IDOR)
**实验:** [Insecure Direct Object References](https://portswigger.net/web-security/access-control/lab-insecure-direct-object-references)
**EN:** The chat transcript download used a sequential filename in the URL (`2.txt`, `1.txt`, etc.). No check was made to verify who the file belonged to. I changed `2.txt` to `1.txt` and downloaded Carlos's conversation , which contained his plaintext password.
**FR:** Le téléchargement des transcripts de chat utilisait un nom de fichier séquentiel dans l'URL (`2.txt`, `1.txt`, etc.). Aucune vérification n'était faite pour savoir à qui appartenait le fichier. J'ai changé `2.txt` en `1.txt` et téléchargé la conversation de Carlos , qui contenait son mot de passe en clair.
## 关键要点 / 我学到的内容
**EN:** After going through all of these, one pattern became very clear: most of these vulnerabilities aren't sophisticated. They exist because of small assumptions , "nobody will type that URL", "the response looks the same", "we're using GUIDs so we're fine". Security isn't about making things hard to find. It's about making sure that even if someone finds it, they still can't do anything with it.
**FR:** Après avoir traversé tout ça, un schéma est devenu très clair : la plupart de ces vulnérabilités ne sont pas sophistiquées. Elles existent à cause de petites suppositions , "personne ne tapera cette URL", "la réponse a l'air identique", "on utilise des GUIDs donc on est tranquilles". La sécurité ne consiste pas à rendre les choses difficiles à trouver. C'est s'assurer que même si quelqu'un les trouve, il ne peut rien en faire.
| 漏洞 / Vulnerability | 根本原因 / Root Cause | 修复 / Fix |
|---|---|---|
| CSRF (Low) | 无保护 / No protection | Anti-CSRF tokens + POST |
| CSRF (Medium | 弱 Referer 检查 / Weak Referer check | 严格的源站验证 / Strict origin validation |
| CSRF (High) | 通过 XSS 窃取 Token / Token stolen via XSS | 修复 XSS + SameSite cookies |
| Username Enumeration | 响应大小不同 / Different response sizes | 统一响应 + 速率限制 / Uniform responses + rate limiting |
| 2FA Bypass | 缺少状态检查 / Missing state check | 服务器端认证状态跟踪 / Server-side auth state tracking |
| JWT Bypass | 签名未验证 / Signature not verified | 始终验证签名 / Always verify signatures |
| Mass Assignment | 无字段过滤 / No field filtering | 服务器端允许列表 / Server-side allowlisting |
| IDOR / GUID | 无归属检查 / No ownership check | 服务器端授权 / Server-side authorization |
| Admin Obscurity | 无真正的访问控制 / No real access control | 对每个 endpoint 进行认证检查 / Auth checks on every endpoint |
| IDOR on Files | 顺序 ID,无认证 / Sequential IDs, no auth | 签名令牌 + 归属检查 / Signed tokens + ownership checks |
标签:Burp Suite, CISA项目, CSRF, DVWA, HTTP安全, IDOR, JWT绕过, PortSwigger, Streamlit, Web安全, 中文标签, 会话管理, 数据可视化, 漏洞分析, 网络安全实验室, 蓝队分析, 认证缺陷, 访问控制, 越权漏洞, 路径探测