Technical

TypeScript Doesn't Suck; You Just Don't Care About Security

Hannah Boothe
Application Security Engineer

The introduction of TypeScript elicited a divided reaction from the JavaScript community. Some liked the new superset, which added static and strong typing. Many hate it with a burning passion from a convenience standpoint. However, that doesn’t change the fact it is more secure than JavaScript.  

And, for that reason TypeScript needs a security champion.  

<blog-teal-text>Typed Language Types<blog-teal-text>

TypeScript is a strongly typed language. Languages can generally be divided between strongly and weakly typed. Strongly typed languages require explicit declarations to convert or compare between types. Weakly typed languages, on the other hand, allow comparison and conversion between types without any explicit declarations. Strong languages are better and more secure because they require that extra step in order to convert between languages. They won’t allow you to change a character into a number unless you force it to, this allows for the mitigation of errors and weaknesses such as the CWE-704 Incorrect Type Conversion or Cast.  

TypeScript is also a statically typed language. Whether a language is considered static or dynamic is based on when types are checked within the language. A statically typed language will check types at compile time and a dynamically typed language will check types at runtime. Statically typed languages are more secure because they can find any potential type errors before they are propagated into the program while they are not yet exposed as exploitable vulnerabilities in a production application. 

<blog-teal-text>TypeScript Wins Security<blog-teal-text>

Across the Internet, there is plenty of dislike for TypeScript. Here’s how security wins against the eleven popular reasons developers disapprove of TypeScript.  

<blog-teal-text>1.<blog-teal-text> TypeScript increases project compilation time from 1 second to 3-4 seconds.

As JavaScript does not have a compilation phase, TypeScript increases the time of compilation from zero. As a result, compilation time will increase slightly; however, the application will also have a higher overall performance due to decreased runtime errors.

TypeScript uses the TypeScript compiler (tsc), which checks for errors and compiles TypeScript into JavaScript. Certain compiler options increase security, such as strict and noEmitOnError. The former increases security by turning on a series of flags to force TypeScript to run in a stricter mode. For example, it turns on things such as noImplicitAny, strictNullChecks, and noImplicitThis, all of which protect your application from including unsafe constructs. In addition, the option noEmitOnError prevents the tsc from creating the corresponding JavaScript file if there is an error within the code, increasing security by preventing errors from being compiled into JavaScript code.

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> The compiler finds errors during compile time, preventing them from propagating to runtime.   

<blog-teal-text>2.<blog-teal-text> TypeScript requires a minimum of 30% more code.

While more code is written, you are generating more code to increase security. The extra code defines types and adds type checks and validation functions. Types define what input is allowed into an object; not having types can cause errors and potential security weaknesses. In TypeScript, the types are strings, numbers, and interfaces, which can help define the syntax of future type checks and input validation. Writing extra code upfront to define types and ensure type safety can help save time by preventing developers from having to fix type errors later.  

Fixing type errors in languages without static, strict types is difficult - you have to figure out where in the code the error is generating from, how to fix it, and determine if any other errors resulted from the fix, all without the help of the IDE or the language itself. While in a strict static typed language, it will prevent these issues entirely and report any additional errors during compilation.

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> Types provide a controlled way to define the input you expect for a particular variable or data structure.

<blog-teal-text>3.<blog-teal-text> Dynamic Typing is not a problem.

In fact, dynamic Typing is a security problem, because it only checks types at runtime. Therefore, any security issues are caught while the application is running and not while it is under development. Static typing checks types at compile-time, which prevents type issues before they become vulnerabilities. When you are trying to remediate the source of an error or weakness, it takes longer to edit code in dynamic typing than  in static typing. This is due to the expressive errors and compile-time type checks native to static typing. More expressive errors give more information about where the code's errors lie, such as the file and line number. Checking types at compile time means that errors are found closer to the source, making it easier to return and fix than if the error was found during runtime. 

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> TypeScript provides static typing, which checks types at compile-time, preventing issues before they become vulnerabilities, and giving more expressive errors to aid in the identification and resolution of errors.

<blog-teal-text>4.<blog-teal-text> There is no NEED for types/strict typing.

Types and strict typing are necessary for security purposes. Strict types prevent the reassignment or comparison of types without explicit declarations. It will not let you take the variable x and redefine it from a number to a string. It will not let you take the string variable y and store it into the number variable x, not without an explicit declaration to overrule the language. There are multiple CWE's that can only be corrected and prevented using strict types.  

Taking in incorrect types, incorrect type conversions, and certain other weaknesses are best prevented using strict typing. If strict typing does not exist within a language, then it does not care if a string is entered into a number variable or if a string is transformed into a number. It's not going to give you any warnings or help; the language assumes you know what you're doing. Maybe you think that's a benefit, but it is an extremely unnecessary risk. One developer may love weak typing because they always know what they're doing and never make typing mistakes. But very rarely is an application going to only have one developer, and typing issues very easily slip through the cracks. Why rely on people to catch everything when your language can help?

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> TypeScript uses strict typing, preventing accidental typing mistakes that lead to common weaknesses within an application.

<blog-teal-text>5.<blog-teal-text> TypeScript prevented my API from sending an object without all the suitable properties.

When TypeScript prevents an object from entering the system when it is not of the right type or syntax, TypeScript is doing its job. It’s always safer to validate that the input has all of the correct properties than to allow objects with missing properties into the system. While not being able to send incomplete objects might be annoying, it is always better to know what the object contains and be assured nothing is missing or extra. While TypeScript does allow extra properties in objects due to duck typing, you can use nominal typing instead by using brands in objects. You want to validate all input and not allow any objects that don't contain the correct properties or types into the system.

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> TypeScript forces a level of input validation inherently within types.

<blog-teal-text>6.<blog-teal-text> TypeScript takes more time to learn than JavaScript.

To learn TypeScript, you first must learn JavaScript. The process of learning JS and then TS will be more time-consuming than learning just JS. That’s true. But, the fact that something takes a longer to learn should never be a reason not to learn it. Especially when the lesson is how to add more security to your applications. The time spent learning how to code in TypeScript, especially in learning how to code securely, will help save time later when you have fewer weaknesses, bugs, and vulnerabilities to fix.

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> The time spent learning TypeScript is worth the investment for a security return.

<blog-teal-text>7.<blog-teal-text> TypeScript removes all of my creativity as a developer, forcing the manual typing of everything.

The TypeScript compiler infers types for variables that are initialized. Only uninitialized variables require manual typing. Don’t assume that the compiler will always consider the correct type for a variable; this is how you get type errors within code.  

An example of a type error is when a function accepts only numbers, but it is called with strings. Don’t place creativity above security. Creativity is important; however, customers will not care how creative your code is if it is not also keeping their information safe. You shouldn't care how creative or pretty your code is if it is insecure and breaks every time a username is submitted as input, because the compiler guesses that it was supposed to be a number. There is also an argument to be made that if you cannot figure out how to accomplish your goals while staying within the confines of security then you are not truly trying to be creative, you are just playing around.  

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> TypeScript encourages creativity, pushing you to accomplish your goals while also staying secure.  

<blog-teal-text>8.<blog-teal-text> It is risky to rely on the compiler to check types rather than doing it yourself.

There is risk in both options, relying on the compiler to check the correct usage of the number type or doing it yourself. If you must choose one over the other, rely on the compiler over your own abilities. Assuming you are working with other developers, do not rely on every single developer to avoid type checking mistakes. Another issue in checking all types yourself is that there becomes a point where it is no longer feasible. There becomes a point in which the application is so large that it is no longer possible to check all types manually.

Compilers do not have the same understanding as people and may miss certain things when checking types, or alternatively flag types as wrong when they are correct. However, the compiler will catch more issues and have greater consistency than any individual developer. Redundancy is the best avenue in this instance. A developer should always check types and add tests within the code. The compiler can be used to find additional issues the developer may have missed. 

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> Check types as you write the code and then use the compiler to catch any type issues you may have missed.

<blog-teal-text>9.<blog-teal-text> There is no point in having both type definitions and maintaining types at runtime.

Actually, there is a massive benefit in having both type definitions and ensuring the types are maintained at runtime. Checking at both compile and runtime means that there are two different points in which types are checked. This is enforcing a level of redundancy; there will be two points in which possible type issues can be caught. By having type definitions, you can define types early to find possible within your code. By maintaining types at runtime, you can prevent incorrect types within the input and find issues that the compiler may have missed. Having two separate checks to ensure that the types are interpreted and used correctly will help ensure the application's security.

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> Redundancy in type definitions and maintaining types at runtime will provide an extra net to catch type errors before they become weaknesses.

<blog-teal-text>10.<blog-teal-text> JavaScript gives you freedom.

JavaScript gives you the freedom to do typing how and where you want, but that opens the door for numerous security risks. Freedom is nice when you are the only one working on a personal project, and you want to see what you can do with code. Freedom is not nice when you are one developer on a massive team, and you all work together to write code and maintain the application.  

Freedom means that any one of those developers can do whatever they want in the code. It means that if one developer thinks it is easier to skip type checking or input validation, they can. Or, if someone is in a rush to get something to production, they have the 'freedom' to write insecure code that doesn't protect your application or your customers.  

While peer code reviews that you would hope would catch something like this, these weaknesses and bad code can still make it into production if someone misses it or doesn't look. This is true for many applications, or improper input validation wouldn't be third on the 2020 CWE top 25. You want security to be required everywhere within your system, not ignored because one of the developers on your team doesn't like to be constricted by rules.

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> TypeScript provides a set of constraints regarding typing, which enforces a higher level of security.

<blog-teal-text>11.<blog-teal-text> TypeScript has all the same errors as JavaScript, and TypeScript doesn't solve any problems other than type issues.

TypeScript helps with validation and maintaining type safety, not with other errors. You cannot expect TypeScript to fix problems unrelated to typing; it's not what it was built for. This also isn't any kind of win for JavaScript. This argument is really a win for TypeScript because while it doesn't prevent other issues with JavaScript, it prevents type errors, which is more than JavaScript does.  

It's like saying that you have two cars, and they both have bad engines, but one also has bad brakes. Then, you decide the one with bad brakes is better than the other one simply because it has a bad engine. The fact that the TypeScript car doesn't have bad brakes on top of a bad engine is reason enough to choose it.  The security properties of types are reason enough to choose TypeScript over JavaScript because they help increase your application's security. 

<blog-cyan-text>TypeScript Security Win:<blog-cyan-text> TypeScript helps with validation and maintaining type safety.

CONCLUSION

Many developers across the Internet hate TypeScript. They hate it because it takes away flexibility and freedom. They hate it because it makes them learn and write more code. They even hate it because it doesn't do enough.  

None of these reasons are valuable enough to forgo the security features of TypeScript.  

TypeScript prevents type issues and adds to input validation efforts. Developers spend more time up front to save time later on. It adds constraints and compiler checks to prevent small mistakes by individual developers from adding up into a weakness.  

TypeScript is not a perfect language by any means; it has flaws and can have security issues when used incorrectly. However, it is more secure than JavaScript, and when deciding between the two, TypeScript is what you should choose. 

Isn’t it time to join us in being security champions for TypeScript?

Ready to start your journey?

Let's Talk!