- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Angular application with SSL VPN
Hello,
We have an Angular 5 application deployed at a client's network at http://10.x.y.z/my-app running fine when accessed directly (using hash in Url). Now the client wants to allow access to this application from external users. They configured SSL VPN in Fortigate (Web Mode) and added a bookmark to this internal app. The traffic from external users reaches the app. Resources such as images, css etc. are retrieved correctly. It's when the application itself starts up the problems begin.
Below is the console output. I'm clueless as I have no experience with Fortigate and have no access to the client infrastructure. Is this a Fortigate configuration issue or should I be looking at the application code? Any pointers?
Console output:
sslvpn.js:formatted:646 [Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
XMLHttpRequest.open @ sslvpn.js:formatted:646
(anonymous) @ polyfills.f49c53c5e5719087df48.bundle.js:formatted:2972
o.(anonymous function) @ polyfills.f49c53c5e5719087df48.bundle.js:formatted:1332
open_func @ sslvpn.js:formatted:502
(anonymous) @ main.1b6318b698ef209032cf.bundle.js:formatted:53868
...
window.webpackJsonp @ inline.f8b796da0c03c9b36b0b.bundle.js:formatted:25
(anonymous) @ main.1b6318b698ef209032cf.bundle.js:formatted:1
14:53:31.141 main.1b6318b698ef209032cf.bundle.js:formatted:54139 DOMException: Failed to set the 'responseType' property on 'XMLHttpRequest': The response type cannot be changed for synchronous requests made from a document.
at l._subscribe (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:2224057)
at l._trySubscribe (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:146031)
at l.subscribe (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:145860)
at l.call (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:2477489)
at l.subscribe (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:145798)
at l.call (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:2476297)
at l.subscribe (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:145798)
at n.a (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:2645998)
at n._innerSub (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:2665193)
at n._tryNext (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:2665117)
14:53:31.156 main.1b6318b698ef209032cf.bundle.js:formatted:49196 ERROR DOMException: Failed to execute 'setAttribute' on 'Element': '' is not a valid attribute name.
at Object.set_attr (https://some-domain.com:10443/sslvpn/js/sslvpn.js:1:20146)
at l.setAttribute (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:405810)
at Object.set_attr (https://some-domain.com:10443/sslvpn/js/sslvpn.js:1:20329)
at l.setAttribute (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:332056)
at Object.set_attr (https://some-domain.com:10443/sslvpn/js/sslvpn.js:1:20329)
at pn (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:232325)
at me (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:248003)
at Se (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:255747)
at Me (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:254954)
at me (https://some-domain.com:10443/proxy/1d335b11/http/10.x.y.z/my-app/main.1b6318b698ef209032cf.bundle.js:1:248677)
14:53:31.159 main.1b6318b698ef209032cf.bundle.js:formatted:12375 ERROR Error: Uncaught (in promise): InvalidCharacterError: Failed to execute 'setAttribute' on 'Element': '' is not a valid attribute name.
Error: Failed to execute 'setAttribute' on 'Element': '' is not a valid attribute name.
at Object.set_attr (sslvpn.js:formatted:558)
at l.setAttribute (main.1b6318b698ef209032cf.bundle.js:formatted:18265)
at Object.set_attr (sslvpn.js:formatted:563)
at l.setAttribute (main.1b6318b698ef209032cf.bundle.js:formatted:15297)
at Object.set_attr (sslvpn.js:formatted:563)
at pn (main.1b6318b698ef209032cf.bundle.js:formatted:9892)
===============
in sslvpn.js:
===============
set_attr: function() {
var s, i, v;
if ("object" == typeof arguments[0]) {
if (3 == arguments.length)
s = this.is_url_attributes(arguments[1]) ? this.url_rewrite(arguments[2]) : arguments[2],
arguments[0].setAttribute(arguments[1], s); // <<< ERROR HERE
else
for (i = 2; i < arguments.length; i += 2)
v = i + 1 < arguments.length ? arguments[i + 1] : null,
s = this.is_url_attributes(arguments) ? this.url_rewrite(v) : v,
arguments[0].setAttribute(arguments[1], arguments, s);
return arguments[0]
}
"function" == typeof arguments[0] && 3 == arguments.length && (s = this.is_url_attributes(arguments[1]) ? this.url_rewrite(arguments[2]) : arguments[2],
arguments[0].setAttribute(arguments[1], s))
},
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi vashist,
I have the same problem. With Angular 1, the URL Rewrites of sslvpn.js broke the angular router, there is the patch: https://github.com/d-trattner/Fortinet-Angular
Now I have problems again with angular 5 and I tried to come up with a solution. I tried to replace the functions again (mainly set_attr), but there is always a Stack Exceed Error. I may try other solutions later...
What I tried a few minutes ago, is to obfuscate the function calls, so they do not get replaced by Forti. You would need the following inside the angular 5 project (as a script):
var a5p = {
set_attr: function() {
return "etubirttAtes".split('').reverse().join('');
}
}
Finally, after building (dist), open up vendor, polyfills and any other file that contains the "setAttribute" call. Replace the string ".setAttribute(" with "[a5p.set_attr()]("
Sure, you could just replace ".setAttribute(" with "["etubirttAtes".split('').reverse().join('')](", but I tried to save some chars.
I just got my sample application running using this approach. But in reality, I do not want to modify the dist, so I try to come up with another solution... digging goes on...
BR, Daniel
Edit: Have not found a better solution by now. To speed up the replace solution, one could put a powershell script inside the project dir (if on Win):
#fortipatch.ps1
$files = Get-ChildItem -Path "C:\path_to_angular_app\dist\*" -Include *.js
foreach ($file in $files){
$find = ".setAttribute("
$replace = "[a5p.set_attr()]("
$content = Get-Content $($file.FullName) -Raw
$content.Replace($find, $replace) | Out-File $($file.FullName) -encoding utf8
}
the packacke.json could have the following script added:
"fortipatch": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command ./fortipatch.ps1"
The PS Script adds a BOM to the files, but my application worked, should not have any impact...
After building the app, developer would run...
npm run fortipatch
But, I'm not giving up the search for another solution...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Now I may have found a working solution without having to replace anything:
https://www.npmjs.com/package/javascript-obfuscator
Tested and worked, I just obfuscated vendor and polyfills files.
Here is an example:
https://ourcodeworld.com/articles/read/607/how-to-obfuscate-javascript-code-with-node-js
BR, Daniel
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
After finalising our A5 application, I want to give you a final conclusion, on what happened here and what worked for me:
First, the obfuscation eliminates most issues. I do not have the time to fix the problems, that arise on Fortis side.
Here are the scripts I use for the obfuscation:
obfuscate-dev.js:
var fs = require("fs");
var JavaScriptObfuscator = require('javascript-obfuscator');
var files = [
'./dist/vendor.bundle.js',
'./dist/polyfills.bundle.js'
]
files.forEach(function(file) {
var backup = file + ".bak";
fs.renameSync(file,backup);
fs.readFile(backup, "UTF-8", function(err, data) {
if (err) { throw err; }
// Obfuscate content of the JS file
var obfuscationResult = JavaScriptObfuscator.obfuscate(data);
// Write the obfuscated code into a new file
fs.writeFile(file, obfuscationResult.getObfuscatedCode() , function(err) {
if(err) { return console.log(err); }
console.log(file + " obfuscated!");
});
});
});
obfuscate-prod.js:
var fs = require("fs");
var path = require("path");
var JavaScriptObfuscator = require('javascript-obfuscator');
var dir = './dist/';
fs.readdir(dir, function(err, list) {
if (err) { throw err; }
list.forEach(function(file) {
if(file.slice(-2) === 'js' && (file.substr(0,5) === 'main.' || file.substr(0,10) === 'polyfills.')) {
var filepath = path.resolve(dir, file);
var backup = filepath + ".bak";
fs.renameSync(filepath,backup);
fs.readFile(backup, "UTF-8", function(err, data) {
if (err) { throw err; }
// Obfuscate content of the JS file
var obfuscationResult = JavaScriptObfuscator.obfuscate(data);
// Write the obfuscated code into a new file
fs.writeFile(filepath, obfuscationResult.getObfuscatedCode() , function(err) {
if(err) { return console.log(err); }
console.log(filepath + " obfuscated!");
});
});
}
});
});
Now in the package.json scripts section:
"dev": "ng build --dev --base-href [your basehref] && node obfuscate-dev",
"prod": "ng build --prod --base-href [your basehref] && node obfuscate-prod",
Second, the XMLHttpRequest Error: By the time of working on the application, I had no server-calls, so I just recently stumbled across the other error you mentioned:
Failed to set the 'responseType' property on 'XMLHttpRequest'
This error arises here:
sslvpn.js (injected forti JS), scrolling down to the bottom
try {Because the prototype function gets overwritten, obfuscation cannot do anything.
!function(open) {
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
open.call(this, method, fgt_sslvpn.url_rewrite(url), async, user, pass)
}
}(XMLHttpRequest.prototype.open)
...
...
So I used the fortipatch approach to re-re-write the "XMLHttpRequest.prototype.open" method like that:
if(typeof fgt_sslvpn !== "undefined"){
try {
!function(open) {
XMLHttpRequest.prototype.open = function(method, url, async=true, user, pass) {
open.call(this, method, fgt_sslvpn.url_rewrite(url), async, user, pass)
}
}(XMLHttpRequest.prototype.open)
console.log("Fortis XMLHttpRequest.prototype.open overwritten with async default value = true");
} catch (e) {}
}
The problem lies in the async property, I defaulted that to "true".
Hope that helps, maybe Forti will update the sslvpn.js anytime soon (hopefully).
Edit:
If users are using IE11, default parameters (ES6) are not supported, so use it like that:
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
if(!async) async = true;
open.call(this, method, fgt_sslvpn.url_rewrite(url), async, user, pass);
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
* sry, the forum reply was misleading
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello dant,
I am trying to access an Angular 1 app behind this FortiGate SSL VPN. Basically, the same issue that you and vashist were having. However, I have not faced any routing issues because when the app loads, it loads with errors (see picture) which does not allow me to do or test anything else on the app.
I went over your solution of obfuscation but I don't know if it will apply to my issue. After doing some testing on my own it seems the issue happens with angular code in the html. When I put an expression, , or a ng-click, or ng-model, then the app shows the error on the picture. But when the html is clean of angular code, except for ng-app and ng-controller, the app works fine, and by that I mean that the angular scripts/controllers are loaded and executed correctly.
Any ideas or suggestions? Let me know if you need more info. Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi jblanco, The picture/screenshot is missing. As this solution was tested for A5, please try the following: https://github.com/d-trattner/Fortinet-Angular BR, Dan
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Dant,
Thanks for your work. May I know what build version you guys are using and do you guys already opened a ticket for that?
Regards.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I am trying to post the image but is not uploading it. I will try to use a link instead. https://drive.google.com/...BCxOVofw08CBfSGNxr0vle
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello kurtli,
Build version of what? Angular? And I have not opened a ticket. If I need to open one, how would I go about it?
Thanks.
