V5340. OWASP. Code contains invisible characters that may alter its logic. Consider enabling the display of invisible characters in the code editor.
The analyzer has detected characters in code that may confuse a developer. These characters can be invisible and change the way the code is displayed in IDEs. Combinations of such characters can cause a human and a compiler to interpret the code differently.
In some cases, this is done intentionally. This type of attack is called Trojan Source. See the following articles for more details:
- Trojan Source attack for introducing invisible vulnerabilities;
- Trojan Source: Invisible Vulnerabilities.
|
Character |
Code |
Definition |
Description |
|---|---|---|---|
|
LRE |
U+202A |
LEFT-TO-RIGHT EMBEDDING |
The text after the LRE character is treated as embedded text and displayed left-to-right. The LRE action is interrupted by the PDF character or a newline character. |
|
RLE |
U+202B |
RIGHT-TO-LEFT EMBEDDING |
The text after the RLE character is treated as embedded text and displayed right-to-left. The RLE action is interrupted by the PDF character or a newline character. |
|
LRO |
U+202D |
LEFT-TO-RIGHT OVERRIDE |
The text after the LRO character is forcibly displayed left-to-right. The LRO action is interrupted by the PDF character or a newline character. |
|
RLO |
U+202E |
RIGHT-TO-LEFT OVERRIDE |
The text after the RLO character is forcibly displayed right-to-left. The RLO action is interrupted by the PDF character or a newline character. |
|
|
U+202C |
POP DIRECTIONAL FORMATTING |
The PDF character interrupts one of the characters encountered earlier: LRE, RLE, LRO, or RLO. It interrupts the last encountered character. |
|
LRI |
U+2066 |
LEFT‑TO‑RIGHT ISOLATE |
The text after the LRI character is displayed left-to-right and interpreted as isolated. This means that other control characters do not affect the display of this text fragment. The LRI action is interrupted by the PDI character or a newline character. |
|
RLI |
U+2067 |
RIGHT‑TO‑LEFT ISOLATE |
The text after the RLI character is displayed right-to-left and interpreted as isolated. This means that other control characters do not affect the display of this text fragment. The RLI action is interrupted by the PDI character or the newline character. |
|
FSI |
U+2068 |
FIRST STRONG ISOLATE |
The text direction after the FSI character is set by the first control character not included in this text fragment. Other control characters do not affect the display of this text. The FSI action is interrupted by the PDI character or a newline character. |
|
PDI |
U+2069 |
POP DIRECTIONAL ISOLATE |
The PDI character interrupts one of the characters encountered earlier: LRI, RLI or FSI. It interrupts last encountered character. |
|
LRM |
U+200E |
LEFT-TO-RIGHT MARK |
The text after the LRM character is displayed left-to-right. The LRM action is interrupted by a newline character. |
|
RLM |
U+200F |
RIGHT-TO-LEFT MARK |
The text after the RLM character is displayed right-to-left. The RLM action is interrupted by a newline character. |
|
ALM |
U+061C |
ARABIC LETTER MARK |
The text after the ALM character is displayed right-to-left. The ALM action is interrupted by a newline character. |
|
ZWSP |
U+200B |
ZERO WIDTH SPACE |
An invisible space character. The use of the ZWSP character causes different strings to be displayed the same way. For example, |
Look at the following code snippet:
boolean isAdmin = false;
/*[RLO] } [LRI] if (isAdmin)[PDI] [LRI] begin admins only */ // (1)
System.out.println("You are an admin.");
/* end admins only [RLO]{ [LRI]*/ // (2)
Now, take a closer look at the (1) line:
[LRI] if (isAdmin)[PDI]
The [LRI] character here is valid up to the [PDI] character. The if (isAdmin) string is displayed left-to-right and is isolated. We get if (isAdmin).
[LRI] begin admins only */
The [LRI] character here is valid to the end of the string. We get the begin admins only */ isolated string.
[RLO] {space1}, '}', {space2}, 'if (isAdmin)', 'begin admins only */'
The [RLO] character here is valid to the end of the string and displays the text right-to-left. Each of the isolated strings obtained in the previous paragraphs is treated as a separate, indivisible character. We get the following sequence:
'begin admins only */', 'if (isAdmin)', {space2}, '{', {space1}
Note that the closing brace character is now displayed as { instead of }.
The final version of the (1) line that can be displayed in the editor:
/* begin admins only */ if (isAdmin) {
Similar transformations affect the (2) line, which is displayed like this:
/* end admins only */ }
The code fragment that can be displayed in the editor:
boolean isAdmin = false;
/* begin admins only */ if (isAdmin) {
System.out.println("You are an admin.");
/* end admins only */ }
A reviewer may think that some check is done in the code before displaying the message. They will ignore the comments and think that this is how the code should run:
boolean isAdmin = false;
if (isAdmin) {
System.out.println("You are an admin.");
}
However, there is no any check. This is how the code looks for the compiler:
boolean isAdmin = false;
System.out.println("You are an admin.");
Now, look at a simple yet dangerous example of using invisible characters:
enum BlockCipherType { DES, TripleDES, AES };
BlockCipherType getBlockCipherType(String cipherTypeName) {
if (cipherTypeName.equals("AES[ZWSP]")) {
return BlockCipherType.AES;
} else if (cipherTypeName.equals("TripleDES[ZWSP]")) {
return BlockCipherType.TripleDES;
} else {
return BlockCipherType.DES;
}
}
The getBlockCipherType method converts the string to one of the BlockCipherType enumeration values. Looking at the code, one might conclude that the function returns three different values, but this is not the case. Due to the addition of an invisible [ZWSP] space character at the end of each string literal, equality checks with the AES and TripleDES strings will be false. As a result, the function will return only BlockCipherType.DES out of three expected values. At the same time, the code in the editor may look like this:
enum BlockCipherType { DES, TripleDES, AES };
BlockCipherType getBlockCipherType(String cipherTypeName) {
if (cipherTypeName.equals("AES")) {
return BlockCipherType.AES;
} else if (cipherTypeName.equals("TripleDES")) {
return BlockCipherType.TripleDES;
} else {
return BlockCipherType.DES;
}
}
If the analyzer issued a warning about invisible characters in the code, enable the display of invisible characters in your editor and make sure that they do not change the execution logic of the program.
This diagnostic is classified as:
|