The Firebase Web-App guide states I should put the given apiKey in my Html to initialize Firebase:
// TODO: Replace with your project's customized code snippet
<script src="https://www.gstatic.com/firebasejs/3.0.2/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: '<your-api-key>',
authDomain: '<your-auth-domain>',
databaseURL: '<your-database-url>',
storageBucket: '<your-storage-bucket>'
};
firebase.initializeApp(config);
</script>
By doing so, the apiKey is exposed to every visitor.
What is the purpose of that key and is it really meant to be public?
The apiKey in this configuration snippet just identifies your Firebase project on the Google servers. It is not a security risk for someone to know it. In fact, it is necessary for them to know it, in order for them to interact with your Firebase project. This same configuration data is also included in every iOS and Android app that uses Firebase as its backend.
In that sense it is very similar to the database URL that identifies the back-end database associated with your project in the same snippet: https://<app-id>.firebaseio.com. See this question on why this is not a security risk: How to restrict Firebase data modification?, including the use of Firebase's server side security rules to ensure only authorized users can access the backend services.
If you want to learn how to secure all data access to your Firebase backend services is authorized, read up on the documentation on Firebase security rules. These rules control access to file storage and database access, and are enforced on the Firebase servers. So no matter if it's your code, or somebody else's code that uses you configuration data, it can only do what the security rules allow it to do.
For another explanation of what Firebase uses these values for, and for which of them you can set quotas, see the Firebase documentation on using and managing API keys.
If you'd like to reduce the risk of committing this configuration data to version control, consider using the SDK auto-configuration of Firebase Hosting. While the keys will still end up in the browser in the same format, they won't be hard-coded into your code anymore with that.
Update (May 2021): Thanks to the new feature called Firebase App Check, it is now actually possible to limit access to the backend services in your Firebase project to only those coming from iOS, Android and Web apps that are registered in that specific project.
You'll typically want to combine this with the user authentication based security described above, so that you have another shield against abusive users that do use your app.
By combining App Check with security rules you have both broad protection against abuse, and fine gained control over what data each user can access, while still allowing direct access to the database from your client-side application code.
Building on the answers of prufrofro and Frank van Puffelen here, I put together this setup that doesn't prevent scraping, but can make it slightly harder to use your API key.
Warning: To get your data, even with this method, one can for example simply open the JS console in Chrome and type:
firebase.database().ref("/get/all/the/data").once("value", function (data) {
console.log(data.val());
});
Only the database security rules can protect your data.
Nevertheless, I restricted my production API key use to my domain name like this:
https://console.developers.google.com/apis
Select your Firebase project
Credentials
Under API keys, pick your Browser key. It should look like this: "Browser key (auto created by Google Service)"
In "Accept requests from these
HTTP referrers (web sites)", add the URL of your app (exemple: projectname.firebaseapp.com/* )
Now the app will only work on this specific domain name. So I created another API Key that will be private for localhost developement.
Click Create credentials > API Key
By default, as mentioned by Emmanuel Campos, Firebase only whitelists localhost and your Firebase hosting domain.
In order to make sure I don't publish the wrong API key by mistake, I use one of the following methods to automatically use the more restricted one in production.
Setup for Create-React-App
In /env.development:
REACT_APP_API_KEY=###dev-key###
In /env.production:
REACT_APP_API_KEY=###public-key###
In /src/index.js
const firebaseConfig = {
apiKey: process.env.REACT_APP_API_KEY,
// ...
};
I am not convinced to expose security/config keys to client. I would not call it secure, not because some one can steal all private information from first day, because someone can make excessive request, and drain your quota and make you owe to Google a lot of money.
You need to think about many concepts from restricting people not to access where they are not supposed to be, DOS attacks etc.
I would more prefer the client first will hit to your web server, there you put what ever first hand firewall, captcha , cloudflare, custom security in between the client and server, or between server and firebase and you are good to go. At least you can first stop suspect activity before it reaches to firebase. You will have much more flexibility.
I only see one good usage scenario for using client based config for internal usages. For example, you have internal domain, and you are pretty sure outsiders cannot access there, so you can setup environment like browser -> firebase type.
The API key exposure creates a vulnerability when user/password sign up is enabled. There is an open API endpoint that takes the API key and allows anyone to create a new user account. They then can use this new account to log in to your Firebase Auth protected app or use the SDK to auth with user/pass and run queries.
I've reported this to Google but they say it's working as intended.
If you can't disable user/password accounts you should do the following:
Create a cloud function to auto disable new users onCreate and create a new DB entry to manage their access.
Ex: MyUsers/{userId}/Access: 0
exports.addUser = functions.auth.user().onCreate(onAddUser);
exports.deleteUser = functions.auth.user().onDelete(onDeleteUser);
Update your rules to only allow reads for users with access > 1.
On the off chance the listener function doesn't disable the account fast enough then the read rules will prevent them from reading any data.
I believe once database rules are written accurately, it will be enough to protect your data. Moreover, there are guidelines that one can follow to structure your database accordingly. For example, making a UID node under users, and putting all under information under it. After that, you will need to implement a simple database rule as below
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
}
}
}
No other user will be able to read other users' data, moreover, domain policy will restrict requests coming from other domains.
One can read more about it on
Firebase Security rules
While the original question was answered (that the api key can be exposed - the protection of the data must be set from the DB rulles), I was also looking for a solution to restrict the access to specific parts of the DB.
So after reading this and some personal research about the possibilities, I came up with a slightly different approach to restrict data usage for unauthorised users:
I save my users in my DB too, under the same uid (and save the profile data in there). So i just set the db rules like this:
".read": "auth != null && root.child('/userdata/'+auth.uid+'/userRole').exists()",
".write": "auth != null && root.child('/userdata/'+auth.uid+'/userRole').exists()"
This way only a previous saved user can add new users in the DB so there is no way anyone without an account can do operations on DB.
Also adding new users is posible only if the user has a special role and edit only by admin or by that user itself (something like this):
"userdata": {
"$userId": {
".write": "$userId === auth.uid || root.child('/userdata/'+auth.uid+'/userRole').val() === 'superadmin'",
...
EXPOSURE OF API KEYS ISN'T A SECURITY RISK BUT ANYONE CAN PUT YOUR CREDENTIALS ON THEIR SITE.
Open api keys leads to attacks that can use a lot resources at firebase that will definitely cost your hard money.
You can always restrict you firebase project keys to domains / IP's.
https://console.cloud.google.com/apis/credentials/key
select your project Id and key and restrict it to Your Android/iOs/web App.
It is oky to include them, and special care is required only for Firebase ML or when using Firebase Authentication
API keys for Firebase are different from typical API keys:
Unlike how API keys are typically used, API keys for Firebase services are not used to control access to backend resources; that can only be done with Firebase Security Rules. Usually, you need to fastidiously guard API keys (for example, by using a vault service or setting the keys as environment variables); however, API keys for Firebase services are ok to include in code or checked-in config files.
Although API keys for Firebase services are safe to include in code, there are a few specific cases when you should enforce limits for your API key; for example, if you're using Firebase ML or using Firebase Authentication with the email/password sign-in method. Learn more about these cases later on this page.
For more informations, check the offical docs
I am making a blog website on github pages. I got an idea to embbed comments in the end of every blog page. I understand how firebase get and gives you data.
I have tested many times with project and even using console. I am totally disagree the saying vlit is vulnerable.
Believe me there is no issue of showing your api key publically if you have followed privacy steps recommend by firebase.
Go to https://console.developers.google.com/apis
and perfrom a security steup.
You should not expose this info. in public, specially api keys.
It may lead to a privacy leak.
Before making the website public you should hide it. You can do it in 2 or more ways
Complex coding/hiding
Simply put firebase SDK codes at bottom of your website or app thus firebase automatically does all works. you don't need to put API keys anywhere
Using this nodeJS example, I could get the data from a public sheet.
But how do I get the data from a non-public sheet owned by me (my google a/c) ?
Is there some way to send in the username and password as arguments ?
I don't want OAuth way as I want the data to be pulled from the sheet & displayed on a public webpage.
The other option I can think of is to have OAuth2 done once write a script to handle refresh tokens automatically as a cron every hour ?
Since this is a file that you the developer own i would recommend using a service account
If you share the file with the service account it will then have permissions to access it without you needing to go though the oauth2 steps of authorizing your application.
On google cloud console simply create Service account credentials
const {google} = require('googleapis');
const auth = new google.auth.GoogleAuth({
keyFile: '/path/to/your-secret-key.json',
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
});
Then change your auth code slightly. Open the service account key file and look for the service account email address its the only one witha # in it. Share the file with the service account like you would any other user in google drive web app.
Once it has access you shouldn't need to authorize the app again.
I have a video on Google drive API upload file with Nodejs + service account which might help you a bit you just need the authorization code. Everything else you have should work as is.
I've been developing a Teams Custom App with the TeamsFx SDK.
I want to use the Microsoft Graph API using an Application identity.
So, I referred to the Microsoft official documentation, however I wasn't able to achieve what I wanted to do.
- Referred document: https://learn.microsoft.com/en-us/microsoftteams/platform/toolkit/teamsfx-sdk.
Under "Create MicrosoftGraphClient service", "Invoke Graph API without user (Application Identity)" section.
I tried the following:
I created a new Teams app from "SSO-enabled tab" sample with Teams Toolkit on Visual Studio Code.
I edited a Graph.jsx as below to get a user info.
import { createMicrosoftGraphClient, IdentityType, TeamsFx } from "#microsoft/teamsfx";
useEffect(() => {
const getProfile = async () => {
const teamsfx = new TeamsFx(IdentityType.App);
const graphClient = createMicrosoftGraphClient(teamsfx);
const profile = await graphClient.api("/users/username#domain.onmicrosoft.com").get();
return profile;
};
const profile = getProfile();
}, []);
I debugged the project by hitting the F5 key in Visual Studio Code.
Although I tried what the document said, the console log said "Application identity is not supported in TeamsFx".
How should do I edit my projec to use Microsoft Graph API without a user identity (i.e. using Application Identity)?
Application Identity is not supported in browser (Tab page), so you need a server environment to use it.
You could add an Azure Function and use the Application Identity in it to achieve desired effect. Here's the steps in Teams Toolkit v4.0.1:
Create new project from "SSO-enabled tab" template.
Choose "Azure Functions" in "Add features" with default name.
Modify the code in "api/getUserProfile/index.ts".
teamsfx = new TeamsFx(IdentityType.App);
...
const graphClient: Client = createMicrosoftGraphClient(teamsfx, [".default"]);
const profile: any = await graphClient.api("/users/username#domain.onmicrosoft.com").get();
res.body.graphClientMessage = profile;
Configure "User.Read.All" permission and grant admin consent on AAD portal.
Run "F5" and click "Call Azure Function" on tab page, the Azure Function will be invoked and get Graph data using Application Identity. You should see the user information below.
What you're trying to do is insecure, because you have code running as Application level security, but running on the client side. Code running with that kind of privilege should only be running on the server side, or in this case behind a protected API (e.g. one that it validating the user's security using SSO to ensure the user is valid, and then executing on the server).
This video gives a bit of an idea of how it should be working: https://www.youtube.com/watch?v=kruUnaZgQaY
In the video, they're actually doing a token -exchange- (exchanging from the pure client side SSO token, and getting an "on behalf of" token in the backend, and making the call then). You can skip that part and just use the application token, but you should, as I mentioned, have this occurring in the server, not on the client.
Hello all.
I have been assigned the task of fetching unanswered emails from the inbox of each member of our Google domain using Spring Boot, but I haven't been able to do it.
In first place, I need the list of users from the domain. This can be achieved via Directory API (which cannot be enabled by that name in the Google Developer console, by the way. Looks like it belongs to Admin SDK or so).
What I have faced so far is this:
There are many related questions on SO, but most of them are outdated.
Java Quickstart for Google Directory API does not include an example using service accounts, and I want to use them because my app runs in a docker container, and using Oauth means I need to manually authorize it every time I deploy a new version or restart the container.
Google documentation makes reference to "API Reference" settings in Admin console, but I don't see that section there.
I am not storing credentials in a JSON file, I have them in an environment variable instead. I am doing this:
var inputStream = IOUtils.toInputStream(apiCredentials, Charset.defaultCharset()); //apiCredentials is a string with the JSON contents.
var credential = GoogleCredential
.fromStream(inputStream, httpTransport, JacksonFactory.getDefaultInstance())
.createScoped(Collections.singleton(DirectoryScopes.ADMIN_DIRECTORY_USER));
var directoryService = new Directory.Builder(httpTransport, JacksonFactory.getDefaultInstance(), credential)
.setApplicationName("My App")
.build();
var result = directoryService.users().list()
.setPageToken(pageToken)
.setDomain("my.domain")
.setMaxResults(10)
.execute();
After this, I get a 400 Bad request error, with no further description.
What am I doing wrong here?
Has anyone used delegates with exchnage web services? I would like one user to be able to control other users' calendars in Exchange. I'm finding this problem to be a little tricky, and I'd like to see how others have been able to get it to work properly.
I'm just getting started here, but i managed to get access to Resource calendars via a delegate account.
I used the recommendations from this article about delegate account and resource accounts. (Resource accounts are tricky because they are disabled in the AD, and you have to use a delegate account to get access to them)
After setting up the delegate account on the server, I set up the ExchangeServerBinding using the credentials of the delegate account:
ExchangeServiceBinding binding = new ExchangeServiceBinding();
binding.Url = #"https://dc1.litwareinc.com/ews/exchange.asmx";
// Setup binding with username and password of the delegate account
binding.Credentials =
new NetworkCredential(delegateuserName, delegatepassword, "litwareinc.com");
(I'm using Microsofts prepared virtual server image for testing)
Then when accessing the mailbox, I set up a FindItemType request and use the smtp address of the account i want to access:
// Prepare request
var findItemRequest = new FindItemType();
// Setup the mailbox using the smtp address of the account wanted
var mailbox = new EmailAddressType {EmailAddress = mailboxId};
findItemRequest.ParentFolderIds =
new[] {new DistinguishedFolderIdType {Mailbox = mailbox}};
((DistinguishedFolderIdType) findItemRequest.ParentFolderIds[0]).Id =
DistinguishedFolderIdNameType.calendar;
findItemRequest.Traversal = ItemQueryTraversalType.Shallow;
// Add ItemResponseShapeType and Calendarview to request here ...
// The create a FindItemResponseType using the binding and the request
var response = binding.FindItem(findItemRequest);
So in short:
Setup an account with delegate access on the Exchange server, this can be done via owa or with a Exchange Shell script
Use the account with delegate access on the ExchangeServiceBinding object
Access target account using a FindItemType with the target account smtp-addres as EmailAddressType
Regards
Jesper Hauge