集成reCAPTCHA v3的React应用能否阻止爬虫获取未渲染内容
}
我的`index.js`代码如下: ```js import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import './i18n'; import reportWebVitals from './reportWebVitals'; import {GoogleReCaptchaProvider} from 'react-google-recaptcha-v3'; import {ValidationRecaptcha} from "./components/reCaptcha"; ReactDOM.render( <React.StrictMode> <GoogleReCaptchaProvider reCaptchaKey={process.env.REACT_APP_RECAPTCHA_PUBLIC_KEY}> <ValidationRecaptcha/> </GoogleReCaptchaProvider> </React.StrictMode>, document.getElementById('root') ); reportWebVitals();
以下是ValidationRecaptcha组件的代码:
import React, {useCallback, useEffect, useState} from "react"; import {useGoogleReCaptcha} from "react-google-recaptcha-v3"; import App from "../App"; import {PuffLoader} from "react-spinners"; export const ValidationRecaptcha = () => { const { executeRecaptcha } = useGoogleReCaptcha(); const [verified, setVerified] = useState(0); const handleReCaptchaVerify = useCallback(async () => { if (!executeRecaptcha) { console.log('Execute recaptcha not yet available'); return; } console.log('Request token from Google...'); const token = await executeRecaptcha('token'); console.log('Received token from Google: ' + token); function handleReCaptchaResponse(success) { setVerified(success ? 2 : 1); } // check the token with the backend const url = process.env.REACT_APP_BACKEND_API_URL + "/recaptcha?token=" + token.toString(); console.log('Request token verification from backend: ' + url); fetch(url) .then((res) => res.json()) .then((data) => handleReCaptchaResponse(JSON.parse(data.success))); console.log('Answer from API: success = ' + verified); }, [executeRecaptcha, verified]); // use useEffect to trigger the verification as soon as the component being loaded useEffect(() => { handleReCaptchaVerify(); }, [handleReCaptchaVerify]); let content; if (verified === 0) { content = <div><div className={'center'}><PuffLoader/></div><div className={'center-horizontally'}>Checking your browser with Google reCAPTCHA v3...</div></div>; } else if (verified === 1) { content = <div className={'center'}>Sorry, looks like you're a Robot 🤖. This site is humans-only!</div>; } else { content = <App/>; } return content; };
简要说明
代码在浏览器中运行符合预期,我已经测试了Chrome、Safari和Firefox浏览器。等待验证请求返回时,应用会渲染verified === 0对应的加载内容;如果验证不通过(token无效或者reCAPTCHA判定为爬虫),应用会渲染verified === 1的拦截内容;如果验证通过,就会渲染<App/>组件,包含所有功能、静态内容(比如备案信息里的邮箱、电话、地址)和联系表单。
我的问题
- 这个方案是否合理?爬虫(比如专门爬取网站信息的垃圾爬虫)能不能直接获取所有JSX文件,拿到
<App/>及其子组件里的个人数据比如邮箱、电话、地址? 我手动测试无效token或者低评分的情况时,Chrome的开发者工具里只能看到当前渲染的内容,搜索邮箱也没有结果,而这个邮箱是明文写在<App/>的子组件里的;只有验证通过的时候才能在源文件里搜索到邮箱内容。 - 我能不能认为React应用只会对外暴露运行时需要渲染的文件?
- 和第一个问题本质一致,就是确认我的认知:这个方案能不能保护深层的联系表单不被垃圾爬虫提交,还是说我在向SMTP服务发数据前还要再做一次校验?
我觉得这些问题是因为我对React的运行机制了解不够,如果有相关的入门资料也麻烦推荐。
祝好
maximotus
回答
问题1解答
这个方案完全不合理,垃圾爬虫完全可以拿到你所有的前端静态资源内容。
你在Chrome元素面板看不到未验证状态下的App组件内容,只是因为元素面板只展示当前已渲染到DOM树的内容,但你所有的React代码(包括App及其所有子组件内容)在执行npm run build后,都会被打包成一个或多个JS bundle静态文件。这些文件默认是公开可访问的,爬虫根本不需要走前端的reCAPTCHA验证逻辑,直接请求打包后的JS文件地址,就能拿到所有硬编码在代码里的邮箱、电话等信息。你可以在未验证的状态下直接访问你站点的打包后JS文件,直接搜索邮箱关键词,大概率能直接搜到对应内容。
问题2解答
绝对不能这么认为。
React是客户端渲染框架,所有打包后的静态资源默认都存放在web服务器的公开可访问目录下,和你前端渲染什么内容没有任何关系。前端的条件渲染只是控制内容要不要展示到DOM里,完全不会阻止静态资源本身被下载,只要爬虫能拿到资源链接,就能下载完整的bundle文件,解析出里面所有的明文内容。
问题3解答
不能,你必须在后端对所有表单提交请求做reCAPTCHA校验,前端的所有校验逻辑都可以被绕过。
不管你前端的verified状态是啥,爬虫完全可以跳过你的前端验证逻辑,直接往表单提交接口发请求。所以后端必须对每一次表单提交请求携带的reCAPTCHA token做校验,校验通过才允许调用SMTP发邮件,前端的任何拦截逻辑都只能防君子不防小人。
补充建议
如果你真的要保护硬编码的敏感静态内容,不要把这些内容写在前端代码里,应该放在后端,只有前端通过reCAPTCHA验证之后,再从后端接口请求这些敏感内容再渲染到页面上,这样爬虫就算拿到了前端bundle,也找不到硬编码的敏感信息。
如果要深入理解React的客户端渲染原理,可以看React官方文档的主概念部分,跟着基础教程走一遍就能搞清楚打包和运行的基本逻辑。
内容的提问来源于stack exchange,提问作者maximotus

