|
23 | 23 | import edu.umd.cs.findbugs.OpcodeStack; |
24 | 24 | import edu.umd.cs.findbugs.Priorities; |
25 | 25 | import edu.umd.cs.findbugs.bcel.OpcodeStackDetector; |
| 26 | +import java.util.regex.Pattern; |
26 | 27 | import org.apache.bcel.Constants; |
27 | 28 |
|
28 | | -import java.util.Arrays; |
29 | | -import java.util.List; |
30 | | - |
31 | 29 | /** |
32 | | - * <p> |
33 | 30 | * This detector mark cipher usage that doesn't provide integrity. |
| 31 | + * <p> |
34 | 32 | * The identification will be made base on the mode use. |
35 | 33 | * </p> |
36 | 34 | * <p> |
|
53 | 51 | * </ul> |
54 | 52 | * </p> |
55 | 53 | * Ref: <a href="http://en.wikipedia.org/wiki/Authenticated_encryption">Wikipedia: Authenticated encryption</a> |
| 54 | + * Ref for the list of potential ciphers: |
| 55 | + * http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#impl |
| 56 | + * Note: Not all ECB mode are vulnerable. see RSA/ECB/* |
56 | 57 | */ |
57 | 58 | public class CipherWithNoIntegrityDetector extends OpcodeStackDetector { |
58 | 59 |
|
59 | 60 | private static final String ECB_MODE_TYPE = "ECB_MODE"; |
60 | 61 | private static final String PADDING_ORACLE_TYPE = "PADDING_ORACLE"; |
61 | 62 | private static final String CIPHER_INTEGRITY_TYPE = "CIPHER_INTEGRITY"; |
62 | 63 |
|
63 | | - private static final boolean DEBUG = false; |
64 | | - private static final List<String> SECURE_CIPHER_MODES = Arrays.asList("CCM","CWC","OCB","EAX","GCM"); |
| 64 | + private static final Pattern AUTHENTICATED_CIPHER_MODES = Pattern.compile(".*/(CCM|CWC|OCB|EAX|GCM)/.*"); |
| 65 | + private static final Pattern INSECURE_ECB_MODES = Pattern.compile("(AES|DES(ede)?)(/ECB/.*)?"); |
65 | 66 |
|
66 | | - private BugReporter bugReporter; |
| 67 | + private final BugReporter bugReporter; |
67 | 68 |
|
68 | 69 | public CipherWithNoIntegrityDetector(BugReporter bugReporter) { |
69 | 70 | this.bugReporter = bugReporter; |
70 | 71 | } |
71 | 72 |
|
72 | 73 | @Override |
73 | 74 | public void sawOpcode(int seen) { |
74 | | - if (seen == Constants.INVOKESTATIC && getClassConstantOperand().equals("javax/crypto/Cipher") && |
75 | | - getNameConstantOperand().equals("getInstance")) { |
76 | | - OpcodeStack.Item item = stack.getStackItem(stack.getStackDepth() - 1); //The first argument is last |
77 | | - if (StackUtils.isConstantString(item)) { |
78 | | - String cipherValue = (String) item.getConstant(); |
79 | | - if (DEBUG) System.out.println(cipherValue); |
80 | | - |
81 | | - //Ref for the list of potential ciphers : http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#impl |
82 | | - //Note: Not all ECB mode are vulnerable. see RSA/ECB/* |
83 | | - if (cipherValue.contains("AES/ECB/") || cipherValue.contains("DES/ECB/") || cipherValue.contains("DESede/ECB/")) { |
84 | | - bugReporter.reportBug(new BugInstance(this, ECB_MODE_TYPE, Priorities.HIGH_PRIORITY) // |
85 | | - .addClass(this).addMethod(this).addSourceLine(this)); |
86 | | - } |
87 | | - |
88 | | - if (cipherValue.contains("/CBC/PKCS5Padding")) { |
89 | | - bugReporter.reportBug(new BugInstance(this, PADDING_ORACLE_TYPE, Priorities.HIGH_PRIORITY) // |
90 | | - .addClass(this).addMethod(this).addSourceLine(this)); |
91 | | - } |
92 | | - |
93 | | - String[] cipherDefinition = cipherValue.split("/"); |
94 | | - if(cipherDefinition.length > 1 && !cipherValue.startsWith("RSA/")) { //Some cipher will not have mode specified (ie: "RSA" .. issue GitHub #24) |
95 | | - String cipherMode = cipherDefinition[1]; |
| 75 | + if ((seen != Constants.INVOKESTATIC |
| 76 | + || !getClassConstantOperand().equals("javax/crypto/Cipher")) |
| 77 | + || !getNameConstantOperand().equals("getInstance")) { |
| 78 | + return; |
| 79 | + } |
| 80 | + OpcodeStack.Item item = stack.getStackItem(stack.getStackDepth() - 1); |
| 81 | + String cipherValue; |
| 82 | + if (StackUtils.isConstantString(item)) { |
| 83 | + cipherValue = (String) item.getConstant(); |
| 84 | + } else { |
| 85 | + return; |
| 86 | + } |
| 87 | + if (INSECURE_ECB_MODES.matcher(cipherValue).matches()) { |
| 88 | + reportBug(ECB_MODE_TYPE); |
| 89 | + } |
| 90 | + if (cipherValue.contains("/CBC/PKCS5Padding")) { |
| 91 | + reportBug(PADDING_ORACLE_TYPE); |
| 92 | + } |
96 | 93 |
|
97 | | - if (!SECURE_CIPHER_MODES.contains(cipherMode)) { //Not a secure mode! |
98 | | - bugReporter.reportBug(new BugInstance(this, CIPHER_INTEGRITY_TYPE, Priorities.HIGH_PRIORITY) // |
99 | | - .addClass(this).addMethod(this).addSourceLine(this)); |
100 | | - } |
101 | | - } |
102 | | - } |
| 94 | + //Some cipher will not have mode specified (ie: "RSA" .. issue GitHub #24) |
| 95 | + if (!AUTHENTICATED_CIPHER_MODES.matcher(cipherValue).matches() |
| 96 | + && !cipherValue.startsWith("RSA")) { |
| 97 | + reportBug(CIPHER_INTEGRITY_TYPE); |
103 | 98 | } |
104 | 99 | } |
| 100 | + |
| 101 | + private void reportBug(String type) { |
| 102 | + bugReporter.reportBug(new BugInstance(this, type, Priorities.HIGH_PRIORITY) |
| 103 | + .addClass(this).addMethod(this).addSourceLine(this)); |
| 104 | + } |
105 | 105 | } |
0 commit comments