How can I intercept only one endpoint of a domain for my browser API calls? - dns

Suppose I enter a (public) website that makes 3 XHR/fetch calls on 3 different endpoints:
https://api.example.com/path1
https://api.example.com/path2
https://api.example.com/path3
What I want to achieve is intercept the call to https://api.example.com/path2 only, redirect it to a local service (localhost:8000) and let path1 and path3 through to the original domain.
What kind of options do I have here? I have studied a lot of approaches to this issue:
DNS rewriting - this solution is not suitable as I still have to intercept path1 and path3, only redirect them to the original IPs and try to mimic the headers as much as possible - which means I would have to do a specific proxy configuration for each intercepted domain - this is unfeasible
Chrome extensions - found none to deal specifically with single endpoint intercepting
Overwriting both fetch and XmlHttpRequest after page load - still doesn't cover all scenarios, maybe some websites cache the values of fetch and XmlHttpRequest before page load (?)

Combining the chrome extension and fetch overwrite will work.
download an webextension that let you load javascript code before a given page loads, e.g. User JavaScript and CSS
Add the following script to run before your page loads, base on: Intercepting JavaScript Fetch API requests and responses
const { fetch: originalFetch } = window;
window.fetch = async (...args) => {
let [resource, config ] = args;
// request interceptor starts
resource = resource === "https://api.example.com/path2" ? "http://localhost:8000/path2" : resource
// request interceptor ends
const response = await originalFetch(resource, config);
// response interceptor here
return response;
};

Related

Page not changing onClick using useNavigate in React?

I have a very basic UI for a login page:
Upon clicking the LOGIN button, the following methods gets called:
async function loginPatient(){
let item ={username:userName, password};
let result = await fetch("http://localhost:8000/users/login",{
method:'POST',
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
},
body: JSON.stringify(item)
});
alert(result);
alert("breakpoint")
result = await result.json();
localStorage.setItem("user-info",JSON.stringify(result));
nav('/patient')
}
At this point I simply want it to change the page when the button is clicked. My API returns the following information from the database:
To test I did console.log("hello world") in the first line of the function and it works
However, If I run console.log("hello world") after the let result = await fetch(...) part it does not work. How can I test this to see why it's not working ?
Here are the errors from the console:
I did not write the API and do not know how Node works yet, I am just doing the front end for this
The issue is code is never reaching after fetch line, basically request is failing, the error on console is saying the due to CORS issue, the request failed, and in your loginPatient function, you have not handled the failed case, if you just wrap your fetch call inside try/catch block, you will see your code will fall into fail block, as api failed.
You need to enable CORS on your server or backend, Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.
You can read more about cors at:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Looks like your client is on some other domain or port(if your are developing locally) than your server. You need to enable CORS permission for your client url.
And if you are using express for your backend, you can check the following url to enable cors.
https://expressjs.com/en/resources/middleware/cors.html
And last thing why Postman is getting success response, because it is by passing this cors check, as Postman is making request from it's server to your direct server instead of browser.
First initialize you navigation variable as follows
const navigate =useNavigate()
then navigate to you specific route by returning you navigation variable as follows.
return navigation("/");
Happy Coding!

Postman Requests Receive a HTTP 401 Status Code

I am working on creating a Node.js REST API, using the Express module, that redirects HTTP GET and PUT requests to another server. However, when running test queries in Postman, I always get HTTP 401 Unauthorized responses. Yet, when I try the same on query on the Chrome browser I get a successful response (HTTP 302). I read through some documentation on the HTTP request/response cycle and authorization. The server I am redirecting to uses HTTP Basic authentication. In my code I am redirecting the API call to my application server using the res.redirect(server) method. In my Postman request I am setting the username/password in Authorization tab for my request. I know this is gets encoded using base64, but I am guessing this isn't being passed on the redirect when done through Postman.
The following code snippets show what I've created thus far.
This is the Express route I created for GET requests
app.get('/companyrecords/:name', function(req, res) {
var credentials = Buffer.from("username:password").toString('base64');
console.log(req);
var requestURL = helperFunctions.createURL(req);
res.redirect(requestURL);
});
I define a function called createURL inside a file called helperFunctions. The purpose of this function is set up the URL to which requests will be directed to. Here is the code for that function.
module.exports.createURL = function (requestURL) {
var pathname = requestURL._parsedUrl.pathname;
var tablename = pathname.split("/")[1];
var filter = `?&filter=name=\'${requestURL.params.hostname}\'`;
var fullPath = BASE_URL + tablename.concat('/') + filter;
console.log(fullPath);
return fullPath;
}
Where BASE_URL is a constant defined in the following form:
http://hostname:port/path/to/resource/
Is this something I need to change in my code to support redirects through Postman or is there a setting in Postman that I need to change so that my queries can execute successfully.
Unfortunately you can't tell Postman not to do what was arguably the correct thing.
Effectively clients should be removing authorisation headers on a redirect. This is to prevent a man-in-the-middle from sticking a 302 in and collecting all your usernames and passwords on their own server. However, as you've noticed, a lot of clients do not behave perfectly (and have since maintained this behaviour for legacy reasons).
As discussed here however you do have some options:
Allow a secondary way of authorising using a query string: res.redirect(302, 'http://appServer:5001/?auth=auth') however this is not great because query strings are often logged without redacting
Act as a proxy and pipe the authenticated request yourself: http.request(authedRequest).on('response', (response) => response.pipe(res))
Respond with a 200 and the link for your client to then follow.

Check outgoing browser network calls using Cypress.io

On our site, we have Omniture calls that are fired when someone clicks on a link or takes some action. In Chrome DevTools in the network tab, you can see the network request being fired.
Is there a way for Cypress.io to capture outgoing network requests, so we can inspect/parse the URLs? The equivalent to this would be something like Browsermob proxy for webdriver set ups. I want to use Cypress.io to tell it to click the link, but then I want to check the outgoing network request by the browser.
You should be able to use cy.route to wait on and make assertions against network requests:
cy.route({
url:'*omniture.com*',
method: 'POST',
onRequest: (xhr) => {
expect(xhr.request.body).to.eql('somebody')
}
})
If the above doesn't work, it may be because the module is using fetch, which doesn't have native support yet. However, you can just have omniture fallback to XHR by adding this to your cy.visit():
cy.visit('example.com', {
onBeforeLoad: (win) => {
win.fetch = null
}
})
..
or (as you mentioned) you can spy on the omniture global directly
You can use cy.spy() to spy on a global object on your site, here's an example:
cy.visit('example.com')
cy.window().should('have.property', 'omnitureRequest').then(win=>{
cy.spy(win, 'omnitureRequest')
})
(the should() will wait for the object to be present before attempting to spy on it, since the omniture <script> tag could load asynchronously
We found a workaround to our Omniture problem. The request URL is stored in an Omniture property on an object attached to the browser window object. Using cy.window() we can get the window object and access this property and use a node module (querystring) to parse the query string.
We could not find any native way in Cypress.io to inspect network requests.

Security implications for setting document.domain in iframed content

I have two sub domains content and www under the domain example.com. Content from content.example.com is being presented in www.example.com via an iframe.
Because the content on content.example.com needs to communicate to www.example.com I've set document.domain="example.com" and also set allow-scripts and allow-same-origin on the iframe.
I'm concerned that if users can upload the content to be displayed in the iframe it could be exploitable, i.e., send the content of the cookies to a remote domain to hijack the session or other security exploits.
I've setup another domain, www.example2.com and put an AJAX request in the iframed content at content.example.com to test my theory and am sending document.cookie to the remote domain. This results in the _ga cookies being sent to the remote domain. I've allowed header('Access-Control-Allow-Origin: *') on the remote domain so it doesn't cause any issues.
Why are only the _ga cookies being sent? I have a number of other cookies in there on the same domain and path as the _ga cookies yet they aren't sent. Are there other security risks in doing this? Ideally I'd like it only possible for communication between content.example.com and www.example.com and it looks like it's mostly doing this, except for Google Analytics cookies, which will mean that others might be able to do it too.
You can use JSONP to communicate different domains, regardless of cross-domain settings and policies.
However JSONP requires the server side to build the callback function with the returned data as a parameter.
I would suggest to load plain Javascript content from the server, which has the same cross-domain independence and security as a JSON request.
Say you have a Javascript file, data.js, in content.example.com, or a service returning the same content as the file in the response,
with a JSON object, prefixed with a variable assignment:
result = {
"string1": "text1",
"object1": {
"string2": "text2",
"number1": 5.6
},
"number2": 7,
"array1": ["text3", "text4"]
}
Then, in your web page, in www.example.com, you can have a script with function loadJS,
which loads the server response as a script:
var loadJS = function (url, callback) {
var script = document.createElement('script');
script.type = "text/javascript";
script.src = url;
script.onload = function (ev) {
callback(window.result);
delete window.result;
this.parentNode.removeChild(this);
};
document.body.appendChild(script);
};
window.onload = function () {
loadJS('http://content.example.com/data.js', function (data) {
console.log(data);
});
};
Same function can be used in content.example.com for requests in the opposite direction.
In order to set cookies or perform any other functionality available in JS,
the response script, data.js, may contain a function rather than a JSON object:
result = (function () {
document.cookie = "cookie1=Value1; cookie2=Value2;";
return true;
})();

cors+s3+browser cache+chrome extension

Yes. this is a complex question. i will try to nake it brief.
My website fetches resources from s3.
I also have an extension that needs to prefetch that s3 file when someone does a google query, so later when they go on my site ,the resource is cached.
At this point I should probably stress that I'm not doing anything malicious. just a matter of user experience.
My problem is. that making an ajax request to s3 fron the extension (either from content-script or background) doesn't send an origin header.
This means that the resource is downloaded and cached without an allow origin header. s3
doesnt add that allow-origin:* if theres no origin in the request. so later, on my site it fails due to missing allow-origin header in cached file :-(
Any ideas on a better way to prefetch to browser cache?
Is there a way to force the ajax request to send an origin? Any origin?
Since I have an allow-origin:* on my s3 bucket, I think any origin will do accept null.
Thanks
Edit: Ended up using one of Rob W's solutions. You are awesome.
Let me comment on each of the options he suggested:
Not to add the host premissions on my manifest - clever idea but wouldn't work for me since I have a content script which runs on any website, so I must use a catch-all wildcard, and I don't think there is an "exclude" permission option.
I tried it, it issues a null origin, which as expected ends up in S3 sending the allow-origin:* header as required. this means I don't get that "allow-origin header missing" error, however the file is then not served from cache. I guess for it to be actually served from cache in chrome this has to be exactly the same origin. so that was very close but not enough.
third option is a charm. And it is the simplest. I didn't know I was able to manipulate the origin header. So I do that and set the exact origin of my website - And it works. The file is cached and later served from cache. I must stress that i had to add a Url filter to only apply this to requests going out to my s3 bucket, otherwise I expect this would wreak havoc on the user's browser.
Thanks. Well done
You've got three options:
Do not add the host permission for S3 to your manifest file. Then the extension will not have the blanket permission to access the resource, and an Origin request header will be sent with the request.
Use a non-extension frame to perform the AJAX request. For example, the following method will result in a cross-origin GET request with Origin: null.
function prefetchWithOrigin(url) {
var html = '<script>(' + function(url) {
var x = new XMLHttpRequest();
x.open('GET', url);
x.onloadend = function() {
parent.postMessage('done', '*');
};
x.send();
} + ')(' + JSON.stringify(url) + ');</script>';
var f = document.createElement('iframe');
f.src = 'data:text/html,' + encodeURIComponent(html);
(document.body || document.documentElement).appendChild(f);
window.addEventListener('message', function listener(event) {
// Remove frame upon completion
if (event.source === f.contentWindow) {
window.removeEventListener('message', listener);
f.remove();
}
});
}
Use the chrome.webRequest.onBeforeSendHeaders event to manually append the Origin header.

Resources