App & Browser Testing Made Easy

Give your users a seamless experience by testing on 3000+ real devices and browsers. Don't compromise with emulators and simulators

Get Started free
Home Guide Maximizing Web Security with Cypress: A Step-by-Step Guide

Maximizing Web Security with Cypress: A Step-by-Step Guide

By Hamid Akhtar, Community Contributor -

All browsers follow the same-origin rule religiously by default. 

This suggests that the browser will obstruct communication between <iframes> if the origin policies of the two are incompatible. The same-origin policy serves as a crucial security measure that limits the ability of a script or document from one origin to interact with resources from a different origin. This mechanism aids in safeguarding against potentially harmful documents and minimizing potential attack avenues. Cypress requires constant direct communication with your remote applications because it operates entirely within the browser. However, the default setting of browsers will work to stop Cypress from doing this. 

By using JavaScript code, the internal APIs of the browser, and network proxying, Cypress engages a number of techniques to get around this restriction and adhere to the same-origin policy’s guidelines. Cypress strives to fully automate your application without requiring you to make any code modifications.

By Using Cookies

The cookie context changes when an iframe is deemed cross-origin to the browser’s top URL. Cypress needs to regard the URL of your web app as the top URL when it runs inside of Cypress. This is taken care of by proxy behind the scenes, giving you a seamless procedure for attaching and setting cookies, such as document.cookie

Here are some in-depth instances of what Cypress does:

  • Inject document.domain into text/html pages.
  • Proxy all HTTP / HTTPS traffic.
  • By modifying the hosted URL, the application being tested will now use the same URL.
  • When dealing with network-level traffic, use the internal APIs of the browser.
  • The internal Cypress web application will be loaded first on a random port, similar to http://localhost:65874/_/

Cypress will update its URL to match the origin of our remote application when you issue the first cy.visit() command in a test, removing the first stumbling block posed by the same-origin policy. Everything will now function as expected because your application’s code will run the same way it does outside Cypress.

How does Cypress support HTTPS?

Cypress puts a lot of effort into the background so that you can test HTTPS sites. It gives you the ability to manage and stub network level. In order to be able to change the traffic in real-time, Cypress must assign and manage browser certificates.

The ‘SSL certificate does not match’ warning that Chrome displays will be apparent to you. This warning is proper and appropriate. This warning appears as a result of the fact that Cypress secretly functions as its own CA authority and then dynamically generates a certificate in order to intercept requests that would otherwise be impossible to access. Other traffic is not affected by this because it only applies to the superdomain that is presently being tested.

Despite the best efforts of the Cypress team, there are still some restrictions that you must be aware of.

Superdomain

All URLs accessed during our test must share the same superdomain since Cypress changes the URL of its own host to match the one used by our application.

If you attempt to access two separate superdomains, Cypress will throw an error. Superdomains can be visited, but only in different tests, never in the same test again.

it('navigates', () => {
cy.visit('https://www.cypress.io')
cy.visit('https://docs.cypress.io')
})
it('navigates', () => {
cy.visit('https://testdocs.com')
cy.visit('https://sampleapp.com') // error
})

it('navigates', () => {
cy.visit('https://testdocs.com')
})

// splits visiting different origin in another test
it('navigates to new origin', () => {
cy.visit('https://sampleapp.com') // good
})

Although Cypress will make an effort to enforce this restriction, there is a chance that your application will bypass Cypress’s capacity to notice it.

Listed below are a few test situations where superdomain restrictions will result in an error:

  • .click() an <a> that points to another superdomain. What’s the point of clicking and opening another app, you should pause to consider.
  • .submit() a <form> that directs your web server to a different superdomain.
  • Put a JavaScript redirect in your application, such as window.location.href = “…”, to a different superdomain.

In each of these scenarios, Cypress is unable to automate your program and will immediately give an exception.

Example usage of cy.origin() and cy.session()

The following example shows a cross-origin failure in a trivial way:

it("navigates", () => {
cy.visit("https://apple.com");

cy.visit("https://google.com");

});

You will discover an error.

You may change this test to make use of the new cy.origin() command in order to effectively traverse across different domains:

it("navigates", () => {
cy.visit("https://apple.com");
cy.origin("https://google.com", () => {});
});

Keep in mind that in order for this to function, you must first set the experimentalSessionAndOrigin flag in your cypress.config.js file to true.

A more thorough illustration of how to use cy.origin() to switch between two separate domains is provided in the example that follows:

describe("Two different URLs", function () {
it("Opens URLs", () => {
cy.visit("First URL");
cy.contains("Home");
cy.visit("first-url-subpage");
cy.origin("Second URL", () => {
cy.visit("/login"); //Do something on the second domain; log in as an example
cy.get("#login_field").
type("USERNAME HERE"); //Insert credentials
cy.get("#password").
type("PASSWORD HERE");
cy.get("input").
contains("Log In").
click();
});
});
});

Instead of having to give login information before each test, the cy.session() command is used here to cache session information between tests. cy.origin() and this command work well together.

In the event of cross-origin navigation, the new Cypress instance running inside the expected same-origin iframe determines whether or not the page has loaded and then executes your Cypress commands

Cross-origin iframes

Cypress won’t be able to automate or connect with an embedded “iframe” that is a “cross-origin frame.”

Although you won’t have native access to the iframes from within Cypress, Cypress can handle the scenarios in a similar manner to how Selenium does.

In order to interact with and control these iframes directly, a workaround is to try using window.PostMessage.

Unsafe Content

By default, Cypress will generate an error if you try to navigate back to an HTTP site while testing an HTTPS site. This behavior contributes to exposing a severe security vulnerability in your application.

Here is an instance of how to access unsafe content:

Let’s say you have a test code that looks like this:

''cy.visit('https://myapp.org')'''

You set the cookies and save a session on the browser in your application code. Let’s now assume that our application code contains a single unsafe link (or JavaScript redirect).

<html>
<a href="http://myapp.org/page3">Page 3</a>
</html>
The following code will fail in Cypress:
// Test code
cy.visit(?https://myapp.org?)
cy.get('a').click() // will fail

By default, browsers will not show insecure content on secure pages. And when the browser followed the href to http://myapp.org/page3, Cypress modified its URL to match https://myapp.org, and the browser declined to show the contents.

This behavior is a security issue in your application that Cypress is exposing to you rather than a flaw in Cypress. If the secure flag on a cookie is not set to true, the cookie will send its contents as clear text to an unsecured URL, making your application susceptible to session hijacking. You could believe that forcing a 301 redirect back to the HTTPS site will fix the issue, but this is untrue.

The original HTTP request leads to the exposure of insecure session information.

You will need to tweak your HTML and JavaScript code to prevent navigation to unsecured HTTP pages and exclusively utilize HTTPS in order to fix this issue. You should also make sure that the secure flag is set to true for cookies.

You can bypass this Cypress restriction by disabling web security if you don’t have access to the code or if there is no other way to get around it.

Port

For the duration of a single test, Cypress requires that the URLs visited to use the same port. The same-origin policy of the browser operates in a manner similar to this.

Workaround to fixing cross-origin problems

Let’s look into how cross-origin problems could appear in your test code and break down how to fix them in Cypress. You can directly send a cy.request() to it.

External Navigation

This issue most frequently occurs when you click on a <a> that directs you to another superdomain.

<html>
<a href="https://google.com">Google</a>
</html>
// Test code
cy.visit('http://localhost:5000') // where your web server + HTML is 
cy.get('a').click() // hosted browser attempt load googledotCom-CypressErrors

It is not advised to access a superdomain in your tests that you are not in control of.

Instead, you can check to see if the HREF attribute is accurate.

cy.visit('http://localhost:5000')
cy.get('a').should('have.attr', 'href', 'https://google.com') // no page load!

However, if you are concerned about Google.com displaying the correct HTML text, you can directly send a cy.request() to it. CORS and the same-origin policy are NOT constrained by cy.request().

cy.visit('http://localhost:5000')
cy.get('a').then(($a) => {

const url = $a.prop('href')

// make a cy.request to it
cy.request(url).its('body').should('include', '</html>')
})

If you still need to go to a separate origin URL, you could choose to disable web security.

Form Submission Redirects

In any case, the browser follows the HTTP(s) request when you submit a standard HTML form.

<html>
<form method="POST" action="/submit">
<input type="text" name="email" />
<input type="submit" value="Submit" />
</form>
</html>
cy.visit('http://localhost:5000')
cy.get('form').submit() // submits the form!

You will experience a cross-origin error if your back-end server, which manages the /submit route, performs a 30x redirect to a different superdomain.

// your localhost:5000 server

app.post('/submit', (req, res) => {
// redirect the browser to google.com
res.redirect('https://google.com')
})

Single sign-on is a prevalent use case for this. You can then POST to a different server and get redirected somewhere else in this scenario (typically with the session token in the URL).

If that’s the case, you can use cy.request() to test this behavior. 

Cypress might skip the first visit entirely and POST directly to your SSO server.

cy.request('POST', 'https://samplesso.com/auth', { username: 'foo', password: 'bar' })
.then((response) => {
// pulls out the location redirect
const loc = response.headers['Location']

const token = parseOutMyToken(loc)

cy.visit('http://localhost:5000?token=' + token)

cy.visit(loc)
})

You should think about deactivating web security if you still need to be able to be redirected to your SSO server.

Using JavaScript Redirects

The following is an example of a JavaScript redirect.

window.location.href = 'http://sample.superdomain.com'

As it frequently results from another source, this is arguably the most difficult case to test. You’ll need to ascertain the cause of your JavaScript code’s redirection. The setup may need to be handled elsewhere if you are not logged in.

If you want to use the code to access a different superdomain in the future, you might think about deactivating web security.

Turning off Web Security

If none of the problems can be resolved using the aforementioned solutions, you might want to think about turning off web security.

{
"chromeWebSecurity": false
}

Only browsers built on Chrome support disabling web security. 

Many well-known websites use a method known as framebusting to thwart clickjacking and iframe hosting. Some of these methods make it difficult for Cypress to operate efficiently. Because of this, the modifyObstructiveCode option is now available and activated by default.

Closing Notes

Cypress has native access to every object because it runs inside your application. You have access to everything in your Cypress tests, whether it is the window, the document, a DOM element, your application instance, a function, a timer, a service worker, or anything else. Cypress has a fresh stability mechanism built in, which is why it tests so well without flaking and users may smoothly go to any origin at any moment thanks to a new Cypress mechanism.

Tags
Cypress

Featured Articles

Cross Browser Testing with Cypress : Tutorial

Understanding Cypress HTML Reporter

App & Browser Testing Made Easy

Seamlessly test across 20,000+ real devices with BrowserStack