CSRF — Cross-Site Request Forgery và cách phòng thủ
CSRF là tấn công lợi dụng browser tự đính kèm cookie cross-site. Bài này bóc cơ chế Synchronizer Token Pattern, Double-Submit Cookie, SameSite cookie, và — quan trọng nhất — lý giải tại sao stateless JWT API tắt CSRF an toàn trong khi session-cookie app bắt buộc phải bật.
TL;DR: CSRF (Cross-Site Request Forgery) là tấn công lợi dụng hành vi mặc định của browser: tự đính kèm cookie với mọi request đến origin chứa cookie đó, kể cả request khởi phát từ trang khác. Phòng thủ gốc là Synchronizer Token Pattern — server nhúng token bí mật vào form, kẻ tấn công không đọc được vì same-origin policy. Stateless JWT API dùng Authorization: Bearer ... header — header không tự gửi cross-site, nên CSRF không có bề mặt tấn công và được tắt an toàn. Session-cookie app bắt buộc phải bật. SameSite cookie là lớp phòng thủ hiện đại bổ sung — chưa thay thế hoàn toàn CSRF token.
1. Kịch bản tấn công — tại sao CSRF nguy hiểm
Giả sử TaskFlow có endpoint chuyển quyền sở hữu task:
POST /api/tasks/42/transfer
Content-Type: application/x-www-form-urlencoded
newOwner=attacker
Server dùng session cookie để xác định user. Cookie đặt HttpOnly — JS không đọc được nhưng browser tự gửi kèm mọi request.
Kịch bản tấn công diễn ra như sau:
- Hien đăng nhập TaskFlow — cookie session được set trong browser.
- Hien nhận email phishing, click link tới
evil.com. - Trang
evil.comchứa form ẩn và script auto-submit:
<form id="f" action="https://taskflow.io/api/tasks/42/transfer" method="POST">
<input name="newOwner" value="attacker" />
</form>
<script>document.getElementById('f').submit()</script>
- Browser của Hien submit POST tới
taskflow.io— kèm session cookie của Hien vì cookie áp dụng theo origin đích, không theo trang phát request. - Server nhìn thấy cookie hợp lệ, xử lý transfer.
- Hien không hay biết gì — tab chớp nhoáng rồi redirect.
Same-origin policy không ngăn được điều này. SOP chỉ chặn chiều đọc response — evil.com không đọc được response từ taskflow.io. Nhưng SOP không chặn chiều gửi request: POST đã đến server và side-effect đã xảy ra trước khi browser kiểm response.
CORS và CSRF đều dính tới "cross-site" nhưng bản chất ngược nhau. CORS là browser policy chặn JS đọc response cross-origin. CSRF là tấn công dùng cross-site request để trigger side-effect — không cần đọc response, chỉ cần request đến nơi. Tắt CORS không ngăn được CSRF; bật CORS không tạo ra CSRF. Xem bảng so sánh ở bài CORS.
2. Cơ chế bên dưới — tại sao browser tự gửi cookie
Để hiểu vì sao CSRF tồn tại, phải hiểu cookie scoping: browser gắn cookie với origin đích của request, không phải origin của trang đang mở. Khi evil.com submit form tới taskflow.io, browser tra cookie jar theo key (scheme, host, port) = (https, taskflow.io, 443) và gắn mọi cookie match vào request — bất kể trang gửi request là ai.
Đây là thiết kế intentional từ thời web ban đầu: cho phép form HTML truyền thống hoạt động cross-site (mua hàng chuyển sang payment gateway, OAuth redirect...). Same-origin policy ra đời sau, chỉ chặn được chiều đọc response, không thể chặn chiều gửi request mà không phá vỡ toàn bộ web legacy.
sequenceDiagram
participant Hien as Browser (Hien)
participant Evil as evil.com
participant TF as taskflow.io
Hien->>TF: GET /dashboard (da co cookie session)
TF-->>Hien: 200 OK + cookie session
Hien->>Evil: GET /phishing (click link email)
Evil-->>Hien: HTML + auto-submit form
Note over Hien: Browser tu submit POST
Hien->>TF: POST /tasks/42/transfer (KEM cookie session)
TF-->>Hien: 200 OK (da transfer - side-effect xong)
Note over Evil,TF: evil.com khong doc duoc response<br/>(same-origin policy)<br/>nhung side-effect da xay ra roi3. Synchronizer Token Pattern — phòng thủ chính
Server sinh một token ngẫu nhiên, lưu vào session, rồi nhúng vào mọi form HTML. Mỗi request side-effect phải kèm token này trong body hoặc header. Server so sánh token nhận được với token đã lưu trong session.
<form action="/tasks/42/transfer" method="POST">
<input type="hidden" name="_csrf" value="f8a3c2d1-token-from-session" />
<input name="newOwner" value="..." />
<button>Transfer</button>
</form>
Tại sao evil.com không forge được: để submit form với token hợp lệ, kẻ tấn công phải đọc token từ HTML của taskflow.io — nhưng đó là cross-origin read, bị same-origin policy chặn tuyệt đối. Không đọc được token → không thể tạo request hợp lệ.
sequenceDiagram
participant Hien as Browser (Hien)
participant Evil as evil.com
participant TF as taskflow.io
Hien->>TF: GET /dashboard
TF-->>Hien: HTML + _csrf token (luu trong session)
Hien->>Evil: GET /phishing
Evil-->>Hien: Form POST taskflow.io (KHONG co _csrf)
Hien->>TF: POST /tasks/42/transfer (khong co _csrf)
TF-->>Hien: 403 Forbidden - CSRF token missing
Note over Evil: evil.com khong doc duoc _csrf token<br/>vi same-origin policy chan cross-origin readSpring Security bật Synchronizer Token mặc định. Config mặc định không cần thêm gì:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.formLogin(Customizer.withDefaults()) // CSRF tu dong bat kem
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
return http.build();
}
Thymeleaf tự inject token vào form qua ${_csrf.token}:
<form th:action="@{/tasks/42/transfer}" method="POST">
<input type="hidden" name="_csrf" th:value="${_csrf.token}" />
...
</form>
Token validate bởi CsrfFilter — filter này chạy sớm trong chain, trước AuthorizationFilter. Request thiếu token hoặc token sai nhận 403.
4. Double-Submit Cookie Pattern — cho stateful SPA
Khi SPA không muốn dùng server-side session nhưng vẫn cần cookie-based auth, Double-Submit Cookie là lựa chọn thay thế stateless hơn:
- Server set cookie chứa token random — không
HttpOnlyđể JS đọc được. - Frontend đọc giá trị cookie, copy vào header
X-XSRF-TOKENcủa mỗi request side-effect. - Server so sánh cookie value với header value — match thì xử lý.
Tại sao an toàn: evil.com không đọc được cookie của taskflow.io (same-origin policy). Khi evil.com submit form, browser gửi cookie tự động nhưng không set được header X-XSRF-TOKEN vì header phải do JS set explicit, mà JS của evil.com không đọc được cookie kia.
http.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// withHttpOnlyFalse: JS doc duoc cookie de copy sang header
);
| Pattern | Cần session? | Phù hợp cho |
|---|---|---|
| Synchronizer Token | Có (lưu token trong session) | Web app MVC truyền thống (Thymeleaf, JSP) |
| Double-Submit Cookie | Không | SPA với cookie-based JWT (HttpOnly JWT + CSRF cookie) |
5. SameSite cookie — phòng thủ tầng browser
RFC 6265bis định nghĩa thuộc tính SameSite kiểm soát khi nào browser gửi cookie kèm cross-site request:
| Giá trị | Browser gửi cookie khi... | CSRF protection |
|---|---|---|
Strict | Chỉ same-site request — không cross-site bất kỳ | Mạnh nhất — form submit cross-site không có cookie |
Lax | Same-site + top-level navigate GET (link click) | Chặn cross-site POST/PUT/DELETE |
None | Mọi request kể cả cross-site — bắt buộc kèm Secure | Không có — dùng cho SSO, embed |
Chrome 80+ (2020) đặt SameSite=Lax làm default cho cookie không khai báo. Điều này có nghĩa là form submit cross-site POST không có cookie → CSRF kinh điển bị chặn ở tầng browser, trước khi request đến server.
Config Spring Boot:
server:
servlet:
session:
cookie:
same-site: strict # hoac lax
secure: true
http-only: true
Hoặc set manual khi build cookie:
ResponseCookie cookie = ResponseCookie.from("SESSION", sessionId)
.httpOnly(true)
.secure(true)
.sameSite("Strict")
.maxAge(Duration.ofHours(1))
.build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
SameSite=Lax chưa thay thế hoàn toàn CSRF token vì:
- Browser cũ không support: IE11, Safari trước 12.1 có bug
Lax-by-default. - Flow SSO callback POST cross-site cần
SameSite=None— mất defense này. - GET với side-effect (anti-pattern nhưng vẫn tồn tại) vẫn vulnerable dưới
Lax. - Defense-in-depth: dùng cả SameSite lẫn CSRF token mạnh hơn chỉ một trong hai.
6. Khi nào tắt CSRF — stateless JWT API
http.csrf(csrf -> csrf.disable());
Đây là line code xuất hiện trong mọi cấu hình REST API Spring Security. Lý do nằm ở bản chất của credential:
Session cookie (cần CSRF): browser tự gửi cookie với mọi request đến origin chứa cookie. evil.com submit form tới taskflow.io → cookie đính kèm → server nhầm là user hợp lệ. CSRF có bề mặt tấn công.
JWT trong Authorization header (không cần CSRF): browser không tự gửi Authorization: Bearer ... header cross-site. Header này phải do JavaScript set explicit qua fetch() hoặc XMLHttpRequest. Để evil.com forge request với JWT, nó phải đọc JWT của Hien từ localStorage/memory — đó là XSS (tấn công khác), không phải CSRF.
// JWT API — CSRF disable an toan
fetch("https://taskflow.io/api/tasks/42/transfer", {
method: "POST",
headers: {
"Authorization": "Bearer eyJhbGc...", // phai set tu JS
"Content-Type": "application/json"
},
body: JSON.stringify({ newOwner: "attacker" })
});
evil.com không thể inject Authorization header vào request của Hien vì:
- Không đọc được JWT của Hien (nằm trong memory/localStorage của origin
taskflow.io). - Cross-origin JS không set
Authorizationheader trên request của origin khác.
Quy tắc rõ ràng:
| Loại app | Auth credential | CSRF |
|---|---|---|
| REST API stateless + JWT header | Authorization: Bearer ... | Tắt — không có bề mặt tấn công |
| Web app MVC + session cookie | Session cookie (auto-attach) | Bật — Spring default |
| SPA + HttpOnly cookie JWT | Cookie (auto-attach) | Bật — dùng CookieCsrfTokenRepository |
| App có cả admin form lẫn REST API | Cookie (admin) + JWT (API) | Hai chain: bật admin, tắt API |
Chi phí thấp nhưng overhead không đến từ computation — đến từ complexity: mọi client (mobile app, server-to-server, CLI tool) phải đọc và gửi kèm token. REST client như curl, Retrofit, Axios không tự xử lý CSRF. Thêm CSRF vào stateless API = thêm coupling không cần thiết. Khi credential là header-based, disable là đúng theo design, không phải shortcut.
7. CORS vs CSRF — bảng phân biệt
| Khía cạnh | CORS | CSRF |
|---|---|---|
| Bản chất | Browser security policy (defense) | Kỹ thuật tấn công (attack) |
| Chiều bảo vệ | Chặn JS đọc response cross-origin | Server defend incoming forged request |
| Trigger | Bật mặc định bởi browser (same-origin) | Attacker chủ động gửi request |
| Defense | Server whitelist origin qua header | Token validate per request, SameSite cookie |
| REST API + JWT header | Cần cấu hình CORS (SPA cross-origin) | Không cần CSRF |
| Web app + session cookie | Thường cùng origin — ít cần CORS | Cần CSRF (cookie auto-attach) |
| SPA + cookie-based JWT | Cần CORS + allowCredentials | Cần CSRF (CookieCsrfTokenRepository) |
Chi tiết cấu hình CORS trong Spring Security: xem bài CORS.
Pitfall
Pitfall 1 — Disable CSRF cho web app session-based:
// SAI: web app dung session cookie
http.csrf(csrf -> csrf.disable()); // CSRF surface van ton tai!
Lỗi này thường xảy ra khi copy config REST API sang web app MVC. Session cookie auto-attach → CSRF attack hoàn toàn khả thi. Giữ CSRF bật là Spring default — chỉ tắt khi có lý do rõ ràng (stateless JWT).
Pitfall 2 — SPA dùng HttpOnly cookie JWT nhưng quên CSRF:
// SAI: JWT luu trong HttpOnly cookie, khong co CSRF config
http
.csrf(csrf -> csrf.disable()) // SAI cho cookie-based JWT
.oauth2ResourceServer(oauth2 -> oauth2.jwt(...));
JWT trong HttpOnly cookie auto-attach cross-site — CSRF surface tồn tại. Phải dùng CookieCsrfTokenRepository:
// DUNG:
http.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers("/api/auth/login") // login chua co session
);
Pitfall 3 — Đặt SameSite=None không kèm Secure:
Chrome 80+ reject cookie SameSite=None không có Secure — cookie không được set. Luôn cặp SameSite=None; Secure.
Pitfall 4 — Tưởng CORS config là đủ để chặn CSRF:
CORS chặn đọc response, không chặn request gửi đi. Tắt CORS không ngăn CSRF. Bật CORS không tạo ra CSRF. Hai cơ chế độc lập hoàn toàn.
Cơ chế bên dưới — CsrfFilter trong chain
Spring Security đặt CsrfFilter ở vị trí thứ 5 trong chain mặc định — sau HeaderWriterFilter, trước LogoutFilter. Filter này:
- Với GET/HEAD/OPTIONS/TRACE (safe methods): không kiểm CSRF token, chỉ load token vào request attribute để template render.
- Với POST/PUT/DELETE/PATCH (unsafe methods): đọc token từ request (body param
_csrfhoặc headerX-XSRF-TOKEN), so sánh với token đã lưu. Nếu mismatch, filter némInvalidCsrfTokenExceptionvà response là 403.
flowchart TB
REQ["Incoming request"] --> SAFE{"Safe method?<br/>(GET HEAD OPTIONS TRACE)"}
SAFE -->|"Ya"| LOAD["Load CSRF token<br/>into request attribute<br/>(template render)"]
LOAD --> PASS["Pass to next filter"]
SAFE -->|"Khong"| READ["Doc token tu request<br/>(body _csrf hoac header X-XSRF-TOKEN)"]
READ --> MATCH{"Match token<br/>trong session?"}
MATCH -->|"Khop"| PASS
MATCH -->|"Sai / Thieu"| ERR["InvalidCsrfTokenException<br/>-> 403 Forbidden"]HttpSessionCsrfTokenRepository (default) lưu token trong HttpSession với key "org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN". Token là UUID random 36 ký tự, đủ entropy để brute-force bất khả thi.
XorCsrfTokenRequestAttributeHandler (Spring Security 6 recommend) XOR token gốc với mask random trước khi nhúng vào response — giá trị byte khác nhau mỗi lần render, chống BREACH attack (compression oracle nhìn thấy token cố định trong gzip response).
📚 Deep Dive
- OWASP CSRF Prevention Cheat Sheet — standard reference đầy đủ nhất
- RFC 6265bis — Cookie SameSite attribute — spec SameSite chính thức
- Spring Security — CSRF — tài liệu Spring chính chủ, có BREACH mitigation
- Spring Security — CsrfFilter source — đọc
requiresProtection()để thấy whitelist safe methods
Liên hệ các bài khác
- CORS: CORS và CSRF đều dùng khái niệm "cross-origin/cross-site" nhưng bản chất ngược nhau — đọc cả hai để thấy tại sao cấu hình một cái không ảnh hưởng cái kia.
- Mini-challenge TaskFlow v3: bài capstone áp dụng toàn bộ cấu hình security cho TaskFlow — JWT stateless + CORS + CSRF disable cùng một
SecurityFilterChain. - Authorization — URL rules và method security: CSRF filter chạy trước
AuthorizationFiltertrong chain — hiểu thứ tự filter giúp debug tại sao 403 đến từ CSRF chứ không phải authz.
Tóm tắt
- CSRF lợi dụng browser tự đính kèm cookie với request đến origin chứa cookie, kể cả request từ trang khác. Same-origin policy không ngăn được vì chỉ chặn đọc response, không chặn gửi request.
- Synchronizer Token Pattern: server lưu token trong session, nhúng vào form. Attacker không forge được vì không đọc được token cross-origin.
- Double-Submit Cookie: stateless alternative — token trong cookie không-HttpOnly, JS copy sang header
X-XSRF-TOKEN. Phù hợp cho SPA + cookie-based JWT. - SameSite cookie:
StricthoặcLaxchặn CSRF ở tầng browser. Defense-in-depth tốt nhưng chưa thay thế hoàn toàn token vì browser cũ và edge case SSO. - Stateless JWT API: disable CSRF an toàn vì
Authorization: Bearer ...header không tự gửi cross-site — không có bề mặt tấn công. - Session-cookie app: luôn bật CSRF (Spring default). Tắt nhầm = vulnerability.
- HttpOnly cookie JWT: bật CSRF với
CookieCsrfTokenRepository— cookie auto-attach nên CSRF surface tồn tại. CsrfFilterchạy trướcAuthorizationFilter— thiếu token trả 403 từ CSRF trước khi tới authz logic.
Tự kiểm tra
Q1Giải thích tại sao same-origin policy không ngăn được CSRF. Trong kịch bản evil.com submit form tới taskflow.io, điều gì browser cho phép và điều gì bị chặn?▸
evil.com submit form tới taskflow.io, điều gì browser cho phép và điều gì bị chặn?Same-origin policy chặn JavaScript của một origin đọc response từ origin khác — nhưng nó không chặn request gửi đi. Đây là thiết kế intentional từ thời web ban đầu để form HTML cross-site vẫn hoạt động.
Trong kịch bản CSRF: evil.com submit form POST tới taskflow.io. Browser cho phép request đi vì đây là behavior legacy; đồng thời tự đính kèm cookie của taskflow.io vì cookie được scope theo origin đích, không origin của trang gửi. Server nhận được request kèm cookie hợp lệ → xử lý side-effect (chuyển quyền, xoá data, v.v.).
Same-origin policy chặn evil.com đọc response trả về từ taskflow.io — nhưng attacker không cần đọc response. Side-effect đã xảy ra trước khi browser kiểm tra. Đó là lý do CSRF nguy hiểm ngay cả khi CORS được cấu hình đúng.
Q2Tại sao http.csrf(csrf -> csrf.disable()) an toàn cho REST API dùng Authorization: Bearer ... header, nhưng không an toàn cho web app dùng session cookie? Giải thích theo cơ chế browser.▸
http.csrf(csrf -> csrf.disable()) an toàn cho REST API dùng Authorization: Bearer ... header, nhưng không an toàn cho web app dùng session cookie? Giải thích theo cơ chế browser.JWT Authorization header: browser không tự gửi Authorization header với bất kỳ request nào — header này phải do JavaScript set explicit qua fetch() hoặc XMLHttpRequest. Để forge request có JWT, evil.com phải đọc JWT của nạn nhân từ localStorage/memory của taskflow.io — đó là XSS, một vector tấn công hoàn toàn khác. CSRF impossible by design khi credential là header-based.
Session cookie: browser tự đính kèm cookie với mọi request đến origin chứa cookie, bất kể trang nào gửi request. evil.com submit form tới taskflow.io → browser gắn session cookie tự động → server hiểu nhầm là user hợp lệ. Disable CSRF ở đây = mở cửa cho tấn công.
Quy tắc thực tế: nếu credential được gửi bởi browser tự động (cookie, HTTP Basic) → cần CSRF. Nếu credential được gửi bởi JS explicit (Authorization header) → không cần CSRF.
Q3Synchronizer Token Pattern và Double-Submit Cookie Pattern khác nhau như thế nào? Khi nào chọn cái nào?▸
Synchronizer Token Pattern: server sinh token random, lưu vào server-side session, nhúng vào form HTML. Mỗi POST/PUT/DELETE phải kèm token trong body. Server so sánh token body với token trong session — mismatch → 403. Cần HttpSession tồn tại server-side.
Double-Submit Cookie: server set cookie chứa token, không HttpOnly để JS đọc được. Frontend copy giá trị cookie sang header X-XSRF-TOKEN. Server so sánh cookie value với header value. Stateless — không cần session lưu token.
Bảo mật: Synchronizer mạnh hơn vì token chỉ tồn tại server-side. Double-Submit dễ bị tấn công nếu subdomain của cùng site có lỗ XSS (XSS có thể đặt cookie root domain → forge token header). Trong môi trường kiểm soát tốt, cả hai đủ an toàn.
Khi nào chọn: web app MVC truyền thống (Thymeleaf, JSP) → Synchronizer Token (Spring default). SPA với cookie-based JWT cần stateless → Double-Submit Cookie (CookieCsrfTokenRepository.withHttpOnlyFalse()). REST API JWT header → disable CSRF hoàn toàn.
Q4SameSite=Lax là default của Chrome 80+ từ năm 2020. Điều đó có nghĩa là CSRF token không còn cần thiết nữa không? Liệt kê 3 tình huống SameSite=Lax không đủ bảo vệ.▸
SameSite=Lax là default của Chrome 80+ từ năm 2020. Điều đó có nghĩa là CSRF token không còn cần thiết nữa không? Liệt kê 3 tình huống SameSite=Lax không đủ bảo vệ.Không. SameSite=Lax là lớp phòng thủ thêm, không phải thay thế hoàn toàn CSRF token.
Tình huống 1 — GET với side-effect: SameSite=Lax cho phép cookie đi kèm với top-level navigation GET (user click link). Nếu app có endpoint GET gây side-effect (anti-pattern nhưng tồn tại — vd /api/tasks/42/delete?confirm=yes), CSRF vẫn khả thi qua link trên trang attacker.
Tình huống 2 — SSO callback POST cross-site: nhiều IdP gửi SAML response hoặc OAuth callback qua POST cross-site. Để flow này hoạt động, cookie phải là SameSite=None — mất hoàn toàn SameSite protection. App phải dùng CSRF token để bù.
Tình huống 3 — Browser cũ: IE11 không support SameSite. Safari trước 12.1 có bug treat SameSite=None như SameSite=Strict. Một số Android WebView cũ ignore attribute. Với app cần support rộng, SameSite không thể là layer duy nhất.
Recommend: dùng cả SameSite=Strict/Lax lẫn CSRF token (defense-in-depth). SameSite chặn 90% CSRF ở tầng browser; token bắt phần còn lại và browser legacy.
Q5TaskFlow v3 dùng JWT stateless. Một junior dev thêm csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) vào config vì "cho an toàn hơn". Điều này có đúng không? Hệ quả là gì nếu app thực sự stateless JWT header?▸
csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) vào config vì "cho an toàn hơn". Điều này có đúng không? Hệ quả là gì nếu app thực sự stateless JWT header?Không đúng. Thêm CSRF token vào stateless JWT API không làm app an toàn hơn mà tạo ra overhead không cần thiết và có thể gây lỗi.
Về bảo mật: JWT trong Authorization header đã không có bề mặt tấn công CSRF — browser không auto-attach header này. Thêm CSRF token không ngăn được vector tấn công nào thêm vì vector đó không tồn tại.
Hệ quả thực tế: mọi client gọi API (mobile app, curl, Retrofit, Axios) phải implement logic đọc CSRF cookie và gửi kèm header X-XSRF-TOKEN. Client không làm điều này → mọi request POST/PUT/DELETE trả 403. Đặc biệt server-to-server API call sẽ fail hoàn toàn.
Cơ sở quyết định: câu hỏi đúng không phải "CSRF token có làm hại không?" mà là "credential có bị browser auto-attach không?". Nếu không (JWT header) → disable. Nếu có (cookie) → bật CSRF tương ứng. Thêm layer security không target đúng vector = complexity không có value.
Bài tiếp theo: Mini-challenge — bảo vệ TaskFlow với JWT + role
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên