Học cách Twitter ngăn người dùng View Page Source (How Twitter prevents user from View Page Source)
Cập nhật gần nhất: 20/12/2024
Mặc dù hiện tại Twitter đã không còn dùng cách này nhưng mình tin là bài hôm nay sẽ đưa đến cho các bạn 1 số kiến thức khá hay đó 😉. Mà Twitter giờ cũng đổi tên thành X luôn rồi chớ 😂
Xin chào các bạn, lại là mình đây 👋👋.
Thời gian vừa rồi bận chuẩn bị cho cuộc sống mới nên chưa có thời gian chia sẻ tiếp về những điều mình học được cho các bạn. Mấy hôm nay ngồi voọc cái vấn đề này, vừa làm vừa nhẩm bụng phải cố làm đc lên chia sẻ cho ae trên này, hehe 😁
Bài này mình sẽ viết nó theo trình tự từ lúc mình gặp vấn đề và theo luồng suy nghĩ của mình nhé các bạn. Nếu có gì thắc mắc cứ comment nhiệt tình cho mình biết nha 😘
Sự vi diệu của Twitter
Mình có một thói quen đó là vì là Goét Đi ve lốp pơ, nên vào bất kì trang web nào mình hầu như đều xem source của nó, xem nó dùng công nghệ gì, có lộ tí gì cho ae copy được không 😆, kéo kéo trình duyệt tí xem có responsive không, trang nào responsive tốt là yêu ngay rồi ❤️, mình thích frontend mà 😁.
1 ngày nọ vào Twitter, đăng nhập (nhớ phải đăng nhập nhé) và như 1 thói quen click chuột phải và mở View Page Source trên trình duyệt, bùm 1 tab mới bật ra và chẳng hề có 1 cái gì gọi là có giá trị trên đó (Xem hình)
Không một file JS, không gọi API, mình thắc mắc: thế nội dung nó render từ đâu mà ra, những nội dung mình thấy, hình ảnh mình xem từ đâu render mà có, vẫn địa chỉ đó, KHÔNG CÓ 1 thứ gì ngoài vài dòng HTML "vớ vẩn"
Thử copy chỗ HTML kia về rồi chạy thử thì thấy trang reload liên tục. Xong, không hiểu tại sao 😵😵
Tìm kiếm trợ giúp từ Google
Tìm kiếm tập 1
Sau đó có lên Google tìm hiểu về điều này thì thấy chỉ có duy nhất 1 anh bạn cũng thắc mắc như mình tạo câu hỏi trên reddit, các bạn có thể xem tại đây Hầu hết mọi người đều băn khoăn không biết tại sao Twitter làm được thế. Có anh bạn còn comment thế này:
Đại loại là: vì code js đc lưu vào bộ nhớ trình duyệt nên Twitter xoá sạch thẻ script
đi mất rồi 🥹🥹. Comment thần thánh quá mình phải comment lại tôn làm GOD ngay bên dưới 🤣
Nhưng có 1 người phát hiện ra rằng Twitter dùng 1 trường tên là Referer để từ đó xác định xem nên trả về cho client nội dung như thế nào. Mình thử thì đúng thật, khi truy cập vào Twitter như bình thường thì có trường referer
gửi kèm header
trong request
, nhưng khi mở View Page Source thì không có. Dùng Postman cũng cho kết quả tương tự. Lại càng thêm thắc mắc.
Tìm kiếm tập 2
Sau đó mình lại quay lại Google tìm cách "prevent user from viewing source code", thì toàn các bạn chỉ cho cách ngăn user bấm chuột phải 🥹. Tự thắc mắc: nếu user không bấm chuột mà bấm tổ hợp phím thì ngăn sao? Override lại tổ hợp phím à? OK, thế nếu user dùng trình duyệt khác hoặc cuối cùng là họ dùng POSTMAN thì tính sao. ->> BẾ TẮC
Các bài học nhận ra
Đến đây mình đành phải tự lực cánh sinh, mò mẫm tìm hiểu. Mình xoáy sâu vào trường referer
trong header
của request
đã nói ở trên, cố gắng gán giá trị cho nó. Dùng JS các kiểu, lên server vào Nginx config trực tiếp cũng không thể nào thay đổi giá trị của nó được. Haizz... đến đây đành phải nghiêm túc lại học lý thuyết rồi 😜
Referer là gì
Sau đó mình mới đọc kĩ cố tìm hiểu thật kĩ về referer
thì mô tả ngắn gọn về nó như sau: nó là 1 trường với giá trị là trang web mà bạn đã truy cập ngay trước trang web hiện tại.
referer
có thể hiểu là: người tham chiếu - người đã giới thiệu bạn đến trang web của tôi là ai, ai giới thiệu chú đến chỗ chị 😂
Và referer
KHÔNG được tự thay đổi bởi user mà sẽ tự được gửi theo request trên trình duyệt (theo mình đã thử và search thì như thế 😊)
Khi mình mở 1 tab mới vào truy cập vào zing.vn
chẳng hạn, khi đó ta không bắt đầu từ trang nào trước đó, nên ta không có referer
, ta đi thẳng trực tiếp từ tab mới vào zing.vn
. Chỉ các resource(js, css, ...) được tải kèm là có trường referer
vì sau khi ta truy cập vào zing.vn
thì các resource mới được tải. Tiếp theo ta thử bấm vào 1 tin trên zing.vn
thì từ bây giờ ta sẽ có referer
ngay khi trang load lần đầu vì ta đã có người tham chiếu là trang trước đó rồi
Đến đây mình thử mở 1 tab mới, truy cập lại Twitter, thì thấy: Ồ có trường referer
ngay từ đầu luôn, vi diệu vậy ba 🤔🤔
Cách Twitter tải trang
Vì thấy Twitter có referer
ngay từ đầu, và referer
đó có giá trị là https://twitter.com
luôn:
Điều này chứng tỏ, khi chúng ta truy cập vào Twitter thì nó được tham chiếu bởi chính nó hay nói cách khác: đó là là địa chỉ trang web được truy cập ngay trước trang hiện tại là twitter.com
, nghĩa là chúng ta đã ở trang twitter.com
từ trước đó, sau đó ta lại redirect đến twitter.com
WTFFF, tức là mình truy cập 2 lần vào twitter.com
à? Nhưng mình đâu có thấy điều hướng gì đâu nhỉ?????
Thử để ý sâu hơn 1 chút nữa khi mở Twitter (không cần clear cache hay cookie gì nhé):
Nếu để ý kĩ, ngay tại thời điểm chúng ta truy cập vào Twitter.com từ tab mới, nếu để ý kĩ, ta sẽ thấy trình duyệt đã load 2 lần (không cần phải đến slow motion 😉). 1 khoảng thời gian rất ngắn ban đầu thôi, nhưng đủ để chúng ta thấy nếu để ý kĩ.
Từ đây mình rút ra rằng. Tại thời điểm ban đầu khi truy cập twitter.com
chúng ta (user) sẽ như sau:
- Ban đầu truy cập chưa có
referer
, trang sẽ redirect đến chính nó (phỏng đoán: chắc là dùng js để redirect cho dễ) - Sau đó lần sau có
referer
rồi thì load source thật vào rồi render nội dung thôi
Nghiền ngẫm giải pháp
Từ đây mình mới nhớ lại cái trang rỗng tuếch của Twitter mình nói ở đầu bài khi ta bấm View Page Source. Thử copy trang đó về và tự chạy, thấy trang load lại liên tùng tục đến khi nào browser nó chặn thì thôi, từ đó mình đoán ra vấn đề rất có thể từ đây. Trang này không hoàn toàn vô dụng như mình nghĩ từ đầu. Ban đầu còn tưởng nó làm trang đó để đánh lừa trông cho thần thánh chứ 🤪.
Nhưng điều băn khoăn lại nảy sinh, đó là mình làm thì bị reload liên tục, tại sao Twitter chỉ reload có 1 lần là ngon -> Phải có 1 cái gì đó kiểm tra, sau lần thứ nhất thì render nội dung được rồi, không cần load tiếp nữa, việc này chắc sẽ không cần đến backend vì đã cần query xử lý dữ liệu qué chỗ nào đâu. Chắc Webserver là thằng chịu trách nhiệm chính 🧐
Đến đây thì mình dặn lòng: thôi đành nghiền ngẫm thật kĩ lại cái trang "rỗng tuếch" kia xem từng thứ 1 trong đó là gì. Cùng nhìn lại:
Ở đây những thẻ như meta
hay đoạn css
không quan trọng, nghĩ thế nên mình không tập trung vào, còn lại những chỗ sau:
- Thẻ
noscript
: thẻ này sẽ chạy khi Javascript không được bật hoặc không hỗ trợ trên trình duyệt, khi đó Twitter sẽ bảo người dùng tự bấm vào cái link trong thẻa
để tự load lại trang (tự redirect bằng tay), nhằm mục đích lấyreferer
- Xuống thẻ
script
bên dưới có trườngnonce
, để giải thích ngắn gọn về trườngnonce
: trường này để ngăn chặn hacker tấn công XSS, bảo mật cho trang web của mình hơn, trường này cần là 1 mã băm đủ dài và cần phải khác nhau giữa mỗi lần tải trang, nếu không thì nó vô dụng. Đây là 1 trong rất nhiều cách bảo vệ trang web bằngContent-Security-Policy
, các bạn đọc kĩ hơn ở đây nhé. - Tiếp theo là đến câu lệnh tạo cookie: cookie này tên là
app_shell_visited
, ứng với route/
và có thời gian sống là 5 giây. Thực sự đến bây giờ mình vẫn không hiểu được là trường này dùng để làm gì, khi load lại check trong khoảng 5 giây cũng chẳng thấy nó đâu - Câu lệnh cuối cùng
location.replace(location.href.split("#")[0]);
, code JS chạy đến đây sẽ reload lại trình duyệt của chúng ta dựa vào địa chỉ trang web hiện tại. Tức là nếu mình đang ởtwitter.com
thì nó sẽ redirect đến cùng địa chỉtwitter.com
. Mà như thế là redirect xong ta sẽ córeferer
-> Chìa khoá đây rồi 😘
Demo
Chuẩn bị
Các bạn có thể tham khảo code demo của mình ở đây: https://github.com/maitrungduc1410/twitter-hacking
Giải thích:
- ở đây ta có 1 file
login.html
file này để login, và redirect người dùng về homepage nơi chứa nội dung chính index.html
: trang demo chính sau khi logindefault.html
: file này là file khi người dùng chưa córeferer
sẽ nhìn thấy, là file khi mà user chọn View Page Source sẽ thấy. Nội dung thì mình copy y như của Twitter luôn. Mình đã xoá đi dòng code set cookie bằng JS vì thấy nó không cần thiết
Luồng hoạt động là:
- Chưa login show
login.html
- Đã login mà không có Referer ở Header -> show
default.html
- Đã login và có Referer -> show
index.html
Phần chính chính là file server.js
, logics ở đó mình cũng để rất đơn giản thôi không có gì cao siêu hết.
Điều đặc biệt quan trọng cần chú ý là đoạn Disable caching
, ta cần phải nói với Browser là đừng có cache file index.html
nếu không khi mở View Page Source trình duyệt nó sẽ mặc định lấy từ cache của nó (cái này ta có thể debug bằng cách để console.log
trên server và ta sẽ không thấy nó chạy)
Sau khi login thì session đăng nhập của user được lưu trực tiếp vào memory luôn cho đơn giản, ở phía server ta gọi res.redirect("/")
sau khi login thành công để trả về trang chính, cũng làm refresh trình duyệt luôn (Referer sẽ tự động được set ở đoạn này)
Setup
Để chạy project cũng cực kì đơn giản.
Nếu ta có Docker thì chỉ cần chạy:
docker compose up -d
Hoặc nếu không có Docker thì cần NodeJS:
npm install
npm run dev
Sau đó là có thể truy cập được từ trình duyệt ở địa chỉ http://localhost:3000
Kết quả
Các bạn có thể xem kết quả mình đã hiện thực hoá ở đây nhé, demo này vẫn còn ở mức lởm lởm lắm ae xem thấy có gì không ổn comment cho mình nha: https://twitter-hacking.jamesisme.com
Thử đăng nhập với email bất kì cũng đc, sau đó View page source thử xem.
Sau đó tiếp lại thử bấm Logout và f5 lại xem nhé 😘
Đọc thêm
Giải pháp này của Twitter thực chất là để giải quyết một vấn đề về bảo mật danh tính của người dùng, các bạn có thể đọc thêm ở đây: https://blog.x.com/engineering/en_us/topics/insights/2018/twitter_silhouette
Về cơ bản vấn đề kĩ thuật này cho phép các trang web độc hại xác định danh tính người dùng đã đăng nhập trên các nền tảng trực tuyến, bao gồm cả Twitter, bằng cách đo thời gian tải trang (Ở trong bài họ gọi tên kĩ thuật này là Silhouette
)
Giải pháp của Twitter là: khi người dùng truy cập vào một trang hồ sơ (profile), Twitter sẽ thực hiện việc tải lại trang hai lần. Lần tải đầu tiên nhằm thiết lập các điều kiện cần thiết, và lần tải thứ hai đảm bảo rằng thời gian tải trang trở nên đồng nhất, bất kể nội dung hiển thị. Điều này ngăn chặn kẻ tấn công suy luận danh tính người dùng dựa trên sự khác biệt về thời gian tải trang.
Giải pháp này giúp bảo vệ danh tính người dùng, giảm thiểu nguy cơ bị lộ thông tin cá nhân khi truy cập các trang web không đáng tin cậy.
Sau khi mình viết xong bài này thì mới đọc được bài blog của Twitter. Cảm giác thật tình cờ làm sao hướng đi của mình khá giống với cách họ đã implement 😂😂
Kết luận
Sau 2 ngày to hết đầu vì cái này thì cuối cùng cũng có được 1 cái gì đó để tiếp tục chia sẻ với mọi người. Qua đây mình thấy việc bảo mật code sao cho càng làm khó các developer thì càng ngày càng được ưa chuộng, dù sao thì che giâú càng nhiều càng tốt phải không ☺️. Mặc dù làm thế này nhưng nếu user đăng nhập vào twitter, lấy cookie đó sau đó gửi bằng postman kèm referer (dùng postman thì gửi referer theo được) thì vẫn sẽ thấy source. Nên mình nghĩ cũng rất khó để ngăn chặn được hết các hành động của user trên trình duyệt 😊.
Đồng thời chúng ta cũng hiểu thêm được 1 cách tiếp cận mới mà ông lớn Twitter thực hiện (mình không dám chắn chắn phần đằng sau - server làm giống được như họ). Chúng ta cũng thấy rằng thế giới ngoài kinh thật 😝, nghĩ ra đủ thử mới cho mình luôn luôn phải học theo 😎😎.
Thôi thì cứ đành đi bòn góp kiến thức, tích luỹ dần dần để tăng khả năng của bản thân nhé các bạn đọc của tôi, hẹn gặp lại các bạn vào các bài sau 😘
Tại hạ cũng chỉ mong muốn giúp người, không yêu xin đừng nói lời cay đắng 🙏
All Rights Reserved