Support Forum
The Forums are a place to find answers on a range of Fortinet products from peers and product experts.
vashist
New Contributor

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))
    },

13 REPLIES 13
dant
New Contributor

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...

dant
New Contributor

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

dant
New Contributor

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 {
        !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)
...
...
Because the prototype function gets overwritten, obfuscation cannot do anything.

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);
            }

dant
New Contributor

* sry, the forum reply was misleading

jblanco
New Contributor

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. 

dant
New Contributor

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

kurtli_FTNT

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.

jblanco
New Contributor

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

jblanco

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.

Labels
Top Kudoed Authors