The document discusses potential security vulnerabilities in Java applications. It describes 7 categories of common security errors called "Pernicious Kingdoms": Security Features, Time and State, Input Validation and Representation, API Abuse, Code Quality, Encapsulation, and Environment. The document then examines examples of code from a sample application called Plimsoll and identifies specific vulnerabilities that fall into some of these categories, such as lack of input validation, potential for API abuse, and error handling issues.
Locking the Doors -7 Pernicious Pitfalls to avoid with Java
1. Locking the Doors : 7 Pernicious
Pitfalls to avoid with Java
Steve Poole
JAX 2018
2. About me
Steve Poole
IBM Lead Engineer / Developer advocate
Making Java Real Since Version 0.9
Open Source Advocate
DevOps Practitioner (whatever that means!)
Driving Change
4. This talk is about
security
• It’s about how to start thinking about
secure application design
• It’s about why this really matters
• It’s a taster only
6. In 2016 Cybercrime was
estimated to be worth
450 Billion Dollars
@spoole167
Organized Cybercrime is the most profitable type of crime
In 2016 The illicit drug trade
was estimated to be worth
435 Billion Dollars
7. Organized Cybercrime is the most profitable type of crime
• Guess which one has the least risk to the criminal ?
• Guess which is growing the fastest ?
• Guess which one is the hardest to prosecute ?
• Guess which one is predicted to reach 2100 Billion Dollars by 2019?
• Guess which one is predicted to reach 6000 Billion Dollars by 2021?
@spoole167
10. Cyber attackers use vulnerabilities to get into your system
And go through your system to another (and repeat)
To steal your data To change your data To crash your systems Use your compute power
11. Vulnerabilities are almost always simple
there are no smoking guns
Exploits are chaining together vulnerabilities
https://www.flickr.com/photos/84744710@N06/
12. This talk is giving you a
taster for how you can
design your software
to be less vulnerable
to attack
14. A bit of a marketing
exercise
• 2005 Fortify Software released a
paper called
“Seven Pernicious Kingdoms: A
Taxonomy of Software Security
Errors”
• It caught on among the security
community
19. Authentication, access control, confidentiality,
cryptography, privilege management.
Unexpected interactions between
threads, processes, time, and information
(shared state)
Errors related to error handling
Problems caused by metacharacters,
alternate encodings and numeric
representations. General security problems
result from trusting input.
Using an API in a manner contrary to its
intended use
Insufficient encapsulation of critical data or
functionality
Everything that is outside of the source
code but is still critical to the security of the
application
Time and State
Poor code quality leading to unpredictable
behaviour and opportunities to stress the
system in unexpected ways.
Errors
API abuse
Code Quality
Security Features
Encapsulation
Input Validation and Representation
Environment
20. Talk Flow
Using a real scenario
walk though some of
the 7PK looking at
code and thinking
about design
21. My scenario
• I’m developing a tool for Adopt OpenJDK
• Make it easy for others to get the new
drivers
• Make it easy to try out the new Java
versions with common app servers and user
applications.
• It’s designed to run locally
24. Problems caused by metacharacters, alternate encodings and
numeric representations. General security problems result from
trusting input.
Input Validation and Representation
25. Input Validation and Representation
Forms Input.
Browser javascript code validates the form
Send the data to the server over a websocket as JSON.
Server turns JSON into Java object assuming it’s a valid
PlimsollCommand object
public class PlimsollCommand {
public String type;
public Object msg;
}
{ ‘type’ : cmd_id , ‘msg’ : {} }
26. Input Validation and Representation
Forms Input.
Browser javascript code validates the form
Send the data to the server over a websocket as JSON.
Server turns JSON into Java object assuming it’s a valid
PlimsollCommand object
public class PlimsollCommand {
public String type;
public Object msg;
}
Actually I expect a Map but
Object is easier to use with
the JSON parser
{ ‘type’ : cmd_id , ‘msg’ : {} }
27. Input Validation and Representation
My application only expects local war files to be loaded…
{ ‘type’ : ‘upload’ ,
‘msg’ : { ‘war’ : ‘file://dir/app.war’ }
}
This example gets converted into java objects
quite easily. Lets the user point me at their
application on disk. What happens if the
browser sends me this?
{ ‘type’ : ‘upload’ ,
‘msg’ : { ‘war’ :
‘http://badserver.com/badapp.war’ }
}
28. Input Validation and Representation
Forms Input.
Browser javascript code validates the form
Send the data to the server over a websocket as JSON.
Server turns JSON into Java object assuming it’s a valid
PlimsollCommand object
public class PlimsollCommand {
public String type;
public Object msg;
}
What would happen if the
JSON data was actually a list?
{ ‘type’ : cmd_id , ‘msg’ : [ ‘a’ , ‘bad’ , ‘thing’ ] }
29. Input Validation and Representation
Forms Input.
Browser javascript code validates the form
Send the data to the server over a websocket as JSON.
Server turns JSON into Java object assuming it’s a valid
PlimsollCommand object
public class PlimsollCommand {
public String type;
public Object msg;
}
Pretty obviously you cannot ever
trust what a client sends you. Ever.
What would happen if the
JSON data was actually a list?
30. • Input from an end point is an obvious place to be cautious
• Think of it like this…
• Every API you write. Whether REST interface or a simple class defines
a ‘contract’ with the caller.
• If that contract is not explicit and enforced your code is vulnerable.
• And it’s even more subtle than that…
Input Validation and Representation “It is the callers responsibility”
35. What’s wrong with this code?
public classDockerCommandLineDriver{
privatestaticfinalStringDEFAULT_EXEC ="/usr/local/bin/docker";
privateStringdocker;
privatebooleanerror;
privateFileoutputFile;
privateListresults=newLinkedList();
staticenumCMD{
active,images,containers
};
privatestaticfinalString[]cmds =newString[]{ "ps-a", "images","containerls -a"};
public DockerCommandLineDriver(){
this(DEFAULT_EXEC);
}
public DockerCommandLineDriver(Stringdriver){
docker= driver;
outputFile= newFile("/tmp/docker.out");
}
Input Validation and Representation
36. This field is never validated.
• Does it really point to a docker executable?
• What would happen if it was set to some
• other executable?
• Could simply run a version check on it!
public classDockerCommandLineDriver{
privatestaticfinalStringDEFAULT_EXEC ="/usr/local/bin/docker";
privateStringdocker;
privatebooleanerror;
privateFileoutputFile;
privateListresults=newLinkedList();
staticenumCMD{
active,images,containers
};
privatestaticfinalString[]cmds =newString[]{ "ps-a", "images","containerls -a"};
public DockerCommandLineDriver(){
this(DEFAULT_EXEC);
}
public DockerCommandLineDriver(Stringdriver){
docker= driver;
outputFile= newFile("/tmp/docker.out");
}
“docker –version”
Input Validation and Representation
37. Even if the field was validated
during construction it can still be
changed afterwards.
Relying on constructor validation
only is also poor practise.
public classDockerCommandLineDriver{
privatestaticfinalStringDEFAULT_EXEC ="/usr/local/bin/docker";
privateStringdocker;
privatebooleanerror;
privateFileoutputFile;
privateListresults=newLinkedList();
staticenumCMD{
active,images,containers
};
privatestaticfinalString[]cmds =newString[]{ "ps-a", "images","containerls -a"};
public DockerCommandLineDriver(){
this(DEFAULT_EXEC);
}
public DockerCommandLineDriver(Stringdriver){
docker= driver;
outputFile= newFile("/tmp/docker.out");
}
(Re)validate just before usage
Input Validation and Representation
38. What about this field?
Set to “/tmp/docker.out” ?
public classDockerCommandLineDriver{
privatestaticfinalStringDEFAULT_EXEC ="/usr/local/bin/docker";
privateStringdocker;
privatebooleanerror;
privateFileoutputFile;
privateListresults=newLinkedList();
staticenumCMD{
active,images,containers
};
privatestaticfinalString[]cmds =newString[]{ "ps-a", "images","containerls -a"};
public DockerCommandLineDriver(){
this(DEFAULT_EXEC);
}
public DockerCommandLineDriver(Stringdriver){
docker= driver;
outputFile= newFile("/tmp/docker.out");
}
Input Validation and Representation
39. With serialisation vulnerabilities I
may be able to get the code to
overwrite a critical file somewhere
else.
If I modify both the exe and file
fields I can control what data gets
written to your system
Remember constructors are not
called during deserialization. The
data is simply inserted.
public classDockerCommandLineDriver{
privatestaticfinalStringDEFAULT_EXEC ="/usr/local/bin/docker";
privateStringdocker;
privatebooleanerror;
privateFileoutputFile;
privateListresults=newLinkedList();
staticenumCMD{
active,images,containers
};
privatestaticfinalString[]cmds =newString[]{ "ps-a", "images","containerls -a"};
public DockerCommandLineDriver(){
this(DEFAULT_EXEC);
}
public DockerCommandLineDriver(Stringdriver){
docker= driver;
outputFile= newFile("/tmp/docker.out");
}
Input Validation and Representation
40. #1: all data received must be considered
untrusted and potentially dangerous.
• Data means anything that
comes in – command lines,
env variables, sockets, GUI,
REST , even data coming
from your own server…
• You must never trust the
systems who send you data.
• You can almost never trust
the systems you retrieve
data from
• You must validate data
when you get it AND before
you use it
Input Validation and Representation
41. Using an API in a manner contrary to its intended use
API Abuse
42. API Abuse
My tool tags all the
containers it creates with
“adoptopenjdk=true”
That makes it easier to
retrieve only the relevant
containers when talking to
docker.
I have a test to check I can
create such containers
@Test
voidtestCreatedContainerHasPlimsollLabel() {
Plimsoll p=new Plimsoll();
Stringid =p.docker.createSimpleContainer(TEST_IMAGE_NAME);
ContainerInfo ci=p.docker.inspectContainer(id);
assertNotNull(ci);
assertTrue(ci.config().labels().containsKey(DockerController.LABEL_ID));
assertTrue(p.docker.removeContainer(id));
}
43. API Abuse
But I want to make sure
that the calls to docker are
really working.
So I have a ‘get all’ method.
It’s not used in production
But its exposed as a test
end point…
@Test
voidtestAllContainersListHasEntries() {
Plimsoll p=new Plimsoll();
Stringid =p.docker.createSimpleContainer(TEST_IMAGE_NAME);
assertNotNull(id);
List<Container> allc =p.docker.allContainerList();
assertTrue(allc.isEmpty()==false);
assertTrue(p.docker.removeContainer(id));
}
get("/test/v1/*" , (req, res) ->plimsoll.test_v1_request.handleRequest(req,res));
44. API Abuse
A common vulnerability pattern is to find hidden end points like ones just for
testing.
Reading the code on github or just Googling for your sites metadata can easily
reveal them
And of course test end points tend to have less security
Never have APIs that are just for ‘testing’ or bypass normal
authentication processes..
45. API Abuse
My tool allows for complex scenarios to be created.
JVM = Hotspot or OpenJ9
App Server = Tomcat or Jetty
Various versions strings.
The combinations are sent one by one…
JVM=Hotspot, AppServer=Tomcat, AppVersion=9.0
46. API Abuse
The code validates the entries as being
individually
correct..
JVM=Hotspot
AppServer=Tomcat
AppVersion=9.4.9
But not that the combinations are valid!
47. API Abuse
My code also returns lists of containers so there
are page / page size type parameters..
“list containers from page 7 with page size 100”
“list containers from page 1 with page size 10”
48. API Abuse
My code also returns lists of containers so there
are page / page size type parameters..
“list containers from page 7 with page size 100”
“list containers from page 1 with page size 10”
What about..
“list containers from page 0 with page size -100”
“list containers from page -1 with page size 10”
“list containers from page MAX_INT with page size -MAX_INT”
50. #2 Never give control to the
caller by accepting
unconstrained instructions
• Calling endpoints that are
hidden
• Finding hidden fields in your
requests and changing them
• Sending unexpected but
pseudo-valid combinations of
data
• Exploring the data ranges (+
and - )
• Using an API out of context.
(like credit card validation)
• Exploiting query languages
(SQL injection plus. JQ, or
Xpath etc)
• most systems assume any call
to it is correct and do minimal
validation once the individual
elements are accepted.
API Abuse
55. Errors
Catching or throwing exceptions in a more generic manner than needed can hide
the activities of the attacker
Either because of a lack of specific info in logging and log analysis
i.e: “Config file not found”. Vs “Unexpected IO Error reading the config file”
Or simply because the unusual exception is treated as usual
56. Errors
Catching or throwing exceptions in a more generic manner than needed can hide
the activities of the attacker
Either because of a lack of specific info in logging and log analysis
i.e: “Config file not found”. Vs “Unexpected IO Error reading the config file”
Or simply because the unusual exception is treated as usual
BTW - catching NullPointer Exceptions as a way of detecting ‘expected’ nulls is also
poor practice – as described above. You simply can’t tell which NPE is which.
57. Errors
Being to helpful can also be a problem.
public IHandlerloadHandler(String datatype) throws IOException {
StringclassName=handlers.get(datatype);
Class<IHandler> handlerClass;
try{
handlerClass = (Class<IHandler>) Class.forName(className);
return handlerClass.newInstance();
}catch (Exception e) {
throw new IOException("handler for type "+datatype+" of class "+className+" cannot beloaded");
}
}
58. Errors
What happens at runtime?
public IHandlerloadHandler(String datatype) throws IOException {
StringclassName=handlers.get(datatype);
Class<IHandler> handlerClass;
try{
handlerClass = (Class<IHandler>) Class.forName(className);
return handlerClass.newInstance();
}catch (Exception e) {
throw new IOException("handler for type "+datatype+" of class "+className+" cannot beloaded");
}
}
try {
IHandlerh=loadHandler("foo.data");
…
}
catch (IOException e) {
System.out.println(e.getMessage());
}
59. Errors
What errors might happen?
public IHandlerloadHandler(String datatype) throws IOException {
StringclassName=handlers.get(datatype);
Class<IHandler> handlerClass;
try{
handlerClass = (Class<IHandler>) Class.forName(className);
return handlerClass.newInstance();
}catch (Exception e) {
throw new IOException("handler for type "+datatype+" of class "+className+" cannot beloaded");
}
}
try {
IHandlerh=loadHandler("foo.data");
…
}
catch (IOException e) {
System.out.println(e.getMessage());
}
60. Errors
public IHandlerloadHandler(String datatype) throws IOException {
StringclassName=handlers.get(datatype);
Class<IHandler> handlerClass;
try{
handlerClass = (Class<IHandler>) Class.forName(className);
return handlerClass.newInstance();
}catch (Exception e) {
throw new IOException("handler for type "+datatype+" of class "+className+" cannot beloaded");
}
}
try {
IHandlerh=loadHandler("foo.data");
…
}
catch (IOException e) {
System.out.println(e.getMessage());
}
Handler for type foo.data of class null cannot be loaded
Handler for type foo.data of class com.acme.handler
cannot be loaded
What errors might happen?
61. Errors
public IHandlerloadHandler(String datatype) throws IOException {
StringclassName=handlers.get(datatype);
Class<IHandler> handlerClass;
try{
handlerClass = (Class<IHandler>) Class.forName(className);
return handlerClass.newInstance();
}catch (Exception e) {
throw new IOException("handler for type "+datatype+" of class "+className+" cannot beloaded");
}
}
try {
IHandlerh=loadHandler("java.ext.dirs");
…
}
catch (IOException e) {
System.out.println(e.getMessage());
}
Handler for type java.ext.dirs of class
/Users/spoole/Library/Java/Extensions:
/Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/
Home/jre/lib/ext:
/Library/Java/Extensions:/Network/Library/Java/Extensions:
/System/Library/Java/Extensions:
/usr/lib/java cannot be loaded
What errors might happen?
Quite often ‘handlers’
will be a Properties
object.backed by
System.getProperties so
you can use –D on the
command line
62. Errors
Driving code down error paths is a profitable exercise
for the attacker. Many Remote Code Execution
vulnerabilities occur due to code execution during
object instantiation etc – even if subsequently failing a
class cast or similar
Correct error
management
is the flipside
of correct
validation
63. #3 Error paths are as critical as normal flows
#4 Report only enough information to
identify the situation
• In general we’re poor at
dealing with errors in our
code.
• Developers focus on the
positive side.
• With consequential lack of
testing for error paths and
checking correct behavior
Errors
65. Security Features
“I don’t need any security as the
system is local…”
“I’ll add security later”
“I’m not sure what security means”
66. Security Features
Many vulnerabilities come from poor authentication and access control
methods but ‘Security’ is a wider topic.
Confidentiality : Managing the data in your care
Cryptography : methods to ensure information is kept private
Privilege management: Ensuring actors are who they say they are
67. “I don’t need any security as the system is local…”
Security Features
Browser Server
Docker
Daemon
Remote
repositories
Local
repositories
WS
Cmd line
http
Adopt Server
http
68. “I don’t need any security as the system is local…”
Security Features
Browser Server
Docker
Daemon
Remote
repositories
Local
repositories
WS
Cmd line
http
Adopt Server
http
The browser is vulnerable to Cross Site Scripting attacks. So a remote attacker could get on to your system
69. “I don’t need any security as the system is local…”
Security Features
Browser Server
Docker
Daemon
Remote
repositories
Local
repositories
WS
Cmd line
http
Adopt Server
http
Since my tool uses an API that is unconstrained (ie the command line). The attacker could do anything
And the tool is downloading and running code from the web – another attack point
70. “I don’t need any security as the system is local…”
Security Features
Browser Server
Docker
Daemon
Remote
repositories
Local
repositories
WS
Cmd line
http
Adopt Server
http
The docker instance may have access to a secure repository. Without security my tool is enabling an easy bypass of
any access controls. The contents of the server are now available for a hacker to explore or use.
71. “I don’t need any security as the system is local…”
Security Features
Browser Server
Docker
Daemon
Remote
repositories
Local
repositories
WS
Cmd line
http
Adopt Server
http
So actually my tool needs really good security features.
73. Security Features
Trusting the end user not to tamper with the data
Cookie[] cookies =
request.getCookies();
for (int i =0; i< cookies.length; i++) {
Cookie c = cookies[i];
if (c.getName().equals("role")) {
userRole = c.getValue();
}
}
74. Security Features
Trusting the end user not to tamper with the data
Cookie[] cookies =
request.getCookies();
for (int i =0; i< cookies.length; i++) {
Cookie c = cookies[i];
if (c.getName().equals("role")) {
userRole = c.getValue();
}
}
Does encryption solve this?
75. Security Features
Trusting the end user not to tamper with the data
Cookie[] cookies =
request.getCookies();
for (int i =0; i< cookies.length; i++) {
Cookie c = cookies[i];
if (c.getName().equals("role")) {
userRole = c.getValue();
}
}
No!
XSS attacks often replace
encrypted cookies etc with
ones from another session
that has the required
privileges.
You must have additional
privilege management
processes in play
76. Security Features
My code has a endpoint that allows the
client to read the config
get: ”/api/v1/config/<key_name>”.
Returns json :
{ “key”: key_name , “value” : value }
public StringgetConfigValue(String key) {
return config.getProperty(key);
}
private Properties loadConfig() {
Properties p=new Properties( System.getProperties() );
..
Poor confidentiality management
77. Security Features
My code has a endpoint that allows the
client to read the config
get: ”/api/v1/config/<key_name>”.
Returns json :
{ “key”: key_name , “value” : value }
public StringgetConfigValue(String key) {
return config.getProperty(key);
}
private Properties loadConfig() {
Properties p=new Properties( System.getProperties() );
..
Poor confidentiality management
Since the config was backed by
System.getProperties all my system
properties are exposed..
78. private Properties loadConfig() {
Properties p=new Properties( System.getProperties() );
File f =new File("plimsoll.properties");
if(f.exists()) {
FileReader fr;
try {
fr=new FileReader(f);
p.load(fr);
fr.close();
} catch (IOException e) {
}
return p;
}
Security Features
Unsophisticated credentials management
-Ddocker.user=foo -Ddocker.password=bar.
Storing information
In text files. Easily
readable.
Easily changed
on the command line.
(turns up in the logs)
81. TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
X509Certificate[] certs, String authType) {
}
public boolean isClientTrusted( X509Certificate[] cert) {
return true;
}
public boolean isServerTrusted( X509Certificate[] cert) {
return true;
}
}}
Security Features
Deliberate weakening of security
protocols
82. Security Features
Defaulting to full access or running too much under privilege
method() {
AccessController.doPrivileged( new PrivilegedAction())
{
public Object run() {
// my app goes here
}
};
}
83. #5 Access controls as near to business logic as possible
#6 Keep credentials encrypted, safe and out of memory
#7 Fail safely
#8 Know your responsibilities for others
• For many the word ‘security’
is content free. Its all
someone elses problem.
• It’s not – every system needs
to understand its security
posture.
• Where do you check access
control?
• Where and do you store
credentials?
• Will you default to a safe
mode or an ‘all access pass
mode’ ?
• BTW - How much code gets
run before you say no?
Security Features
84. This is straightforward
If your system is too complex
to understand
Or just poorly written. It’s
more likely to have edge cases,
weak spots etc
So more open to being
attacked
Code Quality
#9 Keep it simple
# 10 Have a full test suite
# 11 test for failing conditions:
even security related
Poor code quality leading to unpredictable behaviour and
opportunities to stress the system in unexpected ways
85. Using the command line version
of docker (when I could use the
REST API)
Having a selection process for
items with a specific tag.
(Exposes all the docker data if
hacked)
Insecure API to read config
Encapsulation
Insufficient encapsulation of critical data or functionality
Errors that allow functionality or data
to cross trust boundaries
Escalation of data access privileges
Insecure import of untrusted code
Exposure of information through
unprotected resources
#12 use and immutable state where at all possible
#13 Do not give away extraneous data
#14 Only hold the least amount of critical data – and throw it away
Soon as possible
#15 Reduce functionality to just the usecases needed.
No nice-to-haves !
86. #16 control of state never
leaves your system
• Client can send you requests
to change state but it must
never own the state.
Time and State
Unexpected interactions between threads, processes, time,
and information (shared state)
people put state into a cookie and
assume it cant be modified if
encrypted..
What happens if someone sends you
an encrypted state object from another
session…
Did you have a flag in the state that
said the user had higher privileges.?
87. • All the parts of your development
process
• The tools you use
• Your dependencies
• The operating system
• The build machines.
• The code repository
• All these can be vulnerable to
attack.
Environment
88. Authentication, access control, confidentiality,
cryptography, privilege management.
Unexpected interactions between
threads, processes, time, and information
(shared state)
Errors related to error handling
Problems caused by metacharacters,
alternate encodings and numeric
representations. General security problems
result from trusting input.
Using an API in a manner contrary to its
intended use
Insufficient encapsulation of critical data or
functionality
Everything that is outside of the source
code but is still critical to the security of the
application
Time and State
Poor code quality leading to unpredictable
behaviour and opportunities to stress the
system in unexpected ways.
Errors
API abuse
Code Quality
Security Features
Encapsulation
Input Validation and Representation
Environment
89. Phew.
• So many things ways your
application can be
vulnerable.
• Security is not something
you add after. It’s always a
part of your design thinking.
93. 1. Input Validation and Representation
2. API Abuse
3. Security Features
4. Time and State
5. Error Handling
6. Code Quality
7. Encapsulation
* Environment
The Seven Pernicious Kingdoms
97. Secure by Design - Security Design Principles for the
Rest of Us
https://www.slideshare.net/EoinWoods1/secure-by-design-security-design-principles-
for-the-rest-of-us
Online
Guides
101. Summary
Keep current.
Every vulnerability fix you apply is one less way in.
Learn about secure coding - https://cwe.mitre.org
Compartmentalize. Separate data, code, access controls etc.
Just like bulkhead doors in a ship: ensure one compromise
doesn’t sink your boat.
Design for intrusion. Review you levels of ‘helpfulness’ and
flexibility and learn about Penetration Testing
Learn about security tools & services - IBM App Scan,
lgtm.com
Understand that making your development life easier makes
the hackers job easier
102. There are bad guys out there and your
application is at risk
Don’t make it worse by ignoring the problem
https://www.flickr.com/photos/koolmann/
@spoole167