In this snippet, we’re going to take a look at the browser-policy package by the Meteor Development Group. This package helps to protect our applications from unwanted access by third parties. Using this package, we can better control how content is loaded in our application as well as how our application can be framed by other websites and applications. We’re going to look at how to get set up with the package, get a basic idea for how it works, and then explore the APIs we get for configuring rules (with examples). Ready? Let’s get to it!
Installation
Just a single package and you’re all set up! By default Browser Policy configures a handful of rules for you to protect your application. Below, we’ll explain how to configure these.
Terminal
What does Browser Policy do?
Browser policy helps you to police two things in your application:
Specifying where your application can load content from (i.e. what domains).
Specifying who is allowed to frame your application (i.e. loading it in an <iframe></iframe> elsewhere).
For both, Browser Policy gives us a simple API for specifying a ruleset on top of the Content Security Policy (CSP) standard and the X-Frame-Options HTTP header standard. Said in more human terms, using Browser Policy looks something like the following. Below, we showcase two examples of an application with and without Browser Policy set up: one showing the selective blocking of content on the page and the other showing selective framing of the application.
Content example
Here, we see an application that does not have Browser Policy installed. We’re adding a simple Vimeo video to the page which appears as we’d expect.
Great! But, at this point, technically anybody can embed anything in our site. Real quick, we’re going install the browser-policy package and then refresh to see what happens.
Though it may be a bit alarming at first, this is Browser Policy in action. What’s happening here is that the Vimeo video we added earlier is not recognized in the list of content that’s allowed to appear on our site. More specifically, the domain player.vimeo.com—where the video is being loaded from—is not on that list. If we pop open our console, we can get some more clues as to what is happening.
That may be a bit hard to read, here’s the full output: Refused to frame 'https://player.vimeo.com/video/94238859?title=0&byline=0&portrait=0' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'frame-src' was not explicitly set, so 'default-src' is used as a fallback.
So what’s the point? Our application is secure, but we can’t load any content into it. For now, yes. We’re going to go into detail later, but to fix this, let’s add a rule using Browser Policy’s BrowserPolicy.content.allowFrameOrigin() method. On the server, we’ll add this to our startup code like this:
Server
What does this mean? Browser Policy should allow all content of the type Frame from the origin domain player.vimeo.com. This means that if the domain name here was updated to fhqwhgads.vimeo.com, our video would disappear again! The security here is pretty stringent (a good thing). If we refresh now, our video will show up just like it did before we added the browser-policy package.
Framing example
A similar behavior can be controlled on the framing side of things, which again, dictates what websites and applications can display our application a in a frame or iframe. To demonstrate this, we’ve set up another Meteor application and started it on port 5000, a separate domain. We’ve added the following to the page:
Client
See our iframe there pointing to http://localhost:3000? This is our original application (the one with our Vimeo video). If we open up our browser, Browser Policy has already kicked in and said “oh hell no”:
This is a good thing. Here, we can see that in the original app, Browser Policy is examining the request from our new “framing-app” and realizing that it’s trying to frame our app in another domain. Because we haven’t specified another domain, the request gets blocked! Let’s hop back over to our original app and add a quick rule to make this work without issue.
Server
And now, back in our framing app:
But wait! Notice that even though our frame loads up now, we still get an error Invalid 'X-Frame-Options' header encountered when loading http://localhost:3000.... What’s that? Well, technically Google Chrome and Apple Safari browsers do not support the X-Frame-Options HTTP header yet (what Browser Policy sets for us).
Despite this, our security still works because our original application now recognizes the request from http://localhost:5000. It’s a bit plucky, so take a second to wrap your head around why that’s working. Of course, just like in our content example, if we removed the browser-policy package here, our frame would load up without the need for a rule.
Why should I use Browser Policy?
The purpose of using Browser Policy is to improve the security of your application in respect to different types of attacks involving the browser like Clickjacking, Cross Site Scripting (XSS), and code injection. These scary terms are no joke. Using these methods, attackers can do things like tricking your users into thinking they’re using your application when in reality, all of their data is being funneled to the attacker’s database because they wrapped your application in a frame. No good!
Fortunately for us, Meteor makes fixing these problems easy with the browser-policy package. It does require a little more work on our part to specify which types of content and sites can frame our application, but if you put on your Imagination Hat, I’d bet you can come up with some pretty terrible scenarios where you left it out. Don’t be lazy, protect your users!
Content options
So we have a pretty good handle on the basics. If we want to allow some sort of content, we just define a rule for a specific domain and content type like BrowserPolicy.content.allowFrameOrigin( 'player.vimeo.com' );. Of course, this isn’t the only way to get the job done. To make this more clear, let’s look at the handful of methods that we get from Browser Policy and explain what each does.
Server
What you’d expect. When you install browser-policy, this behavior is enabled by default. Here, we’re not setting any domain names, just a global setting. This means that we can use <script></script> tags in our application without blocking them (e.g. loading an analytics service).
Server
The inverse of the above! If we do not want to allow inline scripts to be loaded in our application using <script></script> tags, we can add this.
Server
If we’d like to allow usage of JavaScript’s eval() method, we need to add this. Generally, usage of eval() is frowned upon but you may find some libraries using it to get some pesky tasks done. This method let’s you define usage permissions globally.
Server
Again, the inverse of the above and also the default for the Browser Policy package. Adding this means that any usage of eval() (whether your own code or others) is blocked.
Server
This one is good for applications that allow users to customize styles and need to rely on inline styles to get the job done. If you add this rule, things like <div style="background: #da5347;"></div> will be allowed. This is the default behavior for the Browser Policy package.
Server
No more inline styles! If you do not have any functionality that requires inline styles, this is probably a good one to add to your rule set.
Content-type specific
While the above rules control content on a global basis, like we saw earlier, we can specify rules for specific types of content being loaded from specific domains. Here are the methods to get it done.
Server
Here, we see that <ContentType> is identified as one variable and origin as another. For content type, we can specify the following “types”:
BrowserPolicy.content.allowFrameOrigin( origin );
BrowserPolicy.content.allowScriptOrigin( origin );
BrowserPolicy.content.allowObjectOrigin( origin );
BrowserPolicy.content.allowImageOrigin( origin );
BrowserPolicy.content.allowMediaOrigin( origin );
BrowserPolicy.content.allowFontOrigin( origin );
BrowserPolicy.content.allowConnectOrigin( origin );
BrowserPolicy.content.allowStyleOrigin( origin );
For example, if we wanted to allow only images to be loaded from http://google.com, we’d have a rule like: BrowserPolicy.content.allowImageOrigin( 'google.com' );. Make sense?
Wildcard Domains
Fun fact! Except where noted in the examples here, you can rely on wildcard domains to allow content to be loaded from any root or sub-domain of a domain name like *.vimeo.com. Here, if we tried to load something from player.vimeo.com and stats.vimeo.com, both would work without specifying each individually.
Server
Same rules apply here as the above method (as well as types). Here, this allows the specified content type to be loaded via a data:<type> URL. Here’s an example of a plain text type data URL. This is common on sites that host things like web fonts or images. Note: this does not take a domain argument as the URL is the data string and not a resolvable domain like vimeo.com.
Server
This method allows the specified content type to be loaded from the same origin domain as the application is running on. For example, if I had an image that lived at https://themeteorchef.com/images/blah.jpg and my application was also running on themeteorchef.com, I could specify BrowserPolicy.content.allowImageSameOrigin(); and the image would load fine.
Keep in mind, you shouldn’t have to set this if you’re hosting content within your own application, but it may apply to some edge cases.
Server
Again, what you’d expect. Here, we can globally disallow a specific content type from being allowed to load in the application. For example, if you have a vendetta against stylesheets, you could set BrowserPolicy.content.disallowStyle();. Yes, this would disable local stylesheets as well.
Server
This allows all content types to be loaded from the current domain the application is running on.
Server
This allows all content types to be loaded as a data:<type> URL in the application. Again, remember that data URLs are the URL (there’s no domain involved) so use this option wisely to prevent unwanted data URLs from being loaded in the application.
Server
This is the slammer to your pogs. By adding this, we allow all content types from the specified origin domain. This is handy for when you fully trust the domain and are not worried about them sending over any malicious content.
Server
For party poopers. Adding this disallows all content types in your application. All. This will pretty much break your entire GUI for your application. This is a good one if your application is an API/server-side only and you don’t want any content being loaded on the front-end.
Framing options
Framing options are pretty straightforward. There’s only a handful of them. Let’s take a look.
Server
This disables any framing of your application. If you’re certain that framing of your app won’t be necessary, this is a good one to add.
Server
Any framing of your application is restricted to the specified origin. Setting this can be done one origin at a time and must specify the specific domain name (no wildcard domains like '*.google.com').
Server
Danger Will Robinson! This does exactly what it says: allows framing of your application from any domain. Only add this if your application does something like Vimeo or YouTube where you’re allowing users to access your application’s content via an iframe or other embed.
Common policy patterns
Okay! So we have a pretty good handle on what Browser Policy does along with all of the methods we have access to. Before we part ways, I wanted to share a handful of example policies that I’ve used in multiple projects. Consider this a grab bag and take what you need!
Amazon
Most of my file storage takes place on Amazon S3 so I find it’s good to add this one by default. Keep in mind, you may need to specify your region in the domain as one reader pointed out here.
Google Fonts
If an application calls for Google Fonts, I’ll add their fonts.googleapis.com URL directly. Notice, a wildcard is left out here as that would apply to the entirety of googleapis.com which we don’t necessarily want.
Recurly
For usage with the Recurly payments service. This is used for their client-side library Recurly.js used for generating payment tokens from the client.
Stripe
Stripe is one of the more common tools used in my applications and so I always add access for all content from Stripe to be loaded. Here, we’re using a wildcard domain to cover things like stripe.com, js.stripe.com, checkout.stripe.com, etc.
Typekit
For loading in web fonts. Typekit requires two settings: one for all of any scripts and files it loads and another for the data:font URL that it brings along.
Have a common pattern?
Share any common patterns you use in the comments and I’ll add them to this list!
Takeaways
Install browser-policy in your application early on and make use of it to protect your users.
For trusted sites, you can set wildcard domains that allow for content to be loaded from any subdomain of the parent.
Framing of your application should be rare or non-existent, but if you try to do it yourself and get stuck, don’t forget to add a rule!