Same Origin Policy. Is that really necessary?
SOP is an important security concept in browsers. Put simply, SOP allows client-side programming languages, such as JavaScript, only access to resources in the same domain. First picture shows the problem. SOP is very important for internet applications, because you want to prevent, that everybody can access to your services and content. However, this regulation is often unnecessary and obstructive in enterprise environments. Because it’s no option to turn down your browser security config, I will show you, how you can solve this problem in different ways.
1. Use JSONP to make cross-domain Ajax requests
In my previous post, I explained how you can call a web service from a different domain with jQuery, without getting security issues. This approach is sometimes very useful and easy to implement. But when you can’t enhance your services to provide JSONP (or maybe you just refuse to do this), there are some other solutions to handle this problem.
2. Establish a Reverse Proxy with Apache HTTP Server
2.1 The theory
A reverse proxy is a special kind of a web server. This text passage from the official documentation explains very precisely the features:
A reverse proxy (or gateway) appears to the client just like an ordinary web server. No special configuration on the client is necessary. The client makes ordinary requests for content in the name-space of the reverse proxy. The reverse proxy then decides where to send those requests, and returns the content as if it was itself the origin.
A typical usage of a reverse proxy is to provide Internet users access to a server that is behind a firewall. Reverse proxies can also be used to balance load among several back-end servers, or to provide caching for a slower back-end server. In addition, reverse proxies can be used simply to bring several servers into the same URL space.
The last sentence is very important for us to prevent SOP. The following picture shows the enhanced infrastructure.
- SAPUI5-Apps will no longer be available under http://myapps.com/, but under http://central.com/apps/.
- SAP NetWeaver will no longer be avaiable under http://saphrp.mycompany.com/, but under http://central.com/saphrp/.
- IIS will no longer be avaiable under http://otherservices.com/, but under http://central.com/otherservices.
All systems are in the same domain, so you can call services without getting security issues.
2.2 The practice
Now i will show you, how you can test the functionality of a reverse proxy on your local PC. I will use Windows 7 as operating system and the windows version of Apache HTTP Server. Before we start, just a short note: If you are going to work with Apache HTTP Server in your company, i recommend to use UNIX/Linux as OS and not Windows. Besides, you will need a system engineer or a web-admin to create a complete environment with a thorough configuration. Important issues are security, load-balancing and caching.
2.2.1 Download Apache HTTP Server
As you can see on this site, you can choose different kind of options for deploying Apache httpd on Windows. We will use the server of Apache Lounge. Go to this site and download the latest version Apache win32 binaries (e.g. httpd-2.4.4-win32.zip).
2.2.2 The Setup
After downloading, unzip the Apache<version> folder to C:\Apache<version> (that is the ServerRoot in the config). When you unzip to an other location, you have to do some changes in the configuration. For testing, it’s better to use C:\Apache<version> (in my example: C:\Apache24).
2.2.3 Start the Server
After that, open your console and go to C:\Apache24\bin and start the httpd.exe. When everything is fine, you should see a console like this.
Now the server is running. The message AH00558 is just a warning. To fix this, go to C:\Apache24\conf and open the httpd.conf file (this is the place, where all important configurations are done). Search for ‘ServerName’ and remove the hash at the start of line (so it’s no longer a comment). For our test, you can choose any kind of name, for exaple:
ServerName www.test.com:80
When you now restart the Server (press Ctrl+C in console and start httpd.exe again), the message should be disappeared.
2.2.4 Test
Open your browser an call http://localhost. You should see a simple “It works!”. When you have problems to start your server, make sure that no other service is running under port 80. Besides, be sure that you have installed the Visual C++ 2010 SP1 Redistributable Package x86. You can download it from here. If you want to check an error.log for more information, go to C:\Apache24\logs and open error.txt.
2.2.5 Activating Modules for Reverse Proxy
Apache supports a variety of features, many implemented as compiled modules which extend the core functionality. To activate the basic reverse proxy features for HTTP, go to C:\Apache24\conf and open httpd.conf. Remove the hash at LoadModule proxy_module modules/mod_proxy.so and LoadModule proxy_http_module modules/mod_proxy_http.so. Restart your server.
2.2.6 Adding a simple Configuration
Let’s say, we no longer want to call the start page of amazon.com over http://www.amazon.com but over localhost/amazon. Add in your httpd.conf at the end of file this two lines:
ProxyPass /amazon http://www.amazon.com/
ProxyPassReverse /amazon http://www.amazon.com/
Restart your server and go to localhost/amazon. You should see the start page of amazon.com! And that’s it!
2.3 Some more Information
This was just a tiny and minimal configuration for reverse proxy functionality. For enterprise environments, you will need a lot of more configuration in order to cover all requirements. Some issues of this solution, which can be solved by adding additional modules and configuration, are listed here:
- When you click on a link on localhost/amazon, you will get a HTTP 404 Error. You have to ensure, that all resources of a domain are available over your reverse proxy.
- For Java applications i recommend to use modules, which handle the communication between reverse proxy and server over a specific protocol. For Java-Servers (Tomcat, Jetty etc.) you can use mod_proxy_ajp or mod_jk, which implement the Apache JServ Protocol.
- For SSL-support, you need further modules like mod_ssl.
- If you want to deliver a query string to an application server(this is normally very important for SAPUI5-Apps), you will need a rewrite engine like mod_rewrite.
Beside AJP and HTTP, you can use a reverse proxy for other protocols, too. Currently there are modules for AJP, HTTP, CONNECT(for SSL), FastCGI, ftp and SCGI.
3. Cross-origin resource sharing
An other approach to solve SOP is Cross-origin resource sharing (CORS). This article of mozilla developer network explained very well the characteristics of CORS. Here a little extract:
Cross-site HTTP requests initiated from within scripts have been subject to well-known restrictions, for well-understood security reasons. For example HTTP Requests made using the XMLHttpRequest object were subject to the same-origin policy. In particular, this meant that a web application using XMLHttpRequest could only make HTTP requests to the domain it was loaded from, and not to other domains. Developers expressed the desire to safely evolve capabilities such as XMLHttpRequest to make cross-site requests, for better, safer mash-ups within web applications.
The Cross-Origin Resource Sharing standard works by adding new HTTP headers that allow servers to describe the set of origins that are permitted to read that information using a web browser.
To test this feature, look at the header-data of this service: http://ip.jsontest.com/.
When you call this service with jQuery without JSONP, you will still get an answer (you can test this call right here). This is only possible because of the response header “Access-Control-Allow-Origin”.
This solution means, that you don’t have to establish an additional server instance in your infrastructure. Instead, you have to enhance your services with HTTP-Headers. There are some ways, how you can achieve this.
3.1 Example for ICF-based Services
When you create simple HTTP-Services with ICF, you can add very easily additional header information. Besides, you can implement this feature in an own HTTP-Handler-Class, which will be added to your Handler-List in SICF.
SERVER->response->set_header_field(
EXPORTING
name = 'Access-Control-Allow-Origin'
value = '*'
).
3.2 Example for a Java-Filter
When you can’t configure your Java-Server to add new headers, you can write a simple filter for your web-application to add CORS-Headers.
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class CORSFilter implements Filter {
public CORSFilter() {
}
public void init(FilterConfig fConfig) throws ServletException {
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
((HttpServletResponse) response).addHeader(
"Access-Control-Allow-Origin", "*");
chain.doFilter(request, response);
}
}
This is the enhancement for your web.xml.
<filter>
<filter-name>CORSFilter</filter-name>
<filter-class>CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<url-pattern>/service/*</url-pattern>
</filter-mapping>
3.3 Adding Header-Field with Server-Config
It depends on your server, if it’s possible and how easy it is to add custom header-fields. A short interview with your admins should give you more information.
I hope, all this information will help you to get rid of SOP. I wish you success!
Nice Article! Very good and in-depth explanation of solving SOP issue which is faced by lots of developers.
Superb article .It will be very helpful to developers .
good one. 🙂
Can you tell me how to do reverse proxy on SAP NetWeaver Gateway services? I tried doing reverse proxy on one of the demo gw service offered by SAP but it doesn’t work. I can reverse proxy other gw services (public ones….those with http not with https). Thanks.
Hi Melanie (such a beautiful name)
if you are using Apache HTTP server you can do the following:
I hope, this information will help you further.
Regards Michael
Great blog Michael ! Thanks for the valuable information in this blog. I am actually struggling with a cross domain AJAX call and would be grateful if you can give your inputs here. We have an external url through which we access all our Webb Apps like portal and Web UI. I have created a web app which would be integrated in Web ui that uses AJAX calls. Now I am using CORS=true in my webapp which makes cross domain AJAX call a success but my problem is with https to http calls. The external url is https and the AJAX url is http and because of this my web service fails. The client do not want to expose services using external url. Do u know how to handle such scenarios ?
Thanks,
Dhananjay
Hi Dhananjay,
can you tell me, in which browser the web app is executed? This information is very important, because Internet Explorer up to version 9 supports CORS through XDomainRequest. Compared to XMLHttpRequest, this object has a number of additional security restrictions, which could be the cause of your problem.There are several solutions for this issue:
Hope this helps!
Regards Michael
Thanks for the prompt response Michael. The end users are using ie8 although I have suggested them to upgrade to ie9. Having said that my issue is with HTTPS to HTTP calls. The cross domain call is working atleast in ie10 but the https to http call is not working in ie irrespective of the ie version (not even in ie10). The parent application is HTTPS and the AJAX url is HTTP. I will look into the convoluted workaround. Thanks again.
SOP concept is very useful information to new techies.nice blog.
finally my problem got resolved.
Regards,
Prasad.
Thanks for the lovely article. Helped clear all concepts regarding Reverse Proxy.
But unfortunately, this this work in my case.
I am using Tomcat as UI5 container which is calling Odata Service from Netweaver Gateway Server. Everything is working fine on Desktop and Laptop.
Just as I run from device (Android/ IOS) it loads the UI but is unable to fetch data from Backend.
Any suggestion will be very helpful.
Thanks in advance
Hi Purnima,
as far as I can gather, your application is hosted on tomcat and your OData-Services on SAP Netweaver Gateway. Does that mean, that app and services are running in different domains? In which way access mobile devices this environment (e.g.: are they calling the same URL’s)? Are there any JavaScript errors or security warnings in mobile browsers, when you run your app on Android/iOS? Can you see a service call in your server-side logs, when you try to fetch data from a mobile device? It could be very helpful to examine the corresponding request header.
If you can provide these information, it’s easier for me to estimate this problem.
Regards
Michael
Hi Micheal,
I figuired out the problem. Reverse proxy is working fine now. The issues got resolved by maintaining two seperate configurations – one for Tomcat and one for NW Gateway.
Thank you so much for your post which helped resolve Cross Domain Issues!
Hi Purnima / Mike,
I ran into the same problem and I tried various options but nothing worked for me. Just wondering how you resolved this issue?
My application is running on tomcat on my system. I am using external NW Gateway using HTTPS protocol.
My original error with
var pUrl = “https://sapes1.sapdevcenter.com:443/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/“;
var pUsername = “P250368”;
var pPassword = “Sai4SAP5”;
var oModel = new sap.ui.model.odata.ODataModel(pUrl, false, pUsername, pPassword, {“Access-Control-Allow-Origin”:”*”});
<VirtualHost *:80>
ServerName localhost
Redirect /sampleflight https://sapes1.sapdevcenter.com:443/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/
</VirtualHost>
ProxyPass /ui5test http://tomcat.local.com:8085/SITMIL
ProxyPassReverse /ui5test http://tomcat.local.com:8085/SITMIL
And changed the service url as: var pUrl = “http://localhost:80/sampleflight“; //(this is perfectly reaching the destination if I directly test through browser.
This time I am getting the below error when I run the application:
Hi Sai,
the approach with CORS-Header will definitely not work, because this meta-data always needs to be set in the response-header. So they are not a part of the request! Because you are using NW Gateway, i recommend the second option (reverse proxy).
A possible problem could be the config of your VirtualHost. http://sapes1.sapdevcenter.com should be the target domain for redirecting (try at first without SSL). Don’t add an additional relative path to your host, because you want to redirect from
http://localhost/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/$metadata
to
http://sapes1.sapdevcenter.com/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/$metadata
So you just want to change the domain under certain conditions. Besides, check out this post.
Regards
Michael
Thanks for this valuable Information!
Additionally, for local testing, you can start the Google Chrome browser with the option “–disable-web-security”, which turns off the Same Origin Policy and enables you to consume any OData service in you UI5 application.
When doing this, however, it is best not to open any other websites in Chrome as you are not protected from cross-site-scripting anymore.
Kind Regards,
Elias Mistler
Hi
Very well-written blog and insightful discussion. Thanks alot. Regarding solution 3.3. (server configuration for CORS directive in HTTP response header), there is a good overview of server settings at http://enable-cors.org/server.html.
However, I haven’t yet found any information on how to do this on an SAP NetWeaver AS Java. Did anyone have luck there without resorting to a custom Java servlet filter?
I know for sure that it isn’t supported with the SAP Web Dispatcher, since it cannot modify HTTP response headers. It appears that SAP’s standard recommendation is to use an Apache as a reverse proxy in front of client-facing applications, but that is hardly ever practical at customers running SAP due to the lack of qualified admin resources (it’s not a standard skill of Basis admins to set up and secure XAMPP stacks).
Thanks and regards
Matt
Hi Matt,
yeah, you’re dead right!! Customers, who have only experience and know-how in SAP-technologies, can really getting in trouble, when they are faced with SOP. You always need good system engineers for a comprehensive enterprise solution with DEV, TEST and PROD environments.
Sry, but i have no clue how to configure CORS headers on AS Java. The only way i know is over the mentioned filter respectively, a Reverse Proxy.
Regards
Michael
Hi,
the Apache Reverse Proxy is a nice idea, but there are two problems/ issues which I couldn’t solve yet:
a) the ProxyPass string is limited to 96 characters, a usual Service Url like ‘https://myservername.subdomain.domain.tld/sap/bc/srt/rfc/sap/z_itsm_function_xxx_yyy/001/z_itsm_function_xxx_yyy/…‘ is too long for that routine.
b) when using certificate authentication on the backend (also quite usual scenario) how do I configure apache to forward the client certificate to the SAP Backend?
Any help is highly appreciated.
Oliver
Hi Oliver,
1.) I’ve never get into this, because we are always working with /* patterns. So we are defining: “Everything under ‘mydomain/sap’ is getting routed to ‘targerserver/sap’. Is there any reason, why you can’t use a wildcard-pattern?
2.) Yeah, that’s right, certificate authentication is not easy to implemented. Can you check out this article: Tricks to do client certificate authentications behind a reverse proxy
Regards
Michael
Hi Michael,
thanks for your reply.
1. By using that method, is it still possible to route to different targets depending on a defined criteria?
What I need is, e.g. mydomain/webservice1 routes to https://myservername1.subdomain.domain.tld/sap/bc/srt/rfc/sap/z_itsm_function_xxx_yyy/001/z_itsm_ticket_function_xxx_yyy/…
and mydomain/webservice2 routes to https://myservername2.subdomain.domain.tld/sap/bc/srt/rfc/sap/z_itsm_function_xxx_yyy/001/z_itsm_ticket_function_xxx_yyy/…
Maybe you can show me an example of how you would realize that with Apache?
2. I’ve found that link before, but haven’t figured out how it could help as this might work with an Apache or Tomcat as backend, but not with an SAP Netweaver Application Server. Or am I missing something?
Thanks,
Oliver
Hi,
thanks for the very useful article. The CORS-Variant seems to be the most elegant. Is there a more in depth tutorial/article about that?
Thanks again!
Hi Sai,
Did you solve your problem. I faced the same as yours, if yours has been solved, maybe you can share your solutions? Thanks!
Regards,
Daisy
Hi Daisy,
If you really want to ask a question, please create a new Discussion. It will receive much more visibility from those who might have the answers.
Regards, Mike (Moderator)
SAP Technology RIG
Hi Michel,
Great solution for all time painstaking SOP problem.
Is there anyway we can use this solution behind a corporate proxy server/firewall?
Thanks
Hi Michael,
I am using your 3.2 approach but i am again getting error message attach below-
Hi Michael
I have the following scenario:
With help of ABAP Developer we implemented CORS adding a class in the HTTP handler of “/default_host/sap/opu/odata”
Within this class ZJSONP_HANDLER, we added the following:
server->response->set_header_field(
EXPORTING
name = ‘Access-Control-Allow-Origin’
value = ‘*’ ).
server->response->set_header_field(
EXPORTING
name =’Access-Control-Allow-Methods’
value =’GET, HEAD, OPTIONS, POST’).
server->response->set_header_field(
EXPORTING
name = ‘Access-Control-Allow-Headers’
value = ‘accept, content-type, x-csrf-token’).
server->response->set_header_field(
EXPORTING
name =’Access-Control-Expose-Headers’
value =’origin, x-csrf-token’).
server->response->set_header_field(
EXPORTING
name =’Access-Control-Max-Age’
value =’1728000′).
server->response->set_header_field(
EXPORTING
name =’Access-Control-Allow-Credentials’
value =’true’)
After this we run the report and trace the flow in ABAP code seems to flow ok.
Anyway we’re getting this in chrome:
Looks like the headers are exposed but we still have this preflight (OPTIONS) when we send a GET or POST request.
Could you give some advice on how to solve the preflight from server side (with ABAP on ICF)?
Best Regards
Erick
Hi Eric,
just trying exactly the same – have you solved your problem?
Thanks,
ben
Hi Ben
we’ve been trying to enable CORS, by handling the preflight in ICF-ABAP but there’s a point where the the x-csrf-token is missing (seems to be due a validation process where an anti-token (cookie) must be passed)
. so we were stucked at this point.
Anyway we got support from other platfrom colleagues to enable web dispatcher on top of BOE server to work as a reverse proxy approach (not easy for them too, due SSO certificates, stickyness and other things I’m not experienced).
Best Regards
Erick
@Erick
The solution you post here will work if you make the service anonymous. The problem is SAP implementation is broke here (from what I can tell). SAP should not be asking for login credentials during a preflight request (OPTIONS method). I struggled with this also and using your code (with minor modifications) I was able to get it to work only after I made my service anonymous (unfortunately not my final solution).
Here is an expert from stackoverflow.
——————-
@Cornel Masson, did you solve the problem? I do not understand why your server is asking you to authenticate the OPTIONS request, but I am facing this same issue against a SAP NetWeaver server. I have read the whole CORS specification (I recommend) so I can clarify you some of your doubts.
About your sentence
In the Angular app I explicitly set the following request headers. AFAIK this setting for withCredentials should ensure that credentials are sent even for OPTIONS requests:
http://stackoverflow.com/questions/26296779/chrome-v37-38-cors-failing-again-with-401-for-options-pre-flight-requests
—————-
All requests appear to be intercepted somewhere higher in the stack being sent back with “401 – Not Authorized.” I haven’t found this code yet, but on the hunt for it.
FYI I have read though many posts and this SDN post (in regards to SAP) is the most comprehensive CORS solution when interacting with SAP through web services. Excellent post and discussion here. I would love to here other solutions here.
Hi Clark,
i am exactly stuck at the same step as you are. Did you find a solution without using an anonymous service in the meantime?
I am able to add any header to my response when calling my service directly in Chrome, by adding my custom handler in transaktion “SICF”, path “/default_host/sap/opu/”. As soon as i am calling any service by a sapui5 app Chrome is sending those “preflight”-requests described by you that can not be handled.
Regards
Lukas
For audience who came here to check how to get UI5 proxy servlet and model instantiation from manifest file working,
After lot of headbanging, the below worked for me:
1. Change your UI5 proxy servlet setting like below:
SimpleProxyServlet
com.sap.ui5.proxy.SimpleProxyServlet
com.sap.ui5.proxy.REMOTE_LOCATION
https://mydbpXXXXXXXXXtrial.hanatrial.ondemand.com
SimpleProxyServlet
/proxy/*
2. manifest file, data source:
“dataSources”: {
“ds_employee_xs”: {
“uri”: “../proxy/dev_pkg/services/myservice.xsodata”,
“type”: “OData”,
“settings”: {
“odataVersion”: “2.0”
}
}
While posting the comment, it removed some piece of code in step 1.
In your web.xml file, change ‘context-param’ to ‘init-param’ (where you mention the REMOTE_LOCATION and your host and port) and put the complete section just before tag closing.
More details on http://paperstreetenterprises.com/ui5-using-a-service-from-a-remote-server/
Was this 400:CORS error solved. how?