Whitelist PDF Embed - javascript

Is there a way or a service that allows for read-only (no saving or downloading) PDF embeds on one domain only? I know that there are services like Scribd and Slideshare but the problem is that while they do have private options, no service as far as I can tell allows to whitelist embeds (eg, only allow embeds on certain domains.) Vimeo can do this with videos and I don't mind paying for this service either. Any ideas?
I've also looked into PDFJS and it seems they have a NodeJS implementation so I was thinking maybe PDFJS could grab the PDF from the server on the server side and just stream it to the client without exposing the original PDF url. However I couldn't find good documentation for PDFJS.
Any help would be greatly appreciated.

This can be achieved with any HTTP server, but since you mentioned Node in your question, we will solve the problem with that technology. I am assuming Express Framework as well.
First you simply host the PDF as a static file on your server. Then you would register some Middleware that detects a request for the PDF. If the hostname that is requesting does not match a list of "approved" domains, then you serve an error back to the client. If the domain is approved, you serve the PDF. This is no different then a .htaccess file in Apache that limits access by domain/IP or an "allow" block in a Nginx config. Here is a quick look at the Middleware function...
var approved = []; // Add your approved domains here.
// Make sure this middleware comes before app.use(express.static)
app.use(function(req, res, next){
if(req.url == '/path/to/PDF') {
if(approved.indexOf(req.headers.host) {
next();
} else {
next(new Error('Nu uh uh!'));
}
} else {
next();
}
});
This way even if they copy the embed code, they will get an error from the server (probably should be a 403, but those are semantics you can decide on yourself)

Related

How do I limit access to my Netlify Serverless function

I've searched the netlify docs and I can't figure this out.
I have a serverless function located here
/.netlify/functions/orderCreate
But I can hit this in my browser or with curl and it tries to create an order. If an attacker finds out about this function they could create thousands fake orders in my db.
I know I can do some simple checks like make sure it is a HTTP post, or make sure it has some valid session ID but I would really like some type of auth or better security.
Because all requests should come from the a client side react app via an ajax request can I limit it to the same domain or something ?
As Netlify doesn't provide a way to check and specific requests based on origin, you could do it manually from inside your function's code and send a 403 response if the Origin isn't your client-side domain:
exports.handler = function(event, context, callback) {
if (event.headers["Origin"] !== "https://whateverisyourdomainname.netlify.com")
return callback(null, { status: 403 })
// else, do whatever your function does
}
Recent browsers do prevent a user from setting the Origin header himself. However, nothing prevents anyone to craft a curl request and to spoof the Origin header to hit your function. If you wish to really prevent it, you should set-up a proper authentication process to your application.

How to restrict API's to run only in the browser?

I am using node js and i put polices to restrict the api's accurance otherthan in browser.For that i put the following condition
app.route('/students').all(policy.checkHeader).get(courses.list)
exports.checkHeader = function(req, res, next) {
var headers = req.headers;
if ( headers['upgrade-insecure-requests'] || headers['postman-token']) {
res.status(401).json('Page not found');
} else {
return next();
}
}
I am not sure whether my process is correct.I am searching for the common parameter(header-parameter) that exists only for the browser.Can anyone please help me.Thanks.
This is impossible.
You can't control what types of clients make HTTP requests to your HTTP server.
You can't reliably identify what type of client has made a request you receive.
An upgrade-insecure-requests header can be sent (or not sent) with any value by any custom client. Ditto postman-token. Ditto user-agent. Ditto everything else.
The only way to restrict it would be to require some kind of secret in the request. If you want regular web browsers to access it, then the secret will leak through the browser developer tools.
I don't think it's possible to really block the requests without Authentication / Authorization. However, you can use the HTTP Header's User-Agent field
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

NodeJS: Send HTTPS request but get HTTP

I am building a website using NodeJS, and I deploy it to Heroku. But when I open the website, something went wrong. Here is the problem:
Code:
In the main source file of my web:
app.get('/', (req, res) => {
var data = {
rootURL: `${req.protocol}://${req.get('Host')}`,
};
res.render('home.html', data);
});
Then, in home.html, I include the following script:
<script type="text/javascript">
$.getJSON('{{rootURL}}'+'/about', {}, function(data){
// Code here is deleted for now.
}).fail(function(evt) {
// Code here is deleted for now.
});
</script>
Here I use hbs template, so {{rootURL}} is equal to the 'rootURL' property within the 'data' object rendered along with the 'home.html' page.
The '/about' is one of the APIs I designed for my web. It basically sends back something about the website itself and this information is wrapped in JSON.
Then, here comes the problem. The code works fine locally, and works well when I send HTTP request instead of HTTPS to Heroku. But if I send HTTPS request to Heroku, I'll get 'Mixed Content' Errors:
Errors I get in Chrome Console.
I then switched to 'Elements' tab in the developers tool, and I saw this:
The schema is HTTP, not HTTPS!
I'm very confused here. I just grab the 'protocol' property within the 'req' object, and fill in the template with it. So, I'm assuming if I enter '[my-website-name].herokuapp.com' with 'https' schema in my Chrome Browser, my nodeJS app deployed on Heroku should get 'https' for req.protocol. But Apparently it's not the case. What is wrong here?
I assume you don't actually have an SSL certificate? Heroku will be providing the HTTPS, but it will then translate it to normal HTTP internally when it hits your express endpoint, which is why it sees req.protocol as HTTP.
Is there any point in even providing the URL to getJSON? Why not just send it $.getJSON('/about', callback) and let the browser handle that?
Also, you haven't hidden your URL in that first image you uploaded, if that's what you were intending.
Heroku router is doing SSL termination, so no matter if you connect via http or https, you get http on your side. Original protocol is however set in X-Forward-Proto header. You need use this value.

How to create ssl connections for specific operations, but still allow http for all other site operations?

I have been working on a solution for this problem for about a week, but am at an impasse. I have scoured many online resources, but have not found a relevant example. From what I have read on the Internet, this type of operation should be possible. However, I have not attempted something of this nature before, so I may have some glaring misconceptions/mistakes in my approach.
Is this operational mode possible for the following system configuration? If so, please lend a helping hand.
Here is some relevant information.
System Configuration: Single Page App with many partial views (some require ssl, most do not)
Client: Using latest versions of AngularJS, Bootstrap, and TypeScript, plus CSS, HTML5
Server: Using IIS 7 and C# Web API 2.0
Thanks...
Single Page App Requirements:
Targeting all major operating systems for smart-phones, pads, laptops, and PCs.
Need to have secure communication for all member registration, login, and a few data creation operations.
Need non-secure http for all non-user related downloads. This represents about 99% of the total site usage/day.
Background:
The site works fine if I run the application using https.
If I disable the RequireHttps attribute in the Web API controller, the site works fine.
I have tried enabling CORS to no avail.
I do not understand why Fiddler shows a 200 return value, but my app shows a 404 error.
Fiddler Results:
HTTP Tunnel to www.myDomain.com:443
HTTPS www.myDomain.com /api/Authentication/Login?memberInfo=df... xs
Result is 200 for both of the above in Fiddler using IE11, Chrome, and Safari.
The server Web API is accessed by these browsers correctly.
The Web API login method returns to correct encrypted string value.
Firefox returns a 200 in Fiddler for Tunnel to, but does not execute the HTTPS call like the other browsers.
Inside my application, the callback returns a 404 error when the RequireHttps attribute annotation is used.
Relevant Code:
this.http({
method: 'POST',
url: 'https://www.myDomain.com/api/Authentication/' + "Login?memberInfo=" + $scope.base64Service.encode(memberInfo),
}).
success(function (data, status, headers, config) {
// Success code here.
}).
error(function (data, status) {
// error code here.
// data = “” for https with status = 404;
});
public class AuthenticationController : ApiController {
[RequireHttps]
[HttpPost]
public string Login(string memberInfo) {
// login code here.
return memberData;
}
}
public class RequireHttpsAttribute : AuthorizationFilterAttribute {
public override void OnAuthorization(HttpActionContext actionContext) {
if (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps) {
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden) {
ReasonPhrase = "HTTPS Required"
};
}
else {
base.OnAuthorization(actionContext);
}
}
}
web.config file entry for CORS
Edited, removed second domain
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin"
value="http://myDomain.com" />
</customHeaders>
</httpProtocol>
Edit:
Using one domain for the Access-Control-Allow-Origin and the following code for AngularJS, I have been able to get a secure tunnel connection in all browsers. I also had to add my self-signed certificate for my test server to a domain exception list for Firefox. That explains the difference, noted above, between the four browsers that I tested. Apparently, the browsers were all disallowing the request, hence the 404 error. Even though the server was returning a successful response, the browser was not making the response available to my client application.
smmApp.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);
You need to specify a single domain in Access-Control-Allow-Origin
It seems like you're trying to use SSL to protect against Man in the Middle attacks. If this is the case, you can't do what you're describing.
If a request to the page is made over HTTP and a MitM attack is taking place, there is nothing preventing the attacker from altering the javascript being served to the browser so that the credential requests are proxied through a malicious server.
This is why it is important to ensure that your login form, as well as the page it posts to, are both delivered over SSL.
I promise, this isn't theoretical. If you want to use SSL at all, you must serve the entire site over SSL.
The error you're seeing seems to be a CORS issue.

Express.JS: Attach cookie to statically served content

I use Express.JS to serve static content:
express.use(express.static('./../'));
When index.html is served, I would like to send a cookie alongside the response indicating if the user is signed in or not. Normally one should use res.cookie() but I cannot see how to use it for statically served content.
Not sure why you need to do this, but you can place your own middleware before the static module.
The following quick hack should work:
function attach_cookie(url, cookie, value) {
return function(req, res, next) {
if (req.url == url) {
res.cookie(cookie, value);
}
next();
}
}
app.configure(function(){
app.use(attach_cookie('/index.html', 'mycookie', 'value'));
app.use(express.static(path.join(__dirname, 'public')));
});
The above inserts another function in the chain of middleware express uses before the static middleware. The new layer attaches the cookie to the response if the URL matches the specific one you are looking for -- and passes the response further down the chain.
Consider also the following approach:
If your express is behind web-server you can serve static files without bothering express - it should be faster than via middle-ware. If you use nginx, this can help: Nginx Reverse Proxying to Node.js with Rewrite.
Assuming that your static file has javascript in it, you can also set cookies directly on the client side only requesting from express the data you need for this cookie:
document.cookie = "user_id=" + user_id;
Flanagan's JS definitive guide (edition 6!) has an excellent coverage on how to use cookies in client-side javascript (in addition to being the best among JavaScript books :).
It can be a trivial advice, but I have seen the following flow (more than once): client sends API request (which has a cookie attached to it, obviously), server gets data from the cookie and serves the response completely built on the data contained in this cookie. All this instead of just quietly reading this cookie in the client. Basically client asks the server what it has in its own cookie.
In your scenario you need to request user_id/access_key only once and then always check the cookie in the client, going to the server only for the data that client doesn't already have, but storing and checking session state and, maybe, some compact data used in most pages (such as username, e.g.) in cookies locally (you can also cache data in local storage, to reduce the server load even further). In this case, express won't even know if a user accidentally refreshes the page (if you don't change URLs to reflect application state as well, of course, or only change #-part).
As app.configure was removed since Express.js v4, I would like to do an update.
For Express.js versions > 4, to initialise my app by passing options inside the express.static(root, [options]) method, in which you can pass a Set-Cookie header in the property called setHeaders:
app.use(express.static(publicPath, {
setHeaders: function (res, path, stat) {
res.set('Set-Cookie', "myCookie=cookieValue;Path=/")
}
}));
It is important to set Path=/ because otherwise express will create numerous duplicates of the cookie on the client side.

Categories

Resources