[00:00.000 --> 00:14.360] Hello, everybody. Can you hear me? That's cool. I'm Andrei Yenich. I will talk today [00:14.360 --> 00:20.360] about situ by accident and the slides are to be published under the Creative Commons [00:20.360 --> 00:31.440] Attribution 4.0 license. I am a freelancer since last month. I'm doing web development [00:31.440 --> 00:38.320] and consulting. I'm available on Mastodon, for example. I'm on other places. So if you [00:38.320 --> 00:44.160] have questions, please get in touch with me either afterwards, preferably outside or on [00:44.160 --> 00:53.200] Mastodon. Who is the target audience? I expect you to have some knowledge about Angular, [00:53.200 --> 00:59.960] about TypeScript and Webpack, because I can't go into this. It's not enough time for it. [00:59.960 --> 01:07.280] I'm interested in security and performance. If you are, we are in a good shape. What will [01:07.360 --> 01:14.400] you learn today? I'm going to something which I will get to in a minute, and I show you [01:14.400 --> 01:21.560] which steps I take to reproduce it, help you to understand the results of a Webpack build, [01:21.560 --> 01:28.240] and then go into more details, because you could see the child routes from Angular in [01:28.240 --> 01:37.440] the result, and I want to mention how you can protect them, what benefits it can have, [01:37.440 --> 01:48.640] and how code splitting works and helps in that. A few words about how that came to be. [01:48.640 --> 01:55.840] I was approached by Pechida last year, and was researching something for security and [01:55.840 --> 02:03.000] had questions about Angular, because that's not her expertise area. I explained how Angular [02:03.000 --> 02:09.240] works, what the different files are meant, how to read it, and learned about which information [02:09.240 --> 02:14.400] you better not include in an Angular build without prior authentication and authorization. [02:14.400 --> 02:25.160] So we have four more seats here. This presentation will focus on Angular, but the issue at hand [02:25.200 --> 02:31.080] is not limited to Angular. It will help you react as well. It's not something the framework [02:31.080 --> 02:40.080] can help you with, because it's a responsibility that lies with you as an app developer. I [02:40.080 --> 02:46.400] used Angular for this presentation. It's a minimal application, and to help me read [02:46.440 --> 02:51.840] the webpack build, I used Pretia, which is also quite nice. [02:51.840 --> 03:00.840] So I started with a brand new Angular project, used the recommended approach to use ng new, [03:00.840 --> 03:07.520] called foster, because that was something I had in mind when I was writing the slides, [03:07.520 --> 03:11.720] and because we have to deal with Angular, the documentation isn't complete, so I had [03:11.760 --> 03:18.760] to install some more dependencies to be able to generate a build on Dev Server environment. [03:21.760 --> 03:27.320] As a result from the build, we get several files. For this presentation, I just looked [03:27.320 --> 03:34.320] at the JavaScript files, but I can also quickly go through what are the files meant for them. [03:35.320 --> 03:44.320] We have several files, for example, in HTML, that is a minimal app, so that means it contains [03:44.480 --> 03:51.480] a bare minimum of HTML you need to know to have to load the Angular files and some CSS, [03:53.520 --> 04:00.520] and have the JavaScript file loaded we saw before. Then we have some size.something.css, [04:01.360 --> 04:08.360] which is empty because at that point, we don't have styles applied to components, otherwise [04:09.440 --> 04:15.940] they would go in here. The hash is generated by a webpack at that point. We have a runtime [04:15.940 --> 04:22.940] file that contains the Angular runtime that is that those parts Angular needs to pass [04:24.360 --> 04:29.840] the template and manage the dependency injection and everything. Mostly not interesting from [04:29.840 --> 04:36.840] what we are interested in. We have polyfills that contain something like the zones, certain [04:37.480 --> 04:44.480] promises features as polyfills, so if some browser doesn't support it, it gets added [04:44.480 --> 04:51.480] to the global namespace, and we have the main file, which is mainly what we as an app developer [04:52.480 --> 04:59.480] wrote, but also some boilerplate code for example for RHS or some other template parsing elements. [05:03.560 --> 05:10.560] So what's the case right now? I looked at the routing file, the routing module, that's [05:11.120 --> 05:18.120] how it's generated by Angular, and we are mainly interested in that routes variable over here. [05:25.200 --> 05:30.280] So I looked up what is the type strip definition for a route that's not complete, it's just [05:30.280 --> 05:37.280] a partial of it. We are mainly interested in those properties pass, especially the pass [05:37.960 --> 05:44.960] and the component, which is basically used if you don't use child routes or just have [05:46.000 --> 05:53.000] a mapping from a pass segment to a component, but that's also helpful to have RyderX, it's [05:53.080 --> 06:00.080] also something you will add early on to have a catch all route or RyderX to a 404 page. [06:00.600 --> 06:07.600] What is interesting for this presentation is also the children property, which is another [06:08.600 --> 06:15.600] area of routes, and load children, which is used for lazy loading other route segments, [06:19.000 --> 06:26.000] so you can activate in front of it to guard it, that means you have some check if the currently [06:26.560 --> 06:33.560] user allowed to access that route. If you then load into what is produced by Angular, [06:35.960 --> 06:41.480] you get some Java profile and can look for something with ng generate component, because [06:41.480 --> 06:48.480] that's part of the index HTML that gets generated, and that's the main app component that gets [06:48.480 --> 06:55.480] generated by the boilerplate code, and that's the entry point above that line, it's Java [06:58.760 --> 07:04.760] webpack boilerplate code, so you can just ignore it for now. Below that is that what [07:04.760 --> 07:11.760] you as an author wrote. Next, I created some components a patient found, I thought about [07:13.160 --> 07:17.440] two more components, for example, the speaker component and the slides component, and let's [07:17.480 --> 07:23.520] say we want to protect the speaker component for some reason, if you then generate a new [07:23.520 --> 07:29.640] build, you will see no other changes to it, so it's identically why, because the build [07:29.640 --> 07:35.200] gets reshaken by a webpack, that means that if you don't load those components, they will [07:35.200 --> 07:42.200] not be part of the build. Let's include them then, we want to have them as routes, so I [07:43.200 --> 07:50.200] extend the routing module, declare a route for the slides and for the speakers, right [07:52.400 --> 07:59.400] now just without children, therefore I also have to import them. I added the imports because [07:59.920 --> 08:06.280] when I look at Angular documentation, I often find that the documentation isn't complete, [08:06.280 --> 08:13.280] which is a bummer, so if you want to reproduce it yourself, I give you the necessary hints [08:14.280 --> 08:21.280] to follow along. So I added that route, I just re-run the Angular build command and [08:23.200 --> 08:29.200] gbuild and look into the results and then, for example, I see that the JavaScript now [08:29.200 --> 08:36.200] contains some more lines, the variable names or identifiers might change because Angular [08:36.280 --> 08:40.800] on repack is mainly in the variables, but it has something like this structure, for [08:40.800 --> 08:47.320] example, for the slides or directly below the speakers, so that's what Angular makes [08:47.320 --> 08:54.320] out of your route definition. We also have a mapping from the path to the components, [08:54.360 --> 09:00.480] the components where the function calls before and that's how you can read it. So if you [09:00.560 --> 09:07.560] are a security researcher, that is what you would look at and try to make sense of it. [09:08.360 --> 09:14.360] I feel it's helpful to have, okay, we have the result now, how would I translate it back? [09:14.360 --> 09:21.360] Reverse engineer, so to speak. So the next thing we can add is general catch all paths [09:21.480 --> 09:28.480] that redirects to the slides page to some kind of landing page or index page or for everything [09:31.480 --> 09:38.480] else that can't be mapped, we have a page not found component. So now I want to go into [09:43.960 --> 09:50.960] more details about how I would actually see the components that I had and the index that [09:51.440 --> 09:57.320] the component HTML really bloated and it tells you with an HTML command that you can drop [09:57.320 --> 10:02.320] certain paths and if you just remove everything within that diff container with the road [10:02.320 --> 10:07.320] of main, with something more semantically, you could actually see, okay, what's happening [10:07.320 --> 10:14.320] here. So now we have updated our component. Now we have updated our component. Now we [10:21.360 --> 10:28.360] have our own HTML and I can look into the guarding and into defining child routes and [10:31.880 --> 10:38.880] I would like to have some kind of model. So I'm going with a template-driven form here. [10:43.440 --> 10:50.120] It might also be possible to use a reactive form, but most of the Angular applications [10:50.160 --> 10:55.680] I worked with use the template-driven form, so I'm more familiar with that. And just [10:55.680 --> 11:00.840] defined a model here. It's called auth with some string that is used as password. It's [11:00.840 --> 11:07.840] just for demonstration purposes. You wouldn't use this kind in actual application, I hope. [11:11.960 --> 11:17.040] Now we have defined it in the components part. We should also update the template and here [11:17.120 --> 11:24.120] it's like I removed that speaker work and replaced it with some basic HTML. Still no [11:24.400 --> 11:31.400] styling because that's not relevant for what I'm doing here. I added a form element and [11:31.640 --> 11:38.640] basic here and input and to demonstrate, okay, we have something working here. I show the [11:38.640 --> 11:45.640] link to the access to the slide subpath once a form is valid. And here we have a template [11:48.040 --> 11:55.040] here with a type of password. So the browser is using that obfuscation with dots or stars [11:55.320 --> 12:02.320] or what have you. And you have a model that's helpful for Angular as well. So it's a two-way [12:02.640 --> 12:09.640] data binding. And once it's valid, you get access to that anchor. So last thing you should [12:10.720 --> 12:15.720] have is because when you have child routes, you should have a route outlet that tells [12:15.720 --> 12:22.720] Angular where to display that child route. So now we have updated the template. The next [12:24.560 --> 12:31.560] step will be to also update the route definition. So back to the routing module. I extended [12:33.440 --> 12:40.440] that speaker route. It's now not having a mapping from past component, but it's also [12:40.880 --> 12:47.880] declared, okay, we have some child components here in this application. It would be slash [12:48.080 --> 12:53.880] speaker slash slide to access that child component. It's still rendering the parent, the speaker [12:53.880 --> 13:00.880] component, but also the slide component in the route outlet. So at that point, it will [13:02.840 --> 13:09.840] still be possible to look at the bundle and see, okay, we can animate, we can animate [13:10.600 --> 13:16.080] every route in the application. So as a security researcher, I know, okay, this is the places [13:16.080 --> 13:23.080] I have to look at and check for security. Going more into protecting the thing, the first [13:23.600 --> 13:29.600] thing I would do is defining a new route for the speaker module because that's part of [13:29.600 --> 13:36.600] the application I want to protect. Angular CLI offers you a new way to generate the [13:37.600 --> 13:44.600] boilerplate for that as well with the ng-generate module, which is the name of the module. I [13:44.800 --> 13:51.800] want to have a router as well, and I want to have it as a sub-module of app module. Once [13:52.480 --> 13:59.480] that speaker routing module is defined, you can update the routing module of the app and [14:00.480 --> 14:07.480] replace everything with your head there with just the path and then tell it to lazy load [14:08.480 --> 14:15.480] the speaker module. I will get to that in a minute, later on. So now I lazy load the whole [14:19.480 --> 14:26.480] speaker module, and I have to redo the slide sub-path there. So I go into that, load the [14:27.480 --> 14:34.480] slides component, and okay, I want to have the slides module as a sub-component of the [14:37.480 --> 14:43.480] speaker module, and the path for the speaker module is here, is an empty string because [14:43.480 --> 14:50.480] I already am in the speaker path from the parent component. It's also important to remove [14:51.480 --> 14:56.480] that speaker component from the app module definition because otherwise Angular will be [14:57.480 --> 15:04.480] set, it can't have that in two places. So once I generate a new build, I will now see that I [15:08.480 --> 15:14.480] have a second part about lazy shine files which has some hash and some other hash. I come [15:15.480 --> 15:20.480] back to that as well, and it has a speaker module because that's what we defined, and we have [15:21.480 --> 15:26.480] some sizes. So I can tell, okay, right now I really have some part of my application that gets lazy [15:27.480 --> 15:34.480] loaded. So that means it will only be loaded once that path segment is entered. Now I told you [15:36.480 --> 15:42.480] that I would like to protect that path segment, so I define a route in guard, and GCLI has [15:43.480 --> 15:50.480] a guard schema for that as well, so I run ng generate or ngg guard and name it something. Here [15:51.480 --> 15:56.480] it's usually something that can activate, can deactivate, or the other types of guards you [15:57.480 --> 16:03.480] have. You get a small interactive prompt for what do you want the guard to be able to do, and here [16:04.480 --> 16:12.480] it's for protecting the speaker. What I receive is that I get some boilerplate code provided by the [16:13.480 --> 16:19.480] GCLI again, and I follow the documentation about how to define guards, define the user token [16:20.480 --> 16:26.480] and permissions stuff, right now it's just returning to always, you would have some more checks here. [16:27.480 --> 16:34.480] For example, is there some JSON token set in the local search or whatever you want. I also [16:35.480 --> 16:39.480] described that the documentation isn't complete here, because either you have to export those two [16:40.480 --> 16:48.480] classes, or you have to declare them as injectable. Once those two classes are defined, you can have [16:49.480 --> 16:55.480] them somewhere else, you need to inject them into the guard itself, declare that the guard [16:56.480 --> 17:02.480] implements a can activate interface, that means it has to have a public function called can [17:03.480 --> 17:12.480] activate, that takes active route snapshot as the one part, and returns an observable whatever, not [17:12.480 --> 17:18.480] relevant for our case. I just want to use that injected permissions here and check that can [17:19.480 --> 17:25.480] activate a method on it and handle the current user and route patterns. That's some way you can do, and [17:26.480 --> 17:32.480] then you can check the case. I want to enter that route with the user, is he or she allowed to do that? [17:33.480 --> 17:39.480] In our case for demonstration, I just return true, so it's always allowed, but you can also set it to [17:39.480 --> 17:48.480] false to test it on your thing and discover that you get redirected to the index page, those slides. [17:49.480 --> 17:55.480] Now that we have a guard, I have to update the app routing module, because I want to protect the [17:56.480 --> 18:06.480] speaker, and that means it gets a new line for the can activate, which is a list of guards, in our [18:06.480 --> 18:13.480] case only one, and I also have to tell us that there are some providers for those injected [18:14.480 --> 18:19.480] dependencies, therefore it's important to have those two classes, permissions and user tokens [18:20.480 --> 18:29.480] exported as well, otherwise I couldn't use them here. And now I'm at the part where I would like to [18:29.480 --> 18:36.480] use the name chunk, magic comments from repack, and after my pitch and I got accepted, I discovered [18:37.480 --> 18:45.480] hey, Angular doesn't support that, but you can turn on the AngularJSON property in the, or the name [18:46.480 --> 18:51.480] chunk property in AngularJSON, which would give you not the hashes, but the whole filename that you [18:52.480 --> 19:00.480] would have in a depth server, and therefore you could then apply some more security measures, I will [19:01.480 --> 19:07.480] get to that on the next slide. For those of you who don't know what name chunk is, when you have that [19:08.480 --> 19:15.480] import statement for lazy loading, you can have a JavaScript comment, which is called repack name chunk, [19:15.480 --> 19:25.480] and declare another name and repack will use that one instead of the generated hash. So my idea is that [19:26.480 --> 19:35.480] you have certain files that are on your server then as static applications, and you can add HTTP [19:35.480 --> 19:45.480] headers, for example, content security policy, you can also look at the documentation about Angular and [19:46.480 --> 19:53.480] it says you have to have especially unsafe inline for strips, which is bad, because it allows more for [19:54.480 --> 20:01.480] attacks. If you can't use content security policy, you still have the ability to declare it as a [20:01.480 --> 20:08.480] char hash or as a nonce, which is a bit more labor, but in my experience, I work with companies who [20:09.480 --> 20:17.480] deploy it every other week or so, that's doable to compute a hash and add it to the HTML. The idea is that [20:18.480 --> 20:24.480] nobody else is able to crawl that route of the JavaScript chunk and look at two more, because for [20:24.480 --> 20:31.480] example, you could have a list of certain passwords, that was the case of Percadia, and if you would like to attack [20:32.480 --> 20:37.480] that application, you can just exclude those passwords, because they won't be valid anyway, and therefore you [20:38.480 --> 20:44.480] would aid with credential stuffing that is trying out passwords and credentials you found somewhere else, or [20:45.480 --> 20:51.480] you can exclude your brute force attack, because you know it has to have this length or the special characters are [20:51.480 --> 20:58.480] allowed or not allowed, and therefore you would make the criminal's life easier, so I would like to have that not [20:59.480 --> 21:06.480] part of the bottle, but loaded from some JSON file or something else, which is then be able to be protected. [21:09.480 --> 21:17.480] So basically having some authorization for certain information and only give more information once that user [21:17.480 --> 21:28.480] is authenticated, for example, by an authorization header and the response. So what have you learned today? [21:30.480 --> 21:38.480] I hope you get a bit better understanding about how the result looks like, what Angular is producing, what [21:38.480 --> 21:46.480] name chunks are in Angular, you learned a bit about content security policy, which is really important, there's [21:47.480 --> 21:56.480] a documentation on MDN as well, and learned about some ways to secure static files. I used creative comments images [21:57.480 --> 22:04.480] and provide the resources with the exception of the profile picture of Percadia, but I got the written permission that I am [22:04.480 --> 22:08.480] allowed to use that one. Thank you. Do you have questions?