Detect if request comes from an antivirus - email

I'm building a service with API Gateway + Lambda that tracks email link clicks. The links inside the email lead to my endpoint, which gathers the click info and redirects to another URL. However, I'm detecting that in some cases, some software automatically clicks most of the links, probably to prevent phishing, and the usual suspect here is an antivirus. Since I'm targeting only real user clicks, I want to discard them, but didn't find anything weird in the request headers. How would you check that the request comes from a non user?

In API Gateway settings, you can turn on CloudWatch logs to see all the request headers. Specifically, you can use $context and $input variables to log context variables like user-agent, source-ip or log all the headers.
If the bots are using exact same user-agent and set of headers, I do not see a way to distinguish them at API Gateway side.

Related

CORS issue while fetching data from Pinata IPFS URL [duplicate]

tl;dr; About the Same Origin Policy
I have a Grunt process which initiates an instance of express.js server. This was working absolutely fine up until just now when it started serving a blank page with the following appearing in the error log in the developer's console in Chrome (latest version):
XMLHttpRequest cannot load https://www.example.com/
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:4300' is therefore not allowed access.
What is stopping me from accessing the page?
tl;dr — When you want to read data, (mostly) using client-side JS, from a different server you need the server with the data to grant explicit permission to the code that wants the data.
There's a summary at the end and headings in the answer to make it easier to find the relevant parts. Reading everything is recommended though as it provides useful background for understanding the why that makes seeing how the how applies in different circumstances easier.
About the Same Origin Policy
This is the Same Origin Policy. It is a security feature implemented by browsers.
Your particular case is showing how it is implemented for XMLHttpRequest (and you'll get identical results if you were to use fetch), but it also applies to other things (such as images loaded onto a <canvas> or documents loaded into an <iframe>), just with slightly different implementations.
The standard scenario that demonstrates the need for the SOP can be demonstrated with three characters:
Alice is a person with a web browser
Bob runs a website (https://www.example.com/ in your example)
Mallory runs a website (http://localhost:4300 in your example)
Alice is logged into Bob's site and has some confidential data there. Perhaps it is a company intranet (accessible only to browsers on the LAN), or her online banking (accessible only with a cookie you get after entering a username and password).
Alice visits Mallory's website which has some JavaScript that causes Alice's browser to make an HTTP request to Bob's website (from her IP address with her cookies, etc). This could be as simple as using XMLHttpRequest and reading the responseText.
The browser's Same Origin Policy prevents that JavaScript from reading the data returned by Bob's website (which Bob and Alice don't want Mallory to access). (Note that you can, for example, display an image using an <img> element across origins because the content of the image is not exposed to JavaScript (or Mallory) … unless you throw canvas into the mix in which case you will generate a same-origin violation error).
Why the Same Origin Policy applies when you don't think it should
For any given URL it is possible that the SOP is not needed. A couple of common scenarios where this is the case are:
Alice, Bob, and Mallory are the same person.
Bob is providing entirely public information
… but the browser has no way of knowing if either of the above is true, so trust is not automatic and the SOP is applied. Permission has to be granted explicitly before the browser will give the data it has received from Bob to some other website.
Why the Same Origin Policy applies to JavaScript in a web page but little else
Outside the web page
Browser extensions*, the Network tab in browser developer tools, and applications like Postman are installed software. They aren't passing data from one website to the JavaScript belonging to a different website just because you visited that different website. Installing software usually takes a more conscious choice.
There isn't a third party (Mallory) who is considered a risk.
* Browser extensions do need to be written carefully to avoid cross-origin issues. See the Chrome documentation for example.
Inside the webpage
Most of the time, there isn't a great deal of information leakage when just showing something on a webpage.
If you use an <img> element to load an image, then it gets shown on the page, but very little information is exposed to Mallory. JavaScript can't read the image (unless you use a crossOrigin attribute to explicitly enable request permission with CORS) and then copy it to her server.
That said, some information does leak so, to quote Domenic Denicola (of Google):
The web's fundamental security model is the same origin policy. We
have several legacy exceptions to that rule from before that security
model was in place, with script tags being one of the most egregious
and most dangerous. (See the various "JSONP" attacks.)
Many years ago, perhaps with the introduction of XHR or web fonts (I
can't recall precisely), we drew a line in the sand, and said no new
web platform features would break the same origin policy. The existing
features need to be grandfathered in and subject to carefully-honed
and oft-exploited exceptions, for the sake of not breaking the web,
but we certainly can't add any more holes to our security policy.
This is why you need CORS permission to load fonts across origins.
Why you can display data on the page without reading it with JS
There are a number of circumstances where Mallory's site can cause a browser to fetch data from a third party and display it (e.g. by adding an <img> element to display an image). It isn't possible for Mallory's JavaScript to read the data in that resource though, only Alice's browser and Bob's server can do that, so it is still secure.
CORS
The Access-Control-Allow-Origin HTTP response header referred to in the error message is part of the CORS standard which allows Bob to explicitly grant permission to Mallory's site to access the data via Alice's browser.
A basic implementation would just include:
Access-Control-Allow-Origin: *
… in the response headers to permit any website to read the data.
Access-Control-Allow-Origin: http://example.com
… would allow only a specific site to access it, and Bob can dynamically generate that based on the Origin request header to permit multiple, but not all, sites to access it.
The specifics of how Bob sets that response header depend on Bob's HTTP server and/or server-side programming language. Users of Node.js/Express.js should use the well-documented CORS middleware. Users of other platforms should take a look at this collection of guides for various common configurations that might help.
NB: Some requests are complex and send a preflight OPTIONS request that the server will have to respond to before the browser will send the GET/POST/PUT/Whatever request that the JS wants to make. Implementations of CORS that only add Access-Control-Allow-Origin to specific URLs often get tripped up by this.
Obviously granting permission via CORS is something Bob would only do only if either:
The data was not private or
Mallory was trusted
How do I add these headers?
It depends on your server-side environment.
If you can, use a library designed to handle CORS as they will present you with simple options instead of having to deal with everything manually.
Enable-Cors.org has a list of documentation for specific platforms and frameworks that you might find useful.
But I'm not Bob!
There is no standard mechanism for Mallory to add this header because it has to come from Bob's website, which she does not control.
If Bob is running a public API then there might be a mechanism to turn on CORS (perhaps by formatting the request in a certain way, or a config option after logging into a Developer Portal site for Bob's site). This will have to be a mechanism implemented by Bob though. Mallory could read the documentation on Bob's site to see if something is available, or she could talk to Bob and ask him to implement CORS.
Error messages which mention "Response for preflight"
Some cross-origin requests are preflighted.
This happens when (roughly speaking) you try to make a cross-origin request that:
Includes credentials like cookies
Couldn't be generated with a regular HTML form (e.g. has custom headers or a Content-Type that you couldn't use in a form's enctype).
If you are correctly doing something that needs a preflight
In these cases then the rest of this answer still applies but you also need to make sure that the server can listen for the preflight request (which will be OPTIONS (and not GET, POST, or whatever you were trying to send) and respond to it with the right Access-Control-Allow-Origin header but also Access-Control-Allow-Methods and Access-Control-Allow-Headers to allow your specific HTTP methods or headers.
If you are triggering a preflight by mistake
Sometimes people make mistakes when trying to construct Ajax requests, and sometimes these trigger the need for a preflight. If the API is designed to allow cross-origin requests but doesn't require anything that would need a preflight, then this can break access.
Common mistakes that trigger this include:
trying to put Access-Control-Allow-Origin and other CORS response headers on the request. These don't belong on the request, don't do anything helpful (what would be the point of a permissions system where you could grant yourself permission?), and must appear only on the response.
trying to put a Content-Type: application/json header on a GET request that has no request body the content of which to describe (typically when the author confuses Content-Type and Accept).
In either of these cases, removing the extra request header will often be enough to avoid the need for a preflight (which will solve the problem when communicating with APIs that support simple requests but not preflighted requests).
Opaque responses (no-cors mode)
Sometimes you need to make an HTTP request, but you don't need to read the response. e.g. if you are posting a log message to the server for recording.
If you are using the fetch API (rather than XMLHttpRequest), then you can configure it to not try to use CORS.
Note that this won't let you do anything that you require CORS to do. You will not be able to read the response. You will not be able to make a request that requires a preflight.
It will let you make a simple request, not see the response, and not fill the Developer Console with error messages.
How to do it is explained by the Chrome error message given when you make a request using fetch and don't get permission to view the response with CORS:
Access to fetch at 'https://example.com/' from origin 'https://example.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Thus:
fetch("http://example.com", { mode: "no-cors" });
Alternatives to CORS
JSONP
Bob could also provide the data using a hack like JSONP which is how people did cross-origin Ajax before CORS came along.
It works by presenting the data in the form of a JavaScript program that injects the data into Mallory's page.
It requires that Mallory trust Bob not to provide malicious code.
Note the common theme: The site providing the data has to tell the browser that it is OK for a third-party site to access the data it is sending to the browser.
Since JSONP works by appending a <script> element to load the data in the form of a JavaScript program that calls a function already in the page, attempting to use the JSONP technique on a URL that returns JSON will fail — typically with a CORB error — because JSON is not JavaScript.
Move the two resources to a single Origin
If the HTML document the JS runs in and the URL being requested are on the same origin (sharing the same scheme, hostname, and port) then the Same Origin Policy grants permission by default. CORS is not needed.
A Proxy
Mallory could use server-side code to fetch the data (which she could then pass from her server to Alice's browser through HTTP as usual).
It will either:
add CORS headers
convert the response to JSONP
exist on the same origin as the HTML document
That server-side code could be written & hosted by a third party (such as CORS Anywhere). Note the privacy implications of this: The third party can monitor who proxies what across their servers.
Bob wouldn't need to grant any permissions for that to happen.
There are no security implications here since that is just between Mallory and Bob. There is no way for Bob to think that Mallory is Alice and to provide Mallory with data that should be kept confidential between Alice and Bob.
Consequently, Mallory can only use this technique to read public data.
Do note, however, that taking content from someone else's website and displaying it on your own might be a violation of copyright and open you up to legal action.
Writing something other than a web app
As noted in the section "Why the Same Origin Policy only applies to JavaScript in a web page", you can avoid the SOP by not writing JavaScript in a webpage.
That doesn't mean you can't continue to use JavaScript and HTML, but you could distribute it using some other mechanism, such as Node-WebKit or PhoneGap.
Browser extensions
It is possible for a browser extension to inject the CORS headers in the response before the Same Origin Policy is applied.
These can be useful for development but are not practical for a production site (asking every user of your site to install a browser extension that disables a security feature of their browser is unreasonable).
They also tend to work only with simple requests (failing when handling preflight OPTIONS requests).
Having a proper development environment with a local development server
is usually a better approach.
Other security risks
Note that SOP / CORS do not mitigate XSS, CSRF, or SQL Injection attacks which need to be handled independently.
Summary
There is nothing you can do in your client-side code that will enable CORS access to someone else's server.
If you control the server the request is being made to: Add CORS permissions to it.
If you are friendly with the person who controls it: Get them to add CORS permissions to it.
If it is a public service:
Read their API documentation to see what they say about accessing it with client-side JavaScript:
They might tell you to use specific URLs
They might support JSONP
They might not support cross-origin access from client-side code at all (this might be a deliberate decision on security grounds, especially if you have to pass a personalized API Key in each request).
Make sure you aren't triggering a preflight request you don't need. The API might grant permission for simple requests but not preflighted requests.
If none of the above apply: Get the browser to talk to your server instead, and then have your server fetch the data from the other server and pass it on. (There are also third-party hosted services that attach CORS headers to publically accessible resources that you could use).
Target server must allowed cross-origin request. In order to allow it through express, simply handle http options request :
app.options('/url...', function(req, res, next){
res.header('Access-Control-Allow-Origin', "*");
res.header('Access-Control-Allow-Methods', 'POST');
res.header("Access-Control-Allow-Headers", "accept, content-type");
res.header("Access-Control-Max-Age", "1728000");
return res.sendStatus(200);
});
As this isn't mentioned in the accepted answer.
This is not the case for this exact question, but might help others that search for that problem
This is something you can do in your client-code to prevent CORS errors in some cases.
You can make use of Simple Requests.
In order to perform a 'Simple Requests' the request needs to meet several conditions. E.g. only allowing POST, GET and HEAD method, as well as only allowing some given Headers (you can find all conditions here).
If your client code does not explicit set affected Headers (e.g. "Accept") with a fix value in the request it might occur that some clients do set these Headers automatically with some "non-standard" values causing the server to not accept it as Simple Request - which will give you a CORS error.
This is happening because of the CORS error. CORS stands for Cross Origin Resource Sharing. In simple words, this error occurs when we try to access a domain/resource from another domain.
Read More about it here: CORS error with jquery
To fix this, if you have access to the other domain, you will have to allow Access-Control-Allow-Origin in the server. This can be added in the headers. You can enable this for all the requests/domains or a specific domain.
How to get a cross-origin resource sharing (CORS) post request working
These links may help
This CORS issue wasn't further elaborated (for other causes).
I'm having this issue currently under different reason.
My front end is returning 'Access-Control-Allow-Origin' header error as well.
Just that I've pointed the wrong URL so this header wasn't reflected properly (in which i kept presume it did). localhost (front end) -> call to non secured http (supposed to be https), make sure the API end point from front end is pointing to the correct protocol.
I got the same error in Chrome console.
My problem was, I was trying to go to the site using http:// instead of https://. So there was nothing to fix, just had to go to the same site using https.
This bug cost me 2 days. I checked my Server log, the Preflight Option request/response between browser Chrome/Edge and Server was ok. The main reason is that GET/POST/PUT/DELETE server response for XHTMLRequest must also have the following header:
access-control-allow-origin: origin
"origin" is in the request header (Browser will add it to request for you). for example:
Origin: http://localhost:4221
you can add response header like the following to accept for all:
access-control-allow-origin: *
or response header for a specific request like:
access-control-allow-origin: http://localhost:4221
The message in browsers is not clear to understand: "...The requested resource"
note that:
CORS works well for localhost. different port means different Domain.
if you get error message, check the CORS config on the server side.
In most housing services just add in the .htaccess on the target server folder this:
Header set Access-Control-Allow-Origin 'https://your.site.folder'
I had the same issue. In my case i fixed it by adding addition parameter of timestamp to my URL. Even this was not required by the server I was accessing.
Example yoururl.com/yourdocument?timestamp=1234567
Note: I used epos timestamp
"Get" request with appending headers transform to "Options" request. So Cors policy problems occur. You have to implement "Options" request to your server. Cors Policy about server side and you need to allow Cors Policy on your server side. For Nodejs server:details
app.use(cors)
For Java to integrate with Angular:details
#CrossOrigin(origins = "http://localhost:4200")
You should enable CORS to get it working.

Including a body in a GET, PUT, or POST request results in response of "You need to enable JavaScript to run this app"?

The short story: If I hit my endpoint /api/something with a PUT and no body, it goes through to my Lambda (via CF -> API GW -> Lambda). If I add a body to the request, it doesn't work and seems to die before hitting the API Gateway. I'm not sure why it would do this.
The long story:
I've set up a CloudFront distribution that sends requests to /api to our API gateway and all other requests just vend static website resources.
It has been working great so far for our simple use cases. We previously only hit the API Gateway with GET requests, but now we're going to start needing to send PUT or POST requests to the API Gateway. I had to update the CloudFront distribution "behaviors" for the /api path pattern (the one going to the API gateway) to allow all HTTP methods after that, I was able to start getting responses from the API Gateway for PUT and POST responses (previously only worked for GET).
However, I noticed that if I try to send a payload or body with the request (regardless of the request type), I don't think it actually hits the API Gateway or the Lambda that the API Gateway is routing to because I get a response of "You need to enable JavaScript to run this app". I tried looking at the logs but couldn't find anything. The heads shows "Error from cloudfront" but I'm not sure why or where to find what the error was.
Any tips on how to troubleshoot this or what the issue might be?
Update (10/8): I figured out the "enable javascript" thing was coming up because of my custom error pages for 403&404 errors. I deleted them in my development environment and now get a clearer error.
"This distribution is not configured to allow the HTTP request method
that was used for this request. The distribution supports only
cachable requests. We can't connect to the server for this app or
website at this time. There might be too much traffic or a
configuration error. Try again later, or contact the app or website
owner."
However, I still don't know what to do, as the request type (PUT) is enabled and the request works as long as I don't provide a body. Not sure how to get it to be ok with a body at the moment.

Sending passwords over HTTPS: GET vs POST

I'm creating a headless API that's going to drive an Angular front end. I'm having a bit of trouble figuring out how I should handle user authentication though.
Obviously the API should run over SSL, but the question that's coming up is how should I send the request that contains the user's password: over GET or POST. It's a RESTFUL API, so what I'm doing is retrieving information meaning it should get a GET request. But sending the password over get means it's part of the URI, right? I know even a GET request is encrypted over HTTPS, but is that still the correct way? Or is this a case to break from RESTFUL and have the data in the body or something (can a GET request have data in the body?).
If you pass the credentials in a request header, you will be fine with either a GET or POST request. You have the option of using the established Authorization header with your choice of authentication scheme, or you can create custom headers that are specific to your API.
When using header fields as a means of communicating credentials, you do not need to fear the credentials being written to the access log as headers are not included in that log. Using header fields also conforms to REST standards, and should actually be utilized to communicate any meta-data relevant to the resource request/response. Such meta-data can include, but is not limited to, information like: collection size, pagination details, or locations of related resources.
In summary, always use header fields as a means of authentication/authorization.
mostly GET request will bind data in URL itself... so it is more redable than POST..
so if it is GET, there is a possibility to alive HISTORY LOG
Using ?user=myUsername&pass=MyPasswort is exactly like using a GET based form and, while the Referer issue can be contained, the problems regarding logs and history remain.
Sending any kind of sensitive data over GET is dangerous, even if it is HTTPS. These data might end up in log files at the server and will be included in the Referer header in links to or includes from other sides. They will also be saved in the history of the browser so an attacker might try to guess and verify the original contents of the link with an attack against the history.
You could send a data body with a get request too but this isn't supported by all libraries I guess.
Better to use POST or request headers. Look at other APIs and how they are handling it.
But you could still use GET with basic authentication like here: http://restcookbook.com/Basics/loggingin/

Understanding CORS and Same origin policy

Lets say I own a site www.a.com which shows some information to the user after logging him in. Here is the flow (assume everything is over https even if not explicitly mentioned) -
User loads https://www.a.com/ which also sends down the login page.
User types login+passwd and the JS calls www.a.com/login and gets an auth token (T) back.
JS then calls www.a.com/getdata (and T is sent as well). The server responds with the data for the appropriate user.
Just looking at the code anyone can know that the two APIs the JS uses are www.a.com/login and www.a.com/getdata
Now here are the scenarios I get confused over -
IF a rogue entity (or someone like mint) creates a site (www.r.com) that asks for the user password and posts it to the APIs can my server know it?
Here the JS is not from a.com but completely re-written by r.com. Do the CORS rules or same origin policy apply here?
Another scenario, IF www.r.com embedded a frame on its page that is loading www.a.com and asking for user name and passwd there, that means its actually loading the a.com JS. In this case, can JS from r.com access the data sent down to the frame loading a.com?
Do the CORS rules or same origin policy apply here?
Yes. Per the Same Origin Policy the script on r.com will not be able to read the results of any request it makes to a.com. Therefore it will not be able to read the login token. If you add CORS support then you can opt-in to allowing r.com access, in which case it will be able to interact freely with your site.
Note that if r.com is a malicious site and is able to get the user to enter their password, the browser's Same Origin Policy won't actually protect the user. The malicious page can just send the information to their own server where arbitrary requests can be sent to your site with the user's credentials.
Can JS from r.com access the data sent down to the frame loading a.com?
The Same Origin Policy applies to iframes as well, so the r.com script will not be able to access the data sent to the a.com iframe.

REST API: Providing redirect URIs to external services using client app domain

Background
I have a RESTful API accessed through the domain http://restapi.com
I have a client app using http://restapi.com. The client app has the domain http://myapp.com
The way I have my HATEOAS setup is that the API presents URIs without a domain. So instead of http://restapi.com/some/resource, it contains links to resources like so /some/resource. Example API json resource below:
{"_links":{"self":{"href":"/some/resource"}}}
The benefit this has is that the API doesn't need to know about the client app, and the client app has to do very little to get the correct resource from the API and doesn't have to reformat all the URIs in the resource. For example, in the client app, the following URI would be used by the browser http://myapp.com/some/resource. When the app gets the request, it then needs to call the API to get the resource and simply swaps the domain i.e. http://restapi.com/some/resource.
This has been successful so for, and allows a lot of flexibility to have different clients use the API with the only knowledge required being the initial end point (domain) of the API. It also completely decouples the API from the client apps.
The problem I have run into is that I have started using some external services (specifically PayPal adaptive payments) where I need to provide a redirect URL for cancelled payments and successful payments. For example, the browser navigates to http://myapp.com/payment. The resource returned by http://restapi.com/payment presents a link to PayPal. Without going into too much detail, the API has to ask PayPal for a payment ID, which can then be used to create a link to a PayPal payment e.g. http://paypal.com?PayId-123456. Part of the creation process requires that URLs are provided to redirect on payment cancellation or success. Again, don't want to go into details, but when requesting a PayId from PayPal, the redirect URLs are sent as variables to PayPal, and I guess PayPal stores them against the specific PayId created.
The browser navigates to the link returned in the resource - http://paypal.com?PayId-12345. Payment is made and PayPal then uses the redirect URLs as needed to redirect back to my app e.g. on successful completion of payment, PayPal should redirect to http://myapp.com/paymentcomplete. Note: I realise that this is not a restfully named URI, but it simplifies building up the description of my problem
Problem
The problem I have may now be obvious. I need to redirect back to http://myapp.com/paymentcomplete, BUT its the API that provides the redirect URL to PayPal. It has no knowledge of the client application. Since PayPal is an external service, the full URL must be provided. The best the API can do is send http://restapi.com/paymentcomplete as the redirect URL, but if PayPal redirects to this, the resulting response will be a JSON string (the output format of my API) not the nicely formatted page of the client app.
My question is, what is a good way to correctly provide the redirect URL to PayPal?
One thought I had was to make the client application handle creating the PayPal PayId, but I don't like this option as I would like to keep the creation of the PayPal payment ID on the API side. It would also require every client app to provide its own implementation, something I also don't want.
The other option I though of was to ask the client to provide its domain in the request. Currently the request the client makes to get the resource with the link to PayPal is GET http://restapi.com/payment, but I could use POST http://restapi.com/payment with the client providing its domain as a param. The API can then use this to construct the correct redirect URL. I don't really like this idea either as its seems a bit hackish and also requires the app to know that is must fill in this field i.e. a human user wouldn't fill the domain input in.
Additional solutions, or thoughts greatly welcomed.
As you had already mentioned, PayPal is an external api that requires this additional parameter and you do not have control over it. Looks like the client is the only party that can provide the Redirect URI Information.
Couple of ideas come to mind.
The client could send the redirect uri to restapi via header and thus
keeping your rest urls intact. This is a grey area and not a violation of restful api
in my opinion. (Then again, its just my opinion).
The restapi could return the response with a placeholder for the
client to fill in before rendering. This way the API need not know
about the redirect uri and the responsibility is left to the client
which has this information.
It would be nicer if you could implement option 2 with executing couple of lines on Javascript code on the browser to fill-in the placeholder. Which is easy. Ultimately, only 2 end points of this transaction would be aware of the redirect uri - browser & paypal.
This alleviates most of your concerns. The job of handling PayPal id will continue to remain with your API.
You should be able to use the Referer header to determine the client's full URI. It might be populated automatically for you. If not, you can add it yourself. The URI class has methods to pull out the client's host for you. When the API builds the PayPal URI to return to the client, it can include the client's host.
Note that referer is not always included and sometimes gets stripped by intermediaries, as detailed on the wiki page. Since you control both the client and the server in this case, you should be able to tell everybody to play nicely.
I would keep the GET http://restapi.com/payment and pass in a query param with the client domain
GET http://restapi.com/payment?domain=http://myapp.com (of course, the "http://myapp.com" needs to be encoded)