随应用分发 Google API 凭据是否安全?
Is it safe to distribute Google API credentials with apps?
我正在尝试制作一个简单的桌面电子邮件 reader(带电子),它在可以翻阅的小卡片上显示用户的电子邮件。没什么,这就是为什么我不想为它设置一个完整的远程服务器系统。我按照 Google's NodeJS Quickstart guide 中的说明开始了。这包括将我的 Google API 凭据保存到应用程序中的文件中。登录后,令牌将保存到磁盘。如果不存在这样的令牌,它将在浏览器中打开一个登录页面,重定向到 127.0.0.1:3000/authorize
(express 应用程序 运行 保存令牌)。它可以工作,不需要远程服务器,这正是我想要的。
我的问题是,使用我的应用分发 credentials.json
文件(包含 client_id、client_secret、project_id)是否安全?潜在的安全问题是什么?如果这不合适,让我的应用程序可以安全分发的最简单的替代方法是什么?
编辑
我查看了 Google 的文档并找到了 this。
The process results in a client ID and, in some cases, a client
secret, which you embed in the source code of your application. (In
this context, the client secret is obviously not treated as a secret.)
所以 client_secret 在这种情况下不是秘密,对吧? credentials.json
的其余部分呢?有人可以冒充我的应用并使用该信息做坏事吗?
这是首次登录的代码(有效):
function getNewToken(oAuth2Client, callback, method, args) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
require('electron').shell.openExternal(authUrl);
// mini server for authorization
const express = require('express')()
//express part
express.get('/authorize', function (req, res) {
oAuth2Client.getToken(req.query.code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
method(oAuth2Client, callback, args);
});
res.sendFile(path.join(__dirname, 'authorize.html'));
});
express.listen(3000, () => {
console.log(`Example app listening at http://localhost:3000`)
})
}
如果保存了令牌,我用来访问 API 的函数:
function ApiCall(method, callback, args) {
fs.readFile('credentials.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Gmail API.
var credentials = JSON.parse(content);
var {client_secret, client_id, redirect_uris} = credentials.web;
var oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getNewToken(oAuth2Client, callback, method, args);
oAuth2Client.setCredentials(JSON.parse(token));
method(oAuth2Client, callback, args);
});
});
}
以这种方式分发客户端机密是不安全的,因为它很容易被攻击者从您的应用中泄露出来。客户端 ID 可以安全分发,仅靠它本身不足以进行攻击。您不需要项目 ID 进行授权。
您要实施的流程在 oAuth 中称为授权代码授予类型。您还需要实施 PKCE extension. 总之,授权代码 + PKCE 是在最终用户设备上的本机应用 运行 中使用 oAuth 的推荐方法。
Google documents 没有 PKCE 的授权代码作为使用 OpenId Connect 的“服务器流”(有点用词不当)。后者是 oAuth 的超集,以上内容仍然适用:
- Create an anti-forgery state token
- Send an authentication request to Google
- Confirm the anti-forgery state token
- Exchange code for access
token and ID token
- Obtain user information from the ID token
- Authenticate the user
您的申请中可能不需要第 5-6 步。
您可以尝试在 JS 中滚动第 1-4 步 google-api-nodejs-client or you can give oidc-client-js 一次。
正在查看另一个项目的 google oauth github 存储库,发现了这个:
If you're authenticating with OAuth2 from an installed application
(like Electron), you may not want to embed your client_secret inside
of the application sources. To work around this restriction, you can
choose the iOS application type when creating your OAuth2 credentials
in the Google Developers console:
https://github.com/googleapis/google-auth-library-nodejs#oauth2-with-installed-apps-electron
不确定如何防止其他应用以相同方式使用您的客户端 ID,但这是他们的建议。
我正在尝试制作一个简单的桌面电子邮件 reader(带电子),它在可以翻阅的小卡片上显示用户的电子邮件。没什么,这就是为什么我不想为它设置一个完整的远程服务器系统。我按照 Google's NodeJS Quickstart guide 中的说明开始了。这包括将我的 Google API 凭据保存到应用程序中的文件中。登录后,令牌将保存到磁盘。如果不存在这样的令牌,它将在浏览器中打开一个登录页面,重定向到 127.0.0.1:3000/authorize
(express 应用程序 运行 保存令牌)。它可以工作,不需要远程服务器,这正是我想要的。
我的问题是,使用我的应用分发 credentials.json
文件(包含 client_id、client_secret、project_id)是否安全?潜在的安全问题是什么?如果这不合适,让我的应用程序可以安全分发的最简单的替代方法是什么?
编辑
我查看了 Google 的文档并找到了 this。
The process results in a client ID and, in some cases, a client secret, which you embed in the source code of your application. (In this context, the client secret is obviously not treated as a secret.)
所以 client_secret 在这种情况下不是秘密,对吧? credentials.json
的其余部分呢?有人可以冒充我的应用并使用该信息做坏事吗?
这是首次登录的代码(有效):
function getNewToken(oAuth2Client, callback, method, args) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
require('electron').shell.openExternal(authUrl);
// mini server for authorization
const express = require('express')()
//express part
express.get('/authorize', function (req, res) {
oAuth2Client.getToken(req.query.code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
method(oAuth2Client, callback, args);
});
res.sendFile(path.join(__dirname, 'authorize.html'));
});
express.listen(3000, () => {
console.log(`Example app listening at http://localhost:3000`)
})
}
如果保存了令牌,我用来访问 API 的函数:
function ApiCall(method, callback, args) {
fs.readFile('credentials.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Gmail API.
var credentials = JSON.parse(content);
var {client_secret, client_id, redirect_uris} = credentials.web;
var oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getNewToken(oAuth2Client, callback, method, args);
oAuth2Client.setCredentials(JSON.parse(token));
method(oAuth2Client, callback, args);
});
});
}
以这种方式分发客户端机密是不安全的,因为它很容易被攻击者从您的应用中泄露出来。客户端 ID 可以安全分发,仅靠它本身不足以进行攻击。您不需要项目 ID 进行授权。
您要实施的流程在 oAuth 中称为授权代码授予类型。您还需要实施 PKCE extension. 总之,授权代码 + PKCE 是在最终用户设备上的本机应用 运行 中使用 oAuth 的推荐方法。
Google documents 没有 PKCE 的授权代码作为使用 OpenId Connect 的“服务器流”(有点用词不当)。后者是 oAuth 的超集,以上内容仍然适用:
- Create an anti-forgery state token
- Send an authentication request to Google
- Confirm the anti-forgery state token
- Exchange code for access token and ID token
- Obtain user information from the ID token
- Authenticate the user
您的申请中可能不需要第 5-6 步。
您可以尝试在 JS 中滚动第 1-4 步 google-api-nodejs-client or you can give oidc-client-js 一次。
正在查看另一个项目的 google oauth github 存储库,发现了这个:
If you're authenticating with OAuth2 from an installed application (like Electron), you may not want to embed your client_secret inside of the application sources. To work around this restriction, you can choose the iOS application type when creating your OAuth2 credentials in the Google Developers console:
https://github.com/googleapis/google-auth-library-nodejs#oauth2-with-installed-apps-electron
不确定如何防止其他应用以相同方式使用您的客户端 ID,但这是他们的建议。