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))
},
Nominating a forum post submits a request to create a new Knowledge Article based on the forum post topic. Please ensure your nomination includes a solution within the reply.
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...
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
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);
}
* sry, the forum reply was misleading
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.
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
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.
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
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.
Select Forum Responses to become Knowledge Articles!
Select the “Nominate to Knowledge Base” button to recommend a forum post to become a knowledge article.
User | Count |
---|---|
1733 | |
1106 | |
752 | |
447 | |
240 |
The Fortinet Security Fabric brings together the concepts of convergence and consolidation to provide comprehensive cybersecurity protection for all users, devices, and applications and across all network edges.
Copyright 2024 Fortinet, Inc. All Rights Reserved.