我有一个非常简单的前端网站用例。它有一个联系人表格,联系人表格的详细信息需要保存到firebase数据库中进行进一步处理。该网站使用NextJS
构建。我知道NextJS
的api
功能在使用Firebase Hosting时不可用,因此,我倾向于使用Cloud Functions来设置一个HTTP端点,该端点接受表单数据作为POST请求,并将其保存到实时数据库/Firestore中。
然而,我无法找到一种方法来保护这个端点。如何防止普通用户从网站源代码中提取端点URL并向该URL发送多个请求?我可以让这个端点只对那个特定的域响应吗?或者我该如何解决这个问题?
或者,我可以直接在应用程序中使用Firebase SDK并将数据保存到数据库中,但这需要我将contacts
集合保持为公共集合,以便任何人进行读/写,这也是一个安全风险。
在保持安全的同时,解决这个问题的更好方法是什么?请注意,由于它是一个公共网站,我无法使用Firebase对用户进行身份验证。
这是不可能的。用户可以在"网络"选项卡中看到他们正在调用的所有URL。您的无服务器功能必须准备好处理垃圾邮件(如拒绝恶意或格式错误的请求(,尽管您仍将收取CPU使用费。这是无服务器的最大缺点之一。但你将节省大量时间设置服务器和所有这些麻烦。
你能做的最好的事情就是启用CORS,它仍然不会阻止垃圾邮件,但会在飞行前请求后拒绝请求。虽然只有浏览器遵循CORS,但API客户端(如邮递员或失眠(不遵循
这不能被视为安全威胁,因为这完全取决于代码的逻辑,但您将因使用而被收取费用,这就是所涉及的风险。有像Cloudflare API Shield这样的服务,但Firebase有自己的SDK,所以可以以某种方式绕过它。
谈到reCaptcha案例,它涉及在后端验证reCaptcha令牌,您可能会在一定程度上摆脱机器人。但是,如果有人在没有有效令牌的情况下不断向你的服务器发送垃圾邮件,你的功能仍然会向你收取验证令牌所需的时间。
如果你还有任何问题,请告诉我。
这两个选项都可以工作。
-
使用带有clloud函数的rest api,您可以集成Google Captcha。
-
直接使用数据库,你可以编写数据库规则,这样每个人都只能添加一个新联系人,而不能读取或编辑它。这仍然不太安全,因为有人可能会填满你的数据库。但有了相当好的字段验证和重复限制;较小的";问题
以下是我们如何使用我们的网站:
处理captcha
和联系POST
:的云功能
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const rp = require("request-promise");
const nodemailer = require("nodemailer");
const gmailEmail = encodeURIComponent(functions.config().gmail.email);
const gmailPassword = encodeURIComponent(functions.config().gmail.password);
const mailTransport = nodemailer.createTransport(
`smtps://${gmailEmail}:${gmailPassword}@smtp.gmail.com`
);
exports.checkRecaptcha = functions
.region("europe-west1")
.https.onRequest((req, res) => {
const response = req.query.response;
console.log("recaptcha response", response);
rp({
uri: "https://recaptcha.google.com/recaptcha/api/siteverify",
method: "POST",
formData: {
secret: "TOP_SECRET",
response: response,
},
json: true,
})
.then((result) => {
console.log("recaptcha result", result);
if (result.success) {
res.send("You're good to go, human.");
} else {
res.send("Recaptcha verification failed. Are you a robot?");
}
})
.catch((reason) => {
console.log("Recaptcha request failure", reason);
res.send("Recaptcha request failed.");
});
});
exports.triggerEmail = functions
.region("europe-west1")
.https.onRequest((req, res) => {
if (req.method !== "POST") {
res.status(400).send("Please send a POST request");
return;
}
const values = JSON.parse(req.body);
const email = "email@company.com";
const bcc = "email@company.com";
const mailOptions = {
subject: "Kontakt von Website",
text: `Datum: ${values.dateTime}n
Name: ${`${values.gender} ${values.firstname} ${values.lastname}`}
Firmenname: ${values.company ? values.company : ""}
Strasse: ${values.street ? values.street : ""}
Ort: ${values.place ? values.place : ""}
Land: ${values.country ? values.country : ""}
PLZ: ${values.zip ? values.zip : ""}
E-Mail: ${values.email ? values.email : ""}nn
${values.text ? values.text : ""}`,
to: email,
bcc: bcc,
};
console.log(req.headers.origin);
if (
req.headers.origin == "https://www.your_company.com" ||
req.headers.origin == "http://localhost:3000"
) {
res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
}
return mailTransport.sendMail(mailOptions).then(() => {
res.status(200).send("OK");
});
});
我们这边的Captcha使用了反应:
<ReCAPTCHA
ref='recaptcha'
sitekey='SECRET_KEY'
onChange={response => this.setState({ response: response })}
/>
与联系人POST
通话:
fetch('https://URL_TO_YOUR_FUNCTION_THAT_SENDS_THE_EMAIL', {
method: 'POST',
body: JSON.stringify({
dateTime: new Date().toString(),
gender: this.state.gender,
firstname: this.state.firstname,
lastname: this.state.lastname,
company: this.state.company,
street: this.state.street,
place: this.state.place,
country: this.state.country,
zip: this.state.zip,
email: this.state.email,
text: this.state.text,
isLogistik: isLogistik
})
})
确保您的Captcha设置已设置为我们的第二个云功能进行验证。